【摘自《linux高性能服务器编程》】
管道容量默认大小是65536字节,可以使用fcntl函数来修改管道容量。
Socket的基础API中有一个socketpair函数,能够方便的创建双向管道。
#include<sys/types.h>
#include<sys/socket.h>
Int socketpair(int domain, int type,int protocol,int fd[2]);
Domain只能使用UNIX本地域协议族AF_UNIX,仅能在本地使用这个双向管道。
Pipe函数用于创建一个管道
#include<unistd.h>
Int pipe(int fd[2]);
dup函数和dup2函数
#include<unistd.h>
Int dup(int file_descriptor);
Int dup2(int file_descriptor_one ,int file_descriptor_two);
dup函数创建一个新的文件描述符,该新文件描述符和原有文件描述符file_descriptor指向相同的文件,管道或者网络连接。并且dup返回文件描述符总是取得系统当前可用最小的整数值。dup2和dup类似,知识返回第一个不小于file_descriptor_two的整数值。
dup与dup2创建文件描述符不继承原文件描述符的属性,比如close_on_exec 和non_blocking等。
readv和writev函数
readv函数将数据从文件描述符读到分散的内存块中,即分散读;writev函数则将多块分散的内存数据一并读入文件描述符中,即集中写。
#include<sys/uio.h>
ssize_t readv(int fd, const struct iovec* vector, int count);
ssize_t writev(int fd, const struct iovec* vector, int count);
fd参数是被操作的目标文件描述符。Vector参数的类型是iovec结构数组。
Sendfile函数
Sendfile函数在两个文件描述符之间直接传递数据,从而避免了内核缓冲区和用户缓冲区之间的数据拷贝,效率很高,这被称为零拷贝。
#include<sys/sendifle.h>
ssize_t sendfile(int out_fd, int in_fd, off_t* offset, size_t count);
in_fd必须是一个支持类似mmap函数的文件描述符,即它必须指向 真实的文件,不能是socket和管道;而out_fd则必须是一个socket。由此sendfile几乎是专门为网络上传输文件而设计的。
mmap函数和munmap函数
mmap用于申请一段内存空间。用于进程间通信的共享或将文件映射到其中。Munmap函数则释放由mmap创建的这段内存空间。
#include<sys/mman.h>
void *mmap(void *start, size_t length,int prot, int flags, int fd, off_t offset);
Int munmap(void *start, size_t length);
Start参数允许用于使用特定的内存起始地址,如果设置为NULL系统自动分配一个地址。Length参数指定内存段的长度。Prot参数用来设置内存段的访问权限。取以下几个值的按位或:
PROT_READ,内存段可读。PROT_WRITE,内存段可写。PROT_EXEC,内存段可执行。PROT_NONE,内存段不能被访问。
Flags参数用于控制内存修改后的程序行为。按位或且与MAP_PRIVATE是互斥的不能同时指定。
MAP_SHARED,在进程间共享这段内存,对该内存段的修改将反映到被映射的文件中,它提供了进程间共享内存的POSIX方法。
MAP_PRIVATE,内存段为调用进程所私有,对该内存段的修改不会反映到被映射的文件中。
MAP_ANONYMOUS,这段内存不是从文件映射而来的,其内容被初始化为全为0,这种情况下,mmaps函数的最后两个参数将被忽略。
MAP_FIXED,内存段必须位于start参数指定的地址处。Start必须是内存页面大小的整数倍。
MAP_HUGETLB,按照“大内存页面”来分配内存空间。“大内存页面”的大小可通过、proc/meminfo文件来查看。
Fd参数是被映射文件对应的文件描述符。它一般通过open系统调用获得。Mmap成功时返回目标内存区域的指针,失败返回MAP_FAILED并设置errno;
SPLICE函数
Splice函数用于在两个文件描述符之间移动数据,也是零拷贝操作。
#include<fcntl.h>
Ssize_t splice(int fd_in, loff_t* off_in ,int fd_out, loff_t* off_out, size_t len, unsigned int flags);
Fd_in 参数是待输入数据文件描述符。如果fd_in是一个管道文件描述符,则off_in必须设置为NULL。
Flags参数则控制数据如何移动,按位或。
SPLICE_F_MOVE,如果适合的话,按整页内存移动数据。这只是给内核的一个提示。不过,因为它的实现存在BUG,所以自内核2.6.21开始,它实际上没有任何效果。
SPLICE_F_NONBLOCK,非阻塞的splice操作,但实际效果还会受文件描述符本身的阻塞状态影响。
SPLICE_F_MORE,给内核一个提示;后续的splice调用将读取更多数据。
SPLICE_F_GIFT,对splice没有效果。
使用splice函数时,fd_in和fd_out必须至少有一个是管道文件描述符。Splice函数调用成功时返回移动字节的数量。
Errno
EBADF,参数所指文件描述符有错。
EINVAL,目标文件系统不支持splice,或者目标文件以追加方式打开,或者两个文件描述符都不是管道那文件描述符,或者某个offset参数被用于不支持随机访问的设备。
ENOMEM,内存不够。
ESPIPE,参数fd_in(或fd_out)是管道文件描述符,而off_in(或off_out)不为NULL。
Tee函数
Tee函数在两个管道文件描述符之间复制数据,也是零拷贝操作。它不消耗数据,因此源文件描述符上的数据仍然可以用于后续的读操作。
#include<fcntl.h>
ssize_t tee(int fd_in, int fd_out, size_t len, unsigned int flags);
该函数的参数的含义与splice相同。
Fcntl函数
Fcntl函数,提供了对文件描述符的各种控制操作。另外一个ioctl是控制文件描述符属性和行为的系统调用。而且ioctl比fcntl能够执行更多的控制。但是对于控制文件描述符常用的属性和行为,fcntl函数时由POSIX规范制定的首选方法。
#include<fcntl.h>
Int fcntl(int fd, int cmd, ...);
Fd参数是被操作的文件描述符,cmd参数指定执行何种类型的操作。根据操作类型的不同,该函数可能还需要第三个可选参数arg。Fcntl函数支持的常用操作及其参数。
复制文件描述符 F_DUPFD,创建一个新的文件描述符,其值大于或等于arg long 新创建的文件描述符。
F_DUPFD_CLOSEEXEC,与上面相似,但是创建同时,设置其close_on_exec标志。 Long 新创建的文件描述符的值。
获取和设置文件描述符的标志。 F_GETFD 获取fd的标志,比如close_on_exec标志 fd标志。
F_SETFD 设置fd的标志 long 0
获取和设置文件描述符的状态标志
F_GETFL 获取fd状态标志,这些标志包括可由open系统调用设置的标志(O_APPEND,O_CREAT等)和访问模式(O_RDONLY,O_WRONLY和O_RDWR) void fd的状态标志。
F_SETFL 设置fd的状态标志,但部分标志是不能被修改的(比如访问模式标志) long 0
管理信号 F_GETOWN 获得SIGIO和SIGURG信号的宿主进程的PID或进程组的组ID 无 信号的宿主进程的PID或进程组的组ID
F_SETOWN 设定SIGIO和SIGURG信号的宿主进程的PID或者进程组的组ID long o
F_GETSIG 获取当应用程序被通知fd可读可写时,是哪个信号通知该事件。 无 信号值 ,0标志SIGIO。
F_SETSIG 设置当fd可读可写时,系统应该触发哪个信号来通知应用程序 ,long 0 。
操作管道容量 F_SETPIPE_SZ设置由fd指定的管道容量,/proc/sys/fs/pipe-size-max内核参数制定了fcntl能设置的管道容量的上限。 Long 0;
F_GETPIPE_SZ 获取由fd指定的管道容量 无 管道容量
在网络编程中,fcntl函数通常用来将一个文件描述符设置为非阻塞的
Int setnonblocking(int fd)
{
Int old_option= fcntl(fd, F_GETFL);
Int new_option = old_option | O_NONBLOCK;//设置非阻塞标志。
Fcntl(fd,F_SETFL,new_option);
Return old_option;
}
此外,SIGIO和SIGURG这两个信号与其他Linux信号不同,他们必须与某个文件描述符相关联方可使用;当被关联的文件描述符可读或可写时,系统将触发SIGIO信号;当被关联的文件描述符(而且必须是一个socket)上有外带数据可读时,系统将触发SIGURG信号。将信号和文件描述符关联的方法,就是使用fcntl函数为目标文件描述符指定宿主进程或进程组,那么被指定的宿主进程或进程组将捕获这两个信号。使用SIGIO时,还需要利用fcntl设置其O_ASYNC标志(异步I/O标志,不过SIGIO信号模型并非真正意义上的异步I/O模型。