6 高级IO函数:
用于创建文件描述符:pipe、dum/dup2
用于读写数据:readv/writev、sendfile、mmap/munmap、splice、tee
用于控制IO行为和属性:fcntl
管道:
#include
int pipe(int fd[2]);
默认情况下这对文件描述符都是阻塞的。如果用read调用读取一个空管道,read将会阻塞直到有数据可读为止;write类似。
如果将fd[0]\fd[1]都设置为非阻塞,则read和write会有不同行为。如果fd[1]引用计数减少到0,即没有任何进程需要往管道写入数据,则对fd[0]的read操作返回0,即读到了EOF;反之,如果fd[0]引用计数减少至0,则对fd[1]的write操作将失败,并引发SIGPIPE信号。
管道内部传输的数据是字节流,这和TCP字节流的概念相同。从Linux 2.6.11开始,管道容量的大小是默认65536字节。可以使用fcntl修改。
socketpair函数可以方便的创建双向管道:
#include
#include
int socketpair(int domain, int type, int protocol, int fd[2]);
domain域只能是unix本地域协议AF_UNIX,因为我们仅能在本地使用这个双向管道。
另外两个参数和socket系统调用的参数相同。
dup和dup2函数:
#include
int dup(int file_descriptor);
int dup2(int file_descriptor_one, int file_descriptor_two);
通过dup和dup2创建的文件描述符并不继承原来的文件描述符的属性,比如close-on-exec和non-blocking等。
readv和writev:
readv函数将数据从文件描述符读到分散的内存块中,即分散读;writev函数则将多块分散的内存数据一并写入文件描述符中,即集中写。
#include
ssize_t readv(int fd, const struct iovec* vector, int count);
ssize_t writev(int fd, const struct iovec* vector, int count);
struct iovec{
void *iov_base;//starting address
size_t iov_len;//number of bytes to transfer
};
sendfile函数:
sendfile在两个文件描述符之间直接传递数据(完全在内核中操作),从而避免了内核缓冲区和用户缓冲区之间的数据拷贝,效率很高,这被称为零拷贝。
#include
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释放这段内存空间。
#include
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时,系统自动分配一个地址。
prot参数用来设置访问权限。按位或:
PROT_READ
PROT_WRITE
PROT_EXEC//内存段可执行
PROT_NONE//内存段不能被访问
flags参数:
MAP_SHARED//在进程间共享这段内存,对该内存段的修改将反映到被映射的文件中。
MAP_PRIVATE//内存段为调用进程所私有。对该内存段的修改不会反映到被映射的文件中。
...
splice函数:
用于在两个文件描述符之间移动数据,也是零拷贝操作。
#include
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是管道,则off_in必须是NULL,fd_out类似。
flags参数:
SPLICE_F_MOVE//按整页内存移动数据,只是给内核一个提示,没有实际效果。
SPLICE_F_NONBLOCK//实际效果还会受到文件描述符本身的阻塞状态影响。
SPLICE_F_MORE//给内核一个提示:后续的splice调用将读取更多数据。
SPLICE_F_GIFT//没有效果
使用splice函数时,fd_in和fd_out必须至少有一个是管道文件描述符。
tee函数:
在两个管道文件描述符之间复制数据,也是零拷贝。它不消耗数据,因此源文件描述符上的数据仍然可以用于后续的读操作。
#include
ssize_t tee(int fd_in, int fd_out, size_t len, unsigned int flags);
参数与splice相同。
fcntl函数:
#include
int fcntl(int fd, int cmd, ...);
常用操作:
复制文件描述符:F_DUPFD/F_DUPFD_CLOEXEC
获取和设置文件描述符的标志:F_GETFD/F_SETFD(获取fd标志如close-on-exec)
获取和设置文件描述符的状态标志:F_GETFL/F_SETFL(设置的标志O_APPEND,O_CREAT等和访问模式O_RDONLY,O_WRONLY,O_RDWR)
管理信号:F_GETOWN/F_SETOWN(SIGIO SIGURG信号的宿主进程pid或进程组id)/F_GETSIG/F_SETSIG(fd可读或可写时系统应该触发那个信号通知应用程序)
操作管道容量:F_SETPIPE_SZ/F_GETPIPE_SZ
此外,SIGIO SIGURG这两个信号与其他Linux信号不同,它们必须与某个文件描述符相关联方可使用:当被关联的文件描述符可读或可写时,系统将触发SIGIO信号;当被关联的文件描述符(而且必须是一个socket)上有带外数据可读时,系统将触发SIGURG信号。将信号和文件描述符关联的方法,就是使用fcntl函数为目标文件描述符指定宿主进程或进程组,那么被指定的宿主进程或进程组将捕获这两个信号。使用SIGIO时,还需要利用fcntl设置其O_ASYNC标志(不过SIGIO信号模型并非真正意义上的异步IO模型,详见第8章)。
阅读(2611) | 评论(0) | 转发(0) |