2012-02-17 23 views
7

writev函數採用結構iovec的數組作爲輸入參數列表I/O writev如何在內部工作?

writev(int fd, const struct iovec *iov, int iovcnt);

的輸入是需要被寫入到一個文件(比如說)存儲器緩衝器列表。我想知道的是:

是否writev可以獲得內部做到這一點:

for (each element in iov) write(element)

使得iov每一個元素都寫在一個單獨的I到文件/ O調用?還是writev寫入所有文件在單個 I/O調用?

回答

6

%的標準,爲循環中,您提到的是不是writev的有效實施,有以下幾個原因:

  1. 循環可能無法繼續下一個之前寫完一個IOV,在的情況下,一個簡短的寫法 - 但這可以通過使循環更加精細來解決。
  2. 對於管道的原子性,循環可能具有不正確的行爲:如果總寫入長度小於PIPE_BUF,則管道寫入必須是原子的,但該循環會打破原子性要求。除非在寫入之前將所有iov條目移入單個緩衝區,否則此問題無法解決,因爲總長度最多爲PIPE_BUF
  3. 循環可能會有可能導致阻塞的情況,在這種情況下將需要單個調用來執行部分寫入而不會阻塞。據我所知,這個問題在一般情況下是不可能解決的。
  4. 可能還有其他原因,我沒有想到。

我不確定關於第3點,但它在閱讀時肯定存在相反的方向。在循環中調用read可能會阻塞終端是否有一些數據(短於總的iov長度),然後是EOF指示符;在這種情況下呼叫readv應該立即返回部分閱讀。但是,由於Linux中的一個錯誤,終端上的readv實際上是作爲內核空間中的read循環實現的,並且確實存在這個阻塞錯誤。我必須解決這個bug在實施MUSL的標準輸入輸出:

http://git.etalabs.net/cgi-bin/gitweb.cgi?p=musl;a=commit;h=2cff36a84f268c09f4c9dc5a1340652c8e298dc0

要回答你的問題的最後一部分:

或根本writev寫的一切在一個單一的I/O調用文件?

在所有情況下,一致性writev實現將是一個系統調用。深入瞭解它在Linux上的實現方式:對於普通文件和大多數設備,底層文件驅動程序具有直接實現iov-style io的方法,沒有任何內部循環。但是,Linux上的終端驅動程序已經過時了,並且缺少現代的io方法,導致內核在終端上運行時回退到寫入/讀取循環,以便執行writev/readv

+0

我不明白最後一行「在終端上運行時」。另外,在linux src中,你是否在檢查writev實現? – jitihsk 2012-02-18 21:48:22

+0

「在終端上操作時」是指文件描述符指的是終端設備。至於在源代碼中,http://lxr.linux.no/#linux+v3.2.6/fs/read_write.c#L809 – 2012-02-19 00:39:52

3
Or does writev write everything to file in a single I/O call? 

雖然sys_writev盡力在一次調用中寫入所有內容,但我並不是所有的東西。這取決於vfs的實現,如果vfs沒有提供writev的實現,那麼kenerl會在循環中調用vfs的write()。最好檢查writev/readv的返回值,以查看write()中執行的有多少個字節。

您可以在內核fs/read_write.c中找到writev的代碼:do_readv_writev。

5

瞭解代碼如何工作的直接方式是讀取源代碼。

看到http://www.oschina.net/code/explore/glibc-2.9/sysdeps/posix/writev.c

它簡單地ALLOCA()或malloc()函數的緩衝,所有向量複製到它,而一旦調用write()。

它是如何工作的。沒有什麼神祕的。

+0

這是glibc writev,而不是linux內核writev。 – Eloff 2012-10-15 22:24:56

+0

只是從這個鏈接學習mempcpy是一個史詩般的勝利。 – RishiD 2012-11-29 14:34:07