LINUX提供了很多高级的I/O函数,我们介绍和网络编程相关的几个,在特定情况下可以产生很高的性能。主要分为3类:
1、创建文件描述符的函数,pipe,dup,dup2函数;
2、读写数据的函数,readv,writev,sendfile,mmap,munmap,splice,tee函数;
3、用于控制I/O行为和属性的函数,fcntl()函数。
1、pipe函数
pipe用于创建一个管道实现进程间通信。这里只介绍基本使用。
参数是包含两个int型整数的数组指针。(数组指针只是一个指针变量,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。)成功时返回0,并将一对打开的文件描述符值放入参数指向的数组。
(1)fd[0],fd[1]构成管道的两端,并且,fd[0]只能用于从管道读取数据,fd[1]用于往管道写数据。如果要实现双向的数据传输,就要创建两个管道。
(2)默认情况下,这一对文件描述符都是阻塞的,如果我们用read读取一个空的管道,将会被阻塞,直到管道有数据可读。如果应用程序把fd[0],fd[1]设置为非阻塞的,则read,write会有不同的行为,后面再说。
(3)如果fd[1]写端的引用计数件为0,则读端fd[0]read将会返回0,表示读到了文件结束符EOF,同理,在读端引用计数变为0,写端调用write会失败,并引发SIGPIPE信号。这点后面具体介绍。
(4)管道内传输的是字节流,和TCP字节流是一样的概念,只不过管道最多能写入的数据大小是有限制的,也可以用fcntl函数来修改管道容量。
socket基础API也有一个sockerpair函数可以创建一个可读可写的双向管道,不过只支持本地使用UNIX.
2.dup,dup2函数
有时候我们想标准输入重定向到一个文件,或者把标准输出重定向到一个网络连接(比如CGI编程,我们在线填写表格那种)。可以用复制文件描述符的函数dup,dup2来实现:
dup创建一个新的文件描述符,并且他和原来的文件描述符指的是同一个文件,管道或者网络连接。并且返回的文件描述符是当前系统可用的最小的整数,dup2返回的文件描述符不小于第二个参数的值。(注意新的文件描述符不继承原来文件描述符的属性)
3.readv,writev函数
readv函数将数据从文件描述符读到分散的内存块中,叫分散读,writev相反,称为集中写。
其中iovec是秒数内存区域的结构体,count是内存块的数量,即vector数组的长度。
4.sendfile函数
这个函数直接在内核中完成两个文件描述符的数据传输,避免了用户数据缓冲区和内核数据缓冲区的数据拷贝,称为零拷贝,效率很高。不用设置缓存也不用读取操作。
out_fd是待写入内容的文件描述符,in_fd是待读出内容的文件描述符。注意,这里的in_fd必须指向真实的文件,不能是socket和管道,out_fd必须是一个socket。所以,sendfile几乎专门为了在网络上传输文件设计的。
5、mmap和munmap函数
mmap函数用于申请一段内存空间。可用于进程间通信的共享内存或者直接将文件映射其中,munmap用于释放申请的内存空间。
start参数允许用户使用某个特定地址作为内存起始地址。如果设为NULL,系统分配一个地址。prot用来指定内存段的访问权限。后面我们会进一步探讨如何实现内存共享。
6、splice函数
用于在两个文件描述符间移动数据,也是零拷贝操作。
注意,必须至少有一个是管道文件描述符,成功时返回移动数据的字节数量。
7、tee函数
tee函数用于在两个管道文件描述符之间复制数据,也是零拷贝操作。注意这里是复制,不是移动。
成功时返回复制字节数。
8、fcntl函数(file control)
提供了对文件描述符的各种控制操作。
cmd指定执行何种类型的操作,还有可能有第三个参数。
在网络编程中,此函数经常用来将一个文件描述符设置为非阻塞的。
还有关于信号控制的内容,后面再说。