进程间的通信之管道

一.管道的分类:

     管道是进程间通讯的一种(进程间通讯还有:信号,信号量,消息队列,共享存储,套接字),它分为无名管道和有名管道。

     有名管道应用于任意俩个进程之间数据的单项传递。

     无名管道只能引用于父子进程之间。

二.管道的工作:

     1.管道的操作

                             

  有名管道                无名管道
 创建  mkfifo int pipe(int  fd[2])
打开 open int pipe(int  fd[2])
write write(fd[1],buff,len)
read read(fd[0],buff,size)
关闭 close close(fd[1]);close(fd[0]);

三.注意

   1.有名管道:

       1) 在文件目录树中有一个文件标示(管道文件)。实际不占据磁盘空间。
数据缓存在内存上。

       2)如果一个进程以只写方式打开一个管道文件,open 会阻塞运行,直到有一个进程
以读方式打开管道文件。open 才会返回,进程才会接着执行。
如果一个进程以只读方式打开一个管道文件,open 会阻塞运行,直到有一个进程
以写方式打开管道文件。open 才会返回,进程才会接着执行。
read 函数也会阻塞运行,直到写端写入数据或者所有的写端都关闭。
read 读取数据并且会将内存上的已读数据清空。

只写,open阻塞运行

直到有一个读端打开管道

    2.管道都是半双工通讯,而无名管道创建后,父进程在 fork 产生子进程后,
两个进程分别有一对读写,所以,要在父子进程分别关闭读或写。

四.扩展

    1.管道的默认大小是多少??能不能修改管道的大小?

        一页,即4K字节。

        可以通过修改linux/limits.h/pipe_buff来修改管道大小

     2.管道操作的内核实现。(空间的开辟, 读写操作偏移量的变化)

          linux下的进程的用户态地址空间都是相互独立的,因此俩个进程是没法直接通信的,因为找不到彼此的存在,儿内核是进程间共享的,因此进程间想通信只能通过内核作为中间人,来传达信息。下图显示了俩个进程间通过内核缓存进行通信的过程:

五.代码

1.有名管道

 写端:                                                                                                           读端:


            

    pipe的实现就是和上述图示一样,在pipefs文件系统的inode中有一个属性:struct pipe_inode_info*i_pipe;

   这个结构体定义如下:

                            

        这个结构体定义了管道的缓存,由base指向,缓存大小为一个内存页,有如下定义:

              #define PIPE_SIZEPAGE_SIZE

        其实到现在我们大概猜得到管道的是实现原理,在一个进程中,向管道中写入数据时,其实就是写入这个缓存中;然后在另一个进程读取管道时,其实就是从这个缓存读取,实现进程的通信。

        这个缓存也可以解释为什么管道是单通道的:因为只有一个缓存,如果是双通道,那么两个进程同时向这块缓存写数据时,这样会导致数据覆盖,即一个进程的数据被另一个进程的数据覆盖.而向套接字有读写缓存,因此套接字是双通道的.

        从pipe函数开始,看看内核是如何创建管道的.pipe系统调用在内核对应的服务例程为sys_pipe,在sys_pipe函数中接着调用do_pipe创建两个管道描述符,一个用于写,另一个用于读。

        总结下do_pipe函数的执行过程:

          1.实例化两个空file结构体;

           2.创建带有pipe属性的inode结构;

           3.在当前进程文件描述符表中找出两个未使用的文件描述符;

           4.为这个inode分配dentry结构体,关联file和inode

           5.针对可读和可写file结构,分别设置相应属性,主要是操作函数集合属性

           6.关联文件描述符和file结构

           7.将两个文件描述符返回给用户;

2.无名管道


        遇到并解决的问题:

   ①。第14行刚开始写成了:pid_t pid=fork()

            这样导致fork在管道之前创建,也就是说先有了父子进程才有的管道,那么父子进程都会对管道操作

   ②。32行不能用strlen,要用sizeofstrlen会阻塞,因为一开始读的是0




你可能感兴趣的:(Linux)