【进程间通信】普通管道和命名管道(FIFO)

管道机制的主体是系统调用pipe(),但是由pipe()所建立的 管道的两端都在同一个进程,所以必须在pipe()的配合下,才能在父子进程之间或两个子进程之间建立起进程间的通信管道;由于管道两端都是以打开文件的形式出现在相关的进程中,在具体实现上也是作为匿名文件来实现的,所以pipe()的代码与文件系统密切相关;


(1)在sys_pipe()中,先由do_pipe()建立起一个管道,通过作为调用参数的数组fd[]返回代表着管道两端的两个已打开文件号,再由copy_to_user()将数组fd[]将数组fd[]复制到用户空间;

(1.1)在do_pipe()中,首先为管道的两端f1和f2各分配一个file数据结构,使用get_empty_filp()分配两个file,同时每一个文件都要有一个inode;由于这个文件在创建管道之初并不存在,所以使用get_pipe_node临时创建一个inode数据结构,在inode的i_mode设置相关属性,如S_IFIFO,以及设置i_pipe指向pipe_inode_info,i_fop指向rdwr_pipe_fops;管道文件是无形的,它不出现在磁盘或其他文系统存储介质上,只存在于内存空间,因此inode_operations是不使用的,其他进程也就无从打开或访问这个文件了;然后分配dentry建立file和inode的联系,其中dnetry的d_op指针指向pipefs_dentry_operations,支持删除pipeefs_delete_dentry;由于管道是单向的,所以f1端设置成只读,f2端设置成只写,两端的fops分别设置成read_pipe_fops和write_pipe_fops;而inode的i_ops是为了表示数据流向的连通性;

(2)管道的这两个文件,具体的使用是在应用程序自行安排的;如执行,ls -l | wc -l,如下图,A相当于shell,B执行wc -l,C执行ls -l;不过,在进程C中要将标准输出端stdout重定向到管道的写端,而在进程B中要将标准输入通道stdin重定向到管道的读端;


(3)在pipe_close()中,分别对inode的pipe_inode_info中的readers和writers进行增减,当两个计数都减到为0,整个管道也就完成了使命,此时将缓冲区页面的pipe_inode_info释放掉,最后还要释放掉inode结构,不需要回写到磁盘

(4)sys_read()中,通过read_pipe_fops指针到达了pipe_read(),在pipe_read()中,seek是不允许的,如果缓冲区中没有数据,那就要睡眠等待了,此外,管道的writers计数已经为0,也就是没有生产者向管道中写了,这时候当然就不能再等待了,当指定O_NOBLOCK,表示读不到东西了,当前进程不应该阻塞了;当管道中有了数据,有几种情况需要考虑,读到了所要求的长度,所以count为0,管道的数据已经读完,但是还没有达到所要求的长度,返回实际读出的长度,读到了所要的长度,但是管道中还有数据;读完后,还要唤醒睡眠的生产者进程;

(5)sys_write()中,如果管道的读端已经关闭,那就表示已经没有消费者,既然已经没有消费者了,那么生产者的存在就失去了意义,于是就转到标号sigpipe处,向当前进程发送一个SIGPIPE,一般进程收到该信号,就调用do_exit();然后还要判断count不能超过PIPE_BUF,超过就将free置成1,free表示开始写入之前缓冲区至少还有的空闲字节;有了足够空间以后,就向缓冲区中写入;整个写入过程在一个while循环中,另外要求长度大于PIPE_BUF还要分几次来写,这里进程睡眠醒来,只检验缓冲区是否有空间,不问空间有多大;

(6)管道机制是一种典型的生产者,消费者机制;

(7)管道是一种无名,无形的文件,只能通过fork()创建于近亲进程,而不能成为任意两个进程之间通信的机制,更不可能成为一种通用的进程间通信模型;因此引入命名管道,文件的inode是存在于磁盘或其他文件系统介质的,使得任何进程在任何时间都可以和这个文件联系;同样它是一个FIFO文件,不允许在文件内移动读写指针位置;一个命名管道是由mknod建立的,如mknod mypipe p,其中p表示所建立的节点的类型为命名管道;

(7.1)管道的建立是在do_pipe()中,通过fork()的过程伸展到两个进程之间;而命名管道是不同的,在open()时,进程在内核中sys_open中会进入filp_open()中,然后在open_namei()调用path_walk(),根据文件的路径名在文件系统找到代表着这个文件的inode;在将磁盘上的inode读入内存中,要根据文件的类型(FIFO,文件的S_IFIFO标志位为1),将inode中的i_op和i_fop指针指向相应的inode_operations和file_operations,但是像FIFO这样的特殊文件调用inode_special_inode()来加以初始化;在inode_special_inode()中,对于FIFO文件,其inode结构的inode_operations结构指针i_op设置为0,而file_operations指向def_fifo_fops;根据此函数指针,进入fifo_open(),首次打开这个文件爱你的进程来到fifo_open()时,该管道的缓冲页面尚未分配,所以使用pipe_new()分配所需的pipe_inode_indfo和缓冲页面;以后进程再来打开该文件就会跳过这一段;

(7.2)FIFO文件可以按三种不同的模式打开,即只读,只写,读写;在open时有个参数flags,如果标志位O_NONBLOCK为1,就表示在打开的过程中即使某些条件得不到满足也不要睡眠等待,立即返回;命名果断到,任意一个进程可以通过相同的路径名打开同一个FIFO文件;

(7.2.1)对于只读;如果管道的写端已经打开,那么读端的打开就完成了命名管道的建立过程;此时,写端的进程处于睡眠阶段,等待着命名管道建立过程的完成,因此要将它唤醒,然后两个进程就可以返回各自的用户空间,完成命名管道的通信了;如果管道的写端尚未打开,若O_NONBLOCK为1,表示不应等待,此时应设置f_version中的PIPE_WCOUNTER(*inode),这是与select机制有关的;如果管道的写端尚未打开,若O_NONBLOCK为0,因此要进入睡眠,等待生产者打开命名管道的写端,来建立写的过程;

(7.2.2)对于只写,情况只读类似,只有O_NONBLOCK为1时,此时写打开失败;

(7.2.2)对于读写,相当于在同一个进程同时打开了命名管道的两端,所以不管怎样都不需要等待;但是有可能已经有某个进程先打开了写端或读端而正在睡眠等待;所以只要有任何一端第一次打开,也就唤醒了正在睡眠等待的进程;

(7.3)命名管道一旦建立,以后的读,写以及关闭操作就与普通管道完全相同了;其中,虽然FIFIO文件的inode节点在磁盘上,但那只是一个节点,而文件的数据则只存在于内存缓冲页面中,与普通管道一样;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值