1. 程式人生 > >高階I/O之readv和writev函式

高階I/O之readv和writev函式

為什麼引出readv()和writev()

  1. 因為使用read()將資料讀到不連續的記憶體、使用write()將不連續的記憶體傳送出去,要經過多次的呼叫read、write
    如果要從檔案中讀一片連續的資料至程序的不同區域,有兩種方案:①使用read()一次將它們讀至一個較大的緩衝區中,然後將它們分成若干部分複製到不同的區域; ②呼叫read()若干次分批將它們讀至不同區域。
    同樣,如果想將程式中不同區域的資料塊連續地寫至檔案,也必須進行類似的處理。
  2. 怎麼解決多次系統呼叫+拷貝帶來的開銷呢?
    UNIX提供了另外兩個函式—readv()和writev(),它們只需一次系統呼叫就可以實現在檔案和程序的多個緩衝區之間傳送資料,免除了多次系統呼叫或複製資料的開銷。

readv/writev

在一次函式呼叫中:
① writev以順序iov[0]、iov[1]至iov[iovcnt-1]從各緩衝區中聚集輸出資料到fd
② readv則將從fd讀入的資料按同樣的順序散佈到各緩衝區中,readv總是先填滿一個緩衝區,然後再填下一個

#include <sys/uio.h>
ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
struct iovec {
void *iov_base; /* Starting address */ size_t iov_len; /* Number of bytes to transfer */ };

(1) 引數:readv和writev的第一個引數fd是個檔案描述符,第二個引數是指向iovec資料結構的一個指標,其中iov_base為緩衝區首地址,iov_len為緩衝區長度,引數iovcnt指定了iovec的個數。
(2) 返回值:函式呼叫成功時返回讀、寫的總位元組數,失敗時返回-1並設定相應的errno。

示例程式碼

  1. writev:指定了兩個緩衝區,str0和str1,內容輸出到標準輸出,並列印實際輸出的位元組數
// writevex.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/uio.h>

int main()
{
    char *str0 = "hello ";
    char *str1 = "world\n";
    struct iovec iov[2];
    ssize_t nwritten;

    iov[0].iov_base = str0;
    iov[0].iov_len = strlen(str0);
    iov[1].iov_base = str1;
    iov[1].iov_len = strlen(str1);

    nwritten = writev(STDOUT_FILENO, iov, 2);
    printf("%ld bytes written.\n", nwritten);

    exit(EXIT_SUCCESS);
}

執行結果

$ gcc writevex.c 
$ ./a.out 
hello world
12 bytes written.
  1. readv:從標準輸入讀資料,緩衝區為長度是(8 - 1)的buf1和buf2,並列印讀到的位元組總數和兩個緩衝區各自的內容
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/uio.h>

int main()
{
    char buf1[8] = { 0 };
    char buf2[8] = { 0 };
    struct iovec iov[2];
    ssize_t nread;

    iov[0].iov_base = buf1;
    iov[0].iov_len = sizeof(buf1) - 1;
    iov[1].iov_base = buf2;
    iov[1].iov_len = sizeof(buf2) - 1;

    nread = readv(STDIN_FILENO, iov, 2);
    printf("%ld bytes read.\n", nread);
    printf("buf1: %s\n", buf1);
    printf("buf2: %s\n", buf2);

    exit(EXIT_SUCCESS);
}

執行結果:

$ gcc readvex.c
$ ./a.out 
helloreadv
11 bytes read.
buf1: hellore
buf2: adv