您的位置:首页 > 运维架构 > Linux

Linux下的管道容量及其实现机制

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

·     (2) 读取进程也可能工作得比写进程快。当所有当前进程数据已被读取时,管道变空。当这种情况发生时,一个随后的read()调用将默认地被阻塞,等待某些数据被写入,这解决了read()调用返回文件结束的问题。

2、管道的内部实现机制



        实际上pipe并没有单独的实现数据结构,他利用了文件在 Linux
中,借助了文件系统的file结构和VFS的索引节点inode。通过将两个file 结构指向同一个临时的 VFS 索引节点,而这个 VFS 索引节点又指一个物理页面而实现的。有两个 file 数据结构,但它们定义文件操作例程地址是不同的,其中一个是向管道中写入数据的例程地址,而另一个是从管道中读出数据的例程地址。这样,用户程序的系统调用仍然是通常的文件操作,而内核却利用这种抽象机制实现了管道这一特殊操作。
3、管道的容量
      当管道一端不断地读取数据,另一端却不输出数据。根据linux实现机制当管道读满是输出端自动阻塞。所以这个管道是有大小的。编程测试管道的大小:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
    int pipefds[2]; //[0] for read, [1] for write
    pipe(pipefds);
    char buf[4096];
    for (int i = 0; i < sizeof(buf); ++i)
    {
        buf[i] = 0x7f;
    }
   size_t ret = -1;
   int loop = 100;
    if (argc > 1)
    {
        loop = atoi(argv[1]);
    }
  for (int i = 0; i < loop; ++i)
    {
        printf("loop: %d\n", i);
        ret = write(pipefds[1], buf, sizeof(buf));
        if (ret < 0)
        {
            perror(NULL);
        }
        else
        {
            printf("%d\n", ret);
        }
   } 
 close(pipefds[0]);
 close(pipefds[1]);
  return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: