《Linux高性能服务器编程》学习笔记——第六章 高级I/O函数

来源:互联网 发布:怎么查询端口号 server 编辑:程序博客网 时间:2024/05/18 00:53

Linux高级I/O函数在特定条件下有相当优秀的性能。


 

1、pipe函数

pipe函数用于创建一个管道,实现进程间的通信:

#include <unistd.h>

int pipe(int fd[2]);

函数成功返回0,并将打开的一对文件描述符填入参数数组。失败返回-1并设置errno。

通过pipe函数创建的文件描述符fd[0]和fd[1]分别构成管道的两端,往fd[1]写入的数据可以从fd[0]读出,不能反过来,若要实现双向数据传输就需要两个管道。

默认情况下两个文件描述符都是阻塞的,read系统调用读取空的管道,read将被阻塞直至有数据可读,write系统调用向一个满的管道写入数据,write被阻塞直至有足够空闲空间。如果写端文件描述符fd[1]的引用计数减为0,即没有进程向管道写入数据,针对读端文件描述符fd[0]的read操作返回0,即读取到了文件结束标记EOF。如果读端文件描述符fd[0]的引用计数减为0,即没有进程要读取管道数据,针对写端文件描述符fd[1]的write操作将失败,并引发SIGPIPE信号。

管道内部传输的数据是字节流,和TCP字节流概念相同,但有区别。应用程序能向一个TCP连接中写入多少字节取决于对方的接收通告窗口大小和本端的拥塞窗口大小。管道本身拥有一个容量限制,它规定如果应用程序不将数据从管道读走的话,该管道最多能被写入多少字节的数据。管道容量阿东小默认是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,因为我们仅能在本地使用这个双向管道。fd数组中的文件描述符即可读又可写。函数成功返回0,失败返回-1并设置errno。


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系统调用失败时返回-1并设置errno。

通过dup和dup2创建的文件描述符并不继承原文件描述符的属性。

 

3、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结构数组,该结构体描述一块内存区,count参数是数组的长度。readv/writev成功返回读出或写入fd的字节数,失败返回-1并设置errno。相当于简化版的readmsg/sendmsg函数。


4、sendfile函数

sendfile函数在两个文件描述符之间直接传递数据(完全在内核中操作),从而避免了内核缓冲区和用户缓冲区之间的数据拷贝,效率高,这被称为零拷贝。

#include <sys/sendfile.h>

ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

in_fd参数是待读出内容的文件描述符,out_fd参数是待写入内容的文件描述符,offset参数指定从读入文件流的哪个位置开始读,空则表示从默认起始位置。count指定文件描述符in_fd和out_fd之间传输的字节数。sendfile函数成功返回传输的字节数,失败返回-1并设置errno。in_fd必须支持类似mmap的函数,即它必须指向真实的文件,不能是socket或者管道;而out_fd必须是一个socket。sendfile几乎是专门针对在网络上传输文件而设计的。

 

5、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:内存段不能被访问。

flag参数控制内存段被修改后程序的行为,常用的有:


fd参数是被映射文件对应的文件描述符,一般通过open获得。

offset设置从文件的何处开始映射(针对不需要读入整个文件的情况)。

mmap函数成功时返回指向目标内存区域的指针,失败则返回MAP_FAILED,并设置errno。

munmap成功返回0,失败返回-1并设置errno。 


6、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。如果fd_in不是一个管道文件描述符,比如socket,那么off_in表示从输入数据流的何处开始读取数据。若off_in为NULL,则从当前位置读入,若不为NULL,则它将指出具体的偏移位置。fd_out/off_out与fd_in/off_in相同,不过用于输出数据流。len指定移动数据的长度。flag参数控制数据流如何移动:


使用splice函数时,fd_in和fd_out必须至少有一个是管道文件描述符,成功后返回移动字节的数量。它可能返回0,表示没有数据需要移动,这发生在从管道读取数据(fd_in是管道文件描述符)而该管道没有被写入任何数据时。失败返回-1并设置errno,常见errno有:


7、tee函数

tee函数在两个管道文件描述符之间复制数据,也是零拷贝操作。它不消耗数据,因此源文件描述符上的数据仍然可以用于后续的读操作。tee函数的原型如下: 

#include <fcntl.h>

ssize_t tee(int fd_in, int fd_out, size_t len, unsigned int flags);

fd_in和fd_out必须都是管道文件描述符。成功返回复制的数据字节数。返回0表示没有复制任何数据。失败返回-1并设置errno。 


8、fcntl函数
 

fcntl函数提供了对文件描述符的各种控制操作。另外一个常见的控制文件描述符属性和行为的系统调用时ioctl,而且ioctl比fcntl能够执行更多的控制。但对于控制文件描述符常用属性和行为,fcntl是POSIX规范指定的首选方法:

#include<fcntl.h>

int fcntl(int fd, int cmd, ...);

cmd指定操作类型。常用操作及其参数如下:



失败返回-1并设置errno。

网络编程中,经常使用fcntl将一个文件描述符设置为非阻塞的。

此外,SIGIO和SIGURG这两个信号与其他Linux信号不同,它们必须与某个文件描述符相关联方可使用:当被关联的文件描述符可读或可写时,系统触发SIGIO信号;当被关联的文件描述符(必须是一个socket)有带外数据可读时,系统将触发SIGURG信号。将信号和文件描述符关联的方法,就是使用fcntl函数为文件描述符指定宿主进程或进程组,被指定的宿主进程或进程组将捕获这两个信号。使用SIGIO时还需要利用fcntl设置其O_ASYNC(异步I/O标志,不过SIGIO信号模型并非真正意义上的异步I/O模型)。


0 0
原创粉丝点击