分散/聚集I/O 分散/聚集I/O 是一种可以在单词系统调用中对多个缓冲区输入输出的方法,可以把的一个缓冲区的数据写到单个数据流,也可以把单个数据流读到多个缓冲区中。其命名的原因在于数据会被分散到指定缓冲区向量,或者从指定缓冲区向量中聚集数据。这种输入输出的方法也成为向量I/O。前面所提到的标准读写系统调用可以成为线性I/O
与线性I/O相比,分散聚集I/O有如下优势:
编码模式更自然
如果数据本身是分段的,向量IO提供了直观的数据处理方式
效率更高
单个向量I/O操作可以取代多个线性I/O操作
性能更好
除了减少了发起的系统调用次数,通过内部优化,向量I/O可以比线性I/O提供更好的性能
支持原子性
和多个线性I/O操作不同,一个进程可以执行单个向量I/O操作,比秒了和其它进程交叉操作的风险
readv()和writev() readv()函数从文件描述符fd中读取count个段(一个段即一个iovec结构体)到参数参数iov所指定的缓冲区中:
1 2 3 #include <sys/uio.h> ssize_t readv (int fd, const struct iovec *iov, int count) ;
writev()函数冲参数iov指定的缓冲区中读取count个段的数据,并写入fd中:
1 2 #include <sys/uio.h> ssize_t writev (int fd, const struct iovec *iov, int count) ;
除了同时操作多个缓冲区外,readv()函数和writev()函数的功能分别和read()和write()的功能一致
每个iovec结构体描述一个独立的/物理不连续的缓冲区,我们称其为段:
1 2 3 4 5 6 #include <sys/uio.h> struct iovec { void *iov_base; size_t iov_len; }
一组段的集合称为向量。每个段描述了内存中所要读写的缓冲区的地址和长度。
readv()函数在处理下个缓冲区之前,会填满当前缓冲区的iov_len个字节。
writev()函数在处理下个缓冲区之前,会把当前缓冲区所有iov_len个字节的数据输出
这两个函数都会顺序处理向量中的段,从iov[0]开始,一直到iov[count-1]
返回值 操作成功是,readv()函数和writev()函数分别返回读写的字节数,该字节数应该等于所有count个iov_len的和
出错时,返回-1并设置errno,设置的errno和read()、write()相同。此外还有另外两种错误场景:
如果所有count个iov_len的和超出ssize_max,则不会处理任何数据,返回-1,并把errno设置为EINVAL
count必须大于0,且小于等于IOV_MAX(1024),如果count为0,则返回0(errno可能设置为EINVAL)。如果超过IOV_MAX,不会处理数据,返回-1,并把errno设置为EINVAL
writev()示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/uio.h> int main () { struct iovec iov [3]; ssize_t nr; int fd, i; char *buf[] = { "The term buccaneer comes from the word boucan.\n" , "A boucan is a wooden frame used for cokking meat.\n" , "Buccaneer is the West Indies name for a pirate.\n" }; fd = open ("buccaneer.txt" , O_WRONLY | O_CREAT | O_TRUNC); if (fd == -1 ) { perror ("open" ); return 1 ; } for (i = 0 ; i < 3 ; i++) { iov[i].iov_base = buf[i]; iov[i].iov_len = strlen (buf[i]) +1 ; } nr = writev (fd, iov, 3 ); if (nr == -1 ) { perror ("writev" ); return 1 ; } printf ("wrote %d bytes\n" , nr); if (close (fd)) { perror ("close" ); return 1 ; } }
readv()示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/uio.h> int main () { char foo[48 ], bar[51 ], baz[49 ]; struct iovec iov [3]; ssize_t nr; int fd, i; fd = open("buccaneer.txt" , O_RDONLY); if (fd == -1 ) { perror("open" ); return 1 ; } iov[0 ].iov_base = foo; iov[0 ].iov_len = sizeof (foo); iov[1 ].iov_base = bar; iov[1 ].iov_len = sizeof (bar); iov[2 ].iov_base = baz; iov[2 ].iov_len = sizeof (baz); nr = readv (fd,iov,3 ); if (nr == -1 ) { perror ("readv" ); return 1 ; } for (i = 0 ; i < 3 ; i++) { printf ("%d:%s" , i, (char *)iov[i].iov_base); } if (close (fd)) { perror ("close" ); return 1 ; } }