高级I/O函数

在Linux系统下,提供了很多高级I/O函数,并不像我们之前用的文件函数open/read等。他们大概分为三类:
创建文件描述符:pipe、dup/dup2。
读写数据:readv/writev、sendfile、mmap/munmap、splice和tee。
控制I/O行为属性:fcntl。

1、创建文件描述符:

(1)pipe函数:
用于创建一个管道为了实现进程间通讯。

#include<unistd.h>
int pipe(inf fd[2]);

pipe函数的参数是一个包含两个int型整数的数组指针,调用成功返回0,并把一对打开的文件描述符值填入其参数指向的数组,失败返回的-1。fd参数创建两个文件描述符,fd[0]和fd[1]分别构成管道的两端,fd[0]端固定只能读数据,fd[1]固定只能写数据,不能反过来。使用read和write函数来实现对fd数组的操作。
(2)dup/dup2函数:

#include<unistd.h>
int dup(int file_descriptor);
int dup2(int file_descriptor_one, int file_descriptor_tow);

dup函数创建个新的文件描述符,I想你的文件描述符和原来的文件描述符指向同一个文件、管道或者网络连接。并且dup返回的文件描述符总是取系统当前可用的最小整数值。dup2和dup类似,只不过它的返回第一个不小于file_descriptor_tow的整数值。调用失败二者返回值为-1并置errno。
这里写图片描述
输出结果:
这里写图片描述

2、读写数据:

(1)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参数是vector数组的长度,就是有多块内存数据需要从fd读出或者写到fd,调用成功两个函数返回读出或者写入字节数。

(2)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_file必须是一个socket。可见sendfile就是专门为网络传输文件设计的,下面我们来看看sendfile函数在服务器里的使用:
这里写图片描述
这里写图片描述
接收客户端:
这里写图片描述
执行结果:
server端:
这里写图片描述
client端:
这里写图片描述
当sendfile函数和recve函数调用成功分别返回发送和接收的字节数,失败返回-1。

(3)mmap/munmap:
mmap函数用于申请一段内存空间,可以将这段内存作为进程间通信的共享内存,也可以将文件直接映射到其中。munmap函数则释放由mmap创建的这段空间。定义如下:

#include<sys/mman.h>
void *mmap( void *start, size_t length, int port, int flags, 
int fd,off_t offset );
int munmap( void *start, size_t length);

start参数允许用户使用某个特定的地址作为这段内存的起地址。如果被置为NULL,则系统自动分配一个地址。length参数指定内存的这段的长度。port参数用来设置内存段的访问权限。它可以取以下几个值的按位或:
a、PROT_READ 内存段可读
b、PROT_WRITE 内存段可写
c、PROT_EXEC 内存段可执行
d、PROT_NONE 内存段不能被访问
flags参数控制内存段内容被修改后的程序的行为。下表是常用的flags值,注意的是MAP_SHARED和MAP_PRIVATE不能同时指定。
这里写图片描述
fd参数是被映射文件对应的文件描述符。一般通过open系统调用获得。offset参数设置从文件的那个位置开始映射。
mmap函数调用成功返回指向目标内存区域的指针,失败返回MAP_FAILED( ( void* ) -1 )并设置errno,munmap调用成功返回0,失败返回-1并置errno。
(4)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,咋表示从输入数据流的何处开始读数据。此时如果off_in被设置为NULL,则它将指出具体的偏移位置。fd_out/off_out参数的含义与fd_in/off_in相同。len参数指定移动数据的长度;flags参数则控制数据如何移动,它可以被设置为下表中的某些值的按位或:
这里写图片描述
使用splice函数时,fd_in和fd_out必须至少有一个是管道文件描述符。 splice函数调用成功时返回移动字节的数量。它可能返回0,表示没有数据需要移动,这发生在从管道中读取数据(fd_in是管道文件描述符)而该管道没有被写入任何数据时。splice函数调用失败返回-1并置errno。常见的errno表示:
这里写图片描述
(5)tee函数:
该函数在两个管道文件描述符之间复制数据,也是零拷贝操作。它不消耗数据,因此源文件描述符上的数据仍然可以用于后续的操作读数。函数原型:

#include<fcntl.h>
ssize_t tee(  int fd_in, int fd_out, size_t len, unsigned int flags );

该函数和splice相同(但fd_in和fd_out必须都是管道文件描述符)。tee函数调用成功返回在两个管道描述符之间的传输字符字节数。返回0表示没有复制任何数据。调用失败和其他函数一样返回-1并置errno。

3、控制I/O行为:

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

#include<fcntl.h>
int fcntl( int fd, int cmd, ...);

fd参数是被操作的文件描述符,cmd参数指定执行何种类型的操作。根据操作类型的不同,该函数可能还需要第三个可选参数arg。fcntl函数支持的常用操作机器参数表如下:
这里写图片描述
这里写图片描述
本博客参考文献:《Linux高性能服务器编程》,作者游双。
这里也把这本书推荐给大家,这本书上面有很多服务器源代码,在看不懂的时候不妨看看那源代码,也可以下载再到这本书的源代码,通过程序执行来帮助理解会提高你的学习效率。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值