以前,只是的确听说过有“高级IO“,也确实见过、用过,但一直以来真的没注意到”为什么那些接口是高级的?“ 昨儿算是弄明白了,就是大家常说的“零拷贝”,
不需要数据在应用空间与内核空间之间来回复制!
通过图来对比一下看:
普通IO
高级IO
几种高级IO
第一个:
void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
其中的参数就不详细介绍,MAN一下就一清二楚了。
这个接口的较常见的用法是,做基于文件的共享内存用,映射一个文件,将prot参数设置成MAP_SHARED就行了,不同的进程共享相同的内存索引节点块;如果将prot设置成MAP_PRIVATE就不叫共享内存了,在进程空间里做了份拷贝所以并不共享,一般可以用来高效地读取文件(注意只是读取,并不共享,这也是与MAP_SHARED的区别所在)。这两种用法在UNIX 环境高级编程圣经里面有提到,这儿就不多说了
第二个:
ssize_t sendfile(int out_fd,int in_fd,off_t* offset,size_t count);
这个在圣经里面就没有了,好像是。这个接口实现在两个文件描述符之间传递数据,注意完全是在内核中操作的,也就是说避免了在应用空间与内核空间来回拷贝数据,也就是据说的“零拷贝”。
到这儿其中的几个参数一看就明白了,out_fd 是将写入的fd ,in_fd 是读出的fd,off_set 是读的偏移位置,count是读的字节数。
第三个:
ssize_t splice(int fd_in,loff_t* off_in,int fd_out,loff_t* off_out,size_t len,unsigned int flags);
这个接口也是实现在两个文件描述符之间传递数据,而且至少其中一个是管道描述符。都知道,管道跟TCP是一样是基于的字节流的,是不能定位偏移的。所以当fd_in 为管道时,off_in必须设置成NULL;当fd_out为管道时,off_out必须设置成NULL。len表示移动的数据长度,至于flags涉及到一些更高级的用法,可以MAN一下看。
这个接口在网络编程中可以用得很爽的,看看一个范例啊
int pipe[2];
pipe(pipefd);
splice(0,NULL,pipefd[1],NULL,2048,SPLICE_F_MODE|SPLICE_F_MOVE);
splice(pipefd[0],NULL,sockfd,2048,SPLICE_F_MODE|SPLICE_F_MOVE);
这个小范例实现的是从标准输入读入数据,并写往SOCKET。
第四个:
ssize_t tee(int fd_in,int fd_out,size_t len,unsigned int flags);
这个接口实现的是在两个管道文件描述符之间复制数据。这是上个接口的一个延伸,不多说了
第五个:
ssize_t readv(int fd,const struct iovec* vector,int count);
sszie_t writev(int fd,const struct iovec* vector,int count);
这两个圣经上面也有介绍
第六个:
int dup(int file_descriptor);
int dup2(int file_descriptor_one,int file_descriptor_two);
这个是在单进程空间内复制文件描述符,即在单进程空间内的文件描述符表中可以有多个项指向文件表中的同一项,也就是说两个文件描述符共享同一偏移,这个之前在《与文件相关的几个表》中有过分析
第七个:
int pipe(int fd[2]);
int socketpair(int domain,int type,int protocol,int fd[2]);
前一个是单向管道,后一个是双向管道
的确,有了这些高级IO,也就是少了read wirte了,效率可以提高不少,不过也得注意,当前系统是否支持这些高级IO
以上介绍出自游双的《LINUX 高性能服务器编程》,这本书看得很爽,很通俗易懂!在这儿感谢前辈们给咱们的指引,让我们重新认识了“高级IO”。快看完了,回过头来整理一个头绪。^_^
阅读(882) | 评论(0) | 转发(0) |