高级I/O函数——pipe、dup、dup2、sendfile、mmap、munmap、splice、fcntl

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/YL970302/article/details/89603790

一、创建文件描述符的函数

1、pipe函数(创建管道)

(1)pipe函数的定义:

    

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

 

     通过pipe函数创建的这两个文件描述符fd[0],fd[1]分别构成管道的两端,并且fd[1]只能用于往管道中写数据,fd[0]只能从管道中读取数据,不能反过来使用。默认情况下,这一对文件描述符都是阻塞的。如果管道的写端文件描述符fd[1]的引用计数是0,那就证明没有任何进程往管道中写数据,则针对管道的读端文件描述符fd[0]的操作将返回0,即读取到了文件结束标记EOF。反之,如果管道的读端文件描述符fd[0]的引用计数为0,则代表没有任何进程从管道中读取数据,则针对管道的写端文件描述符fd[1]的write操作将失败,并引发SIGPIPE信号。

(2)请问管道内部传输的数据数据是什么形式?

     字节流

(3)请问该字节流跟TCP字节流的概念相同吗?

   相同,但是又有细微的差别。应用层能往一个TCP连接中写多少数据,取决于对方的接收窗口和本端的拥塞窗口的大小,而管道本身拥有一个容量限制,它规定如果应用程序不将数据从管道中读走的话,该管道最多能被写入多少字节的数据,Linux 2.6.11内核起,管道容量的大小默认是65536字节。

(4)请问管道的结构?

  打开管道,在内存中分配一块空间,写入的数据存放在这块内存中,但是有名管道的管道文件放在磁盘上

2、dup函数和dup2函数(这两个函数是用于复制文件描述符的函数)

  通过dup函数和dup2函数,可以实现将标准输入重定向到一个文件或者把标准输出重定向到一个网络连接。

#include<unistd.h>

int dup(int file_descriptor)

int dup2(int file_descriptor_one, int file_descriptor_two)

dup函数创建一个新的文件描述符,该新文件描述符和原有文件描述符file_descriptor指向相同的文件、管道或者网络连接,并且dup返回的文件描述符总是取系统当前可用的最小整数值。

注:dup和dup2创建的文件描述符并不继承源文件描述符的属性。

 

二、用于读写数据的函数

1、sendfile函数

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

(1)sendfile函数原型

  #include<sys/sendfile.h>

  ssize_t sendfile(int out_fd, int in_fd, off_t* offset,size_t count);成功时返回传输的字节数

in_fd必须时一个支持类似mmap函数的文件描述符,即它必须指向真实的文件,不能是socket和管道;而out_fd则必须是一个socket。

(2)请问这种读写数据的函数和read、write的区别在哪?

   首先分析用read/write函数来实现网络上的文件传输。

   int n = read(fd, tmp_buf,len);

   write(socketfd, tmp_buf,n);

这段代码看起来很简单,好像并没有产生太多的开销,但实际上这两个函数在调用的过程中,目标数据被拷贝了4次,同时,发生了4次用户态和内核态的空间切换。

a:调用read函数,从用户态切换到内核,DMA模块将文件数据从磁盘拷到了内核空间

b:read函数返回,从内核态切换到用户态,内核空间数据拷贝到用户空间

c:write函数调用,从用户态切换到内核态,用户数据拷贝到与socket相关的内核空间

d:write函数返回,从内核态切换到用户态,DMA模块将内核空间数据拷贝到协议引擎上

而sendfile函数调用只需要两步就可以完成文件的发送

a:sendfile函数通过DAM模块将文件数据从磁盘空间拷贝到内核缓冲区

b:文件数据并不需要从内核缓冲区复制到socket关联的缓冲区,而是将记录数据位置和长度的描述符加入到socket缓冲区,DAM(直接存储器读写)模块直接将数据从内核缓冲区传递给协议引擎

虽然数据实际上仍然是由磁盘复制到内存,再由内存复制到发送设备,但是,从操作系统的角度来看,这就是“零拷贝”,因为内核空间不存在冗余数据,应用“零拷贝”特性,除了避免复制之外,还有更少的上下文切换,更少的CPU cache污染以及CPU没有必要计算校验和。

  总结:区别就是sendfile函数可以避免复制,减少上下文的切换,更少的CPU cache污染以及CPU没有必要计算校验和

2、mmap函数和munmap函数

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

3、splice函数

   用于再两个文件描述符之间移动数据,也是零拷贝操作

4、fcntl函数

  提供了对文件描述符的各种控制操作。具体功能有

  1. 复制文件描述符
  2. 设置文件描述符标志位
  3. 设置文件描述符对应的文件状态标志,如阻塞非阻塞等
  4. 设置与异步io有关的属性信息
  5. 设置文件锁

猜你喜欢

转载自blog.csdn.net/YL970302/article/details/89603790