linux进程间命名管道,Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)

管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。

实现机制:

管道是由内核管理的一个缓冲区,相当于我们放入内存中的一个纸条。管道的一端连接一个进程的输出。这个进程会向管道中放入信息。

管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。

一个缓冲区不需要很大,它被设计成为环形的数据结构,以便管道可以被循环利用。

当管道中没有信息的话,从管道中读取的进程会等待,直到另一端的进程放入信息。

当管道被放满信息的时候,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也自动消失。

7559cecfc2aa311e4c00673e4df73efc.png

从原理上,管道利用fork机制建立,从而让两个进程可以连接到同一个PIPE上。

最开始的时候,上面的两个箭头都连接在同一个进程Process1上(连接在Process1上的两个箭头)。

当fork复制进程的时候,会将这两个连接也复制到新的进程(Process2)。

随后,每个进程关闭自己不需要的一个连接(两个黑色的箭头被关闭;Process1关闭从PIPE来的输入连接,Process2关闭输出到PIPE的连接),这样,剩下的红色连接就构成了如上图的PIPE。

5150748e2be4f93ba6b53e1a5abc4eb8.png

实现细节:

在Linux中,管道的实现并没有使用专门的数据结构,而是借助了文件系统的file结构和VFS的索引节点inode。

通过将两个file结构指向同一个临时的VFS索引节点,而这个VFS索引节点又指向一个物理页面而实现的。如下图

78b08a74ad2f831d95a067473fe59e07.png

有两个file数据结构,但它们定义文件操作例程地址是不同的,其中一个是向管道中写入数据的例程地址,而另一个是从管道中读出数据的例程地址。

这样,用户程序的系统调用仍然是通常的文件操作,而内核却利用这种抽象机制实现了管道这一特殊操作。

关于管道的读写

管道实现的源代码在fs/pipe.c中,在pipe.c中有很多函数,其中有两个函数比较重要,即管道读函数pipe_read()和管道写函数pipe_wrtie()。

管道写函数通过将字节复制到VFS索引节点指向的物理内存而写入数据,而管道读函数则通过复制物理内存中的字节而读出数据。

当然,内核必须利用一定的机制同步对管道的访问,为此,内核使用了锁、等待队列和信号。

当写进程向管道中写入时,它利用标准的库函数write(),系统根据库函数传递的文件描述符,可找到该文件的file结构。

file结构中指定了用来进行写操作的函数(即写入函数)地址,

于是,内核调用该函数完成写操作。写入函数在向内存中写入数据之前,必须首先检查VFS索引节点中的信息,同时满足如下条件时,才能进行实际的内存复制工作:

·内存中有足够的空间可容纳所有要写入的数据;

·内存没有被读程序锁定。

如果同时满足上述条件,写入函数首先锁定内存,然后从写进程的地址空间中复制数据到内存。

否则,写入进程就休眠在VFS索引节点的等待队列中,接下来,内核将调用调度程序,而调度程序会选择其他进程运行。

写入进程实际处于可中断的等待状态,当内存中有足够的空间可以容纳写入数据,或内存被解锁时,读取进程会唤醒写入进程,这时,写入进程将接收到信号。

当数据写入内存之后,内存被解锁,而所有休眠在索引节点的读取进程会被唤醒。

管道的读取过程和写入过程类似。但是,进程可以在没有数据或内存被锁定时立即返回错误信息,而不是阻塞该进程,这依赖于文件或管道的打开模式。

反之,进程可以休眠在索引节点的等待队列中等待写入进程写入数据。当所有的进程完成了管道操作之后,管道的索引节点被丢弃,而共享数据页也被释放。

Linux函数原型

#includeintpipe(intfiledes[2]);

filedes[0]用于读出数据,读取时必须关闭写入端,即close(filedes[1]);

filedes[1]用于写入数据,写入时必须关闭读取端,即close(filedes[0])。

程序实例:

intmain(void){intn;intfd[2];pid_tpid;charline[MAXLINE];if(pipe(fd)0){/*先建立管道得到一对文件描述符*/exit(0);}if((pid=fork())0)/*父进程把文件描述符复制给子进程*/exit(1);elseif(pid>0){/*父进程写*/close(fd[0]);/*关闭读描述符*/write(fd[1],"helloworld",14);}else{/*子进程读*/close(fd[1]);/*关闭写端*/n=read(fd[0],line,MAXLINE);write(STDOUT_FILENO,line,n);}exit(0);}

命名管道(namedPIPE)

由于基于fork机制,所以管道只能用于父进程和子进程之间,或者拥有相同祖先的两个子进程之间(有亲缘关系的进程之间)。

为了解决这一问题,Linux提供了FIFO方式连接进程。FIFO又叫做命名管道(namedPIPE)。

FIFO(Firstin,Firstout)为一种特殊的文件类型,它在文件系统中有对应的路径。

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值