前言
在使用socket进程间通信时,如何完成这样一个功能呢:一个进程向另一个进程发送一个cmd,同时发送把这个cmd所携带的参数也发送出去?
在linux上进行开发时,我们经常会使用read和write进行socket通信,但是上面这个功能如果使用read和write来完成,则需要多次调用两个函数来最终达到这样的需求,这样肯定会造成性能的损失,那么还有可以直接实现这个需求的函数吗,是的,writev和readv就可以实现这样一个功能。
ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
- fd表示打开的文件描述符,iov表示指向iovec结构体数组的结构体指针,iovcnt表示
结构体数组中元素的个数 - 成功时,返回值为从缓存区中读出或写入缓存区的字节数
失败时返回-1并设置相应的errno
-
这两个函数类似于read和write,不过readv和writev允许单个系统调用读入到或写出自一个或多个缓冲区。这些操作分别称为分散读(scatter read)和集中写(gather write),因为来自读操作的输入数据被分散到多个应用缓冲区中,而来自应用缓冲区的输出数据则被集中提供给单个写操作。
-
iovec结构数组中元素的数目存在某个限制(IOV_MAX),具体取决于实现。POSIX要求在头文件<sys/uio.h>中定义IOV_MAX常值,而且其值至少为16。
-
readv和writev这两个函数可以用于任何描述字,而不仅限于套接口。另外,writev是一个原子操作,意味着对于一个基于记录的协议(例如UDP)而言,一次writev调用只产生单个UDP数据报。
writev
创建1个文件f3
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/uio.h>
int main(int argc,char *argv[])
{
int fd3;
ssize_t size;
char buf1[9]="12345678",buf2[7]="123456";
struct iovec iov[2];
fd3=open(argv[1],O_RDWR);
iov[0].iov_base=buf1;
iov[0].iov_len=sizeof(buf1);
iov[1].iov_base=buf2;
iov[1].iov_len=sizeof(buf2);
size=writev(fd3,iov,2);
printf("%s size is :%d\n",argv[1],size);
close(fd1);
close(fd2);
close(fd3);
}
编译执行
VM-4-10-ubuntu:~/WorkSpace/TestC$ ./testwritev f3
f3 size is :16
可见f3内存放了16个字节的数据
readv
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/uio.h>
int main(int argc,char *argv[])
{
int fd3;
fd3=open(argv[1],O_RDWR);
char buf3[9]={0}, buf4[7]={0};
struct iovec iov2[2];
iov2[0].iov_base= buf3;
iov2[0].iov_len=sizeof(buf3);
iov2[1].iov_base = buf4;
iov2[1].iov_len = sizeof(buf4);
ssize_t ret = readv(fd3,iov2,2);
printf("ret=%d,read data from fd3, buf3=%s, buf4=%s\n",ret, buf3, buf4);
close(fd3);
}
编译执行
:~/WorkSpace/TestC$ ./testreadv f3
ret=18,read data from fd3, buf3=12345678, buf4=123456
可见,readv将读出的数据按照指定的长度分配到了两个不同的内存buf3和buf4中。
需要注意的是,如果buf1中的数据末尾没有"\0",那么在存储到fd3中后,两段数据之间是没有分割的,那么在buf1和buf2就被存储在了一块连续的内存中,在使用readv读取时,buf虽然指定了长度,但是由于存储在fd3中的数据是连续的,那么会将buf2所表示的数据一并读取到buf3中,这是应该避免的。
writev的特点:
writev允许处理非连续的数据块。也就是说,缓冲区可以逐个单独分配,不用是一块连续的较大的地址空间。
writev 的I/O是“原子的”。例如,如果你执行一个writev操作,所有数据将在一个连续操作中被写入,不会被中断。