Linux下的管道pipe----管道容量和实现机制

  管道容量 

   linux上的PipE容量为(capacity)65536个字节;实验得到Ubuntu的PIPE_BUF为4096。

   当 管道的写端存在时,如果请求的字节数目大于PIPE_BUF,则返回管道中现有的数据字节数,如果请求的字节数目不大于PIPE_BUF,则返回管道中现有数据字节数(此时,管道中数据量小于请求的数据量);或者返回请求的字节数(此时,管道中数据量不小于请求的数据量)(PIPE_BUF在 include/linux/limits.h中定义,不同的内核版本可能会有所不同。Posix.1要求PIPE_BUF至少为512字节,red hat 7.2中为4096字节)

要保证write操作是原子操作,一次写入的字节数n就不能超过PIPE_BUF;非阻塞模式下当然还需要有>=n的空余空间,否则写入失败。一次写入大于PIPE_BUF的数据,不管是不是阻塞模式都不保证是原子操作

非阻塞模式下,能一次性写入的最大数据量就是管道的容量即65536字节了,注意这里的一次性写入是指write不返回的情况下,和原子操作不是一回事。如果写入的更长,也只能写这么多,write函数会返回已写入的长度

如果写端不存在,读的时候会返回0,代表读到了文件尾。如果读端不存在,写的时候就会产生SIGPIPE信号(忽略此信息的情况下write会返回失败,errno为EPIPE)

在linux终端输入“man 7 pipe”命令,可以查找到管道容量的最大和最小字节数

Linux下的管道pipe----管道容量和实现机制_第1张图片

管道实现机制

(一)管道外部实现
当我们定义一个管道时,这个管道是由内核管理的一个缓冲区,可以抽象为现实生活中的一个传输线路。管道的一端连接一个进程的输出,这个进程会向管道中放入信息。管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。当管道中没有信息的话,从管道中读取的进程会等待,直到另一端的进程放入信息。当管道被放满信息的时候,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也自动消失。 从原理上,管道利用fork机制建立,从而让两个进程可以连接到同一个PIPE上。
在Linux中,管道是一种使用非常频繁的通信机制。从本质上说,管道也是一种文件,但它又和一般的文件有所不同,管道可以克服使用文件进行通信的两个问题,具体表现为:
· (1)限制管道的大小。实际上,管道是一个固定大小的缓冲区。在Linux中,该缓冲区的大小为1页,即4K字节,使得它的大小不象文件那样不加检验地增长。使用单个固定缓冲区也会带来问题,比如在写管道时可能变满,当这种情况发生时,随后对管道的write()调用将默认地被阻塞,等待某些数据被读取,以便腾出足够的空间供write()调用写。
· (2) 读取进程也可能工作得比写进程快。当所有当前进程数据已被读取时,管道变空。当这种情况发生时,一个随后的read()调用将默认地被阻塞,等待某些数据被写入,这解决了read()调用返回文件结束的问题。
(二)管道的内部实现机制

Linux下的管道pipe----管道容量和实现机制_第2张图片

 实际上pipe并没有单独的实现数据结构,他利用了文件的在 Linux 中,而是借助了文件系统的file结构和VFS的索引节点inode。通过将两个 file 结构指向同一个临时的 VFS 索引节点,而这个 VFS 索引节点又指向一个物理页面而实现的。有两个 file 数据结构,但它们定义文件操作例程地址是不同的,其中一个是向管道中写入数据的例程地址,而另一个是从管道中读出数据的例程地址。这样,用户程序的系统调用仍然是通常的文件操作,而内核却利用这种抽象机制实现了管道这一特殊操作。
(三)管道的容量
当管道一端不断地读取数据,另一端却不输出数据。根据linux的实现机制当管道读满是输出端自动阻塞。所以这个管道是有大小的
现在我们编写程序来测试管道的大小

[cpp]  view plain  copy
 
  1. #include   
  2. #include   
  3. #include   
  4.   
  5. int main(int argc, char* argv[])  
  6. {  
  7.     int pipefds[2]; //[0] for read, [1] for write  
  8.     pipe(pipefds);  
  9.       
  10.     char buf[4096];  
  11.     for (int i = 0; i < sizeof(buf); ++i)  
  12.     {  
  13.         buf[i] = 0x7f;  
  14.     }  
  15.       
  16.     ssize_t ret = -1;  
  17.       
  18.     int loop = 100;  
  19.     if (argc > 1)  
  20.     {  
  21.         loop = atoi(argv[1]);  
  22.     }  
  23.       
  24.     for (int i = 0; i < loop; ++i)  
  25.     {  
  26.         printf("loop: %d\n", i);  
  27.         ret = write(pipefds[1], buf, sizeof(buf));  
  28.         if (ret < 0)  
  29.         {  
  30.             perror(NULL);  
  31.         }  
  32.         else  
  33.         {  
  34.             printf("%d\n", ret);  
  35.         }  
  36.     } // 当i=16的时候会阻塞,可知管道大小为64k  
  37.       
  38.     close(pipefds[0]);  
  39.     close(pipefds[1]);  
  40.       
  41.     return 0;  
  42. }  

你可能感兴趣的:(Linux)