1、readv()和writev()
主要是因为平常所用到的write与read只是用来对一片连续的内存区域进行操作。read对一篇连续区域的读操作,write对一篇连续区域的写操作。但对于非连续区域的操作用read或者write来,就效率低了很多了。readv和writev就是用来处理非连续区域(或者说,多片连续区域的读写操作的)
因为使用read()将数据读到不连续的内存、使用write()将不连续的内存发送出去,要经过多次的调用read、write。如果要从文件中读一片连续的数据至进程的不同区域,有两种方案:①使用read()一次将它们读至一个较大的缓冲区中,然后将它们分成若干部分复制到不同的区域; ②调用read()若干次分批将它们读至不同区域。同样,如果想将程序中不同区域的数据块连续地写至文件,也必须进行类似的处理。怎么解决多次系统调用+拷贝带来的开销呢?
UNIX提供了另外两个函数—readv()和writev(),它们只需一次系统调用就可以实现在文件和进程的多个缓冲区之间传送数据,免除了多次系统调用或复制数据的开销。
1.1 函数的定义
- iovec()
struct iovec {
void *iov_base; /* Starting address */
size_t iov_len; /* Number of bytes to transfer */
};
- readv()与writev()
#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);
The readv() system call reads iovcnt buffers from the file associated with
the file descriptor fd into the buffers described by iov ("scatter input").
The readv() system call works just like read(2) except that multiple buffers are filled.
The writev() system call works just like write(2) except that multiple buffers are written out.
(1) 参数:readv和writev的第一个参数fd是个文件描述符,第二个参数是指向iovec数据结构的一个指针,其中iov_base为缓冲区首地址,iov_len为缓冲区长度,参数iovcnt指定了iovec的个数。
(2) 返回值:函数调用成功时返回读、写的总字节数,失败时返回-1并设置相应的errno。
1.2 代码示例
- writev()
// 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) + 1;
iov[1].iov_base = str1;
iov[1].iov_len = strlen(str1) + 1;
nwritten = writev(STDOUT_FILENO, iov, 2);
printf("%ld bytes written.\n", nwritten);
exit(EXIT_SUCCESS);
}
- 运行结果
helloworld
13 bytes written.
- readv()
#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);
iov[1].iov_base = buf2;
iov[1].iov_len = sizeof(buf2);
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);
}
- 运行结果
abcdabcdabcdaa
15 bytes read.
buf1: abcdabcdabcdaa
- 文件读写
#include <stdio.h>
#include <sys/uio.h>
#include <fcntl.h>
int main(void){
char buf1[5],buf2[10];
struct iovec iov[2];
iov[0].iov_base = buf1;
iov[0].iov_len = 5;
iov[1].iov_base = buf2;
iov[1].iov_len = 10;
int fd = open("a.txt",O_RDWR);
if(fd < 0){
perror("open");
return -1;
}
int rsize = readv(fd, iov, 2); // 从文件a.txt中读取数据,存到iov[2]中的buf1、buf2
printf("rsize = %d\n",rsize);
close(fd);
fd = open("b.txt", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
if(fd < 0){
perror("open");
return -1;
}
int wsize = writev(fd,iov,2); // 将iov[2]中的buf1、buf2,写入到文件b.txt
printf("wsize = %d\n",wsize);
close(fd);
return 0;
}
- 运行结果
rsize=15
wsize=15