0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

Linux爱好者 来源:华山大师兄 作者:华山大师兄 2020-12-11 16:41 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

管道(pipe)

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

实现机制:

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

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

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

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

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

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

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

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

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

实现细节:

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

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

dae718ba-28a5-11eb-a64d-12bb97331649.jpg

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

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

关于管道的读写

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Linux函数原型

#include int pipe(int filedes[2]);

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

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

程序实例:

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

命名管道(named PIPE)

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

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

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

当一个进程以读(r)的方式打开该文件,而另一个进程以写(w)的方式打开该文件,那么内核就会在这两个进程之间建立管道,所以FIFO实际上也由内核管理,不与硬盘打交道。

之所以叫FIFO,是因为管道本质上是一个先进先出的队列数据结构,最早放入的数据被最先读出来,从而保证信息交流的顺序。

FIFO只是借用了文件系统(file system,命名管道是一种特殊类型的文件,因为Linux中所有事物都是文件,它在文件系统中以文件名的形式存在。)来为管道命名。

写模式的进程向FIFO文件中写入,而读模式的进程从FIFO文件中读出。

当删除FIFO文件时,管道连接也随之消失。

FIFO的好处在于我们可以通过文件的路径来识别管道,从而让没有亲缘关系的进程之间建立连接

函数原型:

#include #include int mkfifo(const char *filename, mode_t mode);int mknode(const char *filename, mode_t mode | S_IFIFO, (dev_t) 0 );

其中pathname是被创建的文件名称,mode表示将在该文件上设置的权限位和将被创建的文件类型(在此情况下为S_IFIFO),dev是当创建设备特殊文件时使用的一个值。

因此,对于先进先出文件它的值为0。

程序实例:

#include #include #include #include int main() { int res = mkfifo("/tmp/my_fifo", 0777); if (res == 0) { printf("FIFO created/n"); } exit(EXIT_SUCCESS); }

编译这个程序:

gcc –o fifo1.c fifo

运行这个程序:

$ fifo1

用ls命令查看所创建的管道

$ls-lF/tmp/my_fifoprwxr-xr-x 1 root root 0 05-08 20:10 /tmp/my_fifo|

注意:ls命令的输出结果中的第一个字符为p,表示这是一个管道。

最后的|符号是由ls命令的-F选项添加的,它也表示是这是一个管道。

FIFO读写规则

1.从FIFO中读取数据:约定:如果一个进程为了从FIFO中读取数据而阻塞打开了FIFO,那么称该进程内的读操作为设置了阻塞标志的读操作

2.从FIFO中写入数据:约定:如果一个进程为了向FIFO中写入数据而阻塞打开FIFO,那么称该进程内的写操作为设置了阻塞标志的写操作。

详见:http://blog.csdn.net/MONKEY_D_MENG/article/details/5570468

信号(Signal)

信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;

Linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)

信号种类

db100efa-28a5-11eb-a64d-12bb97331649.jpg

每种信号类型都有对应的信号处理程序(也叫信号的操作),就好像每个中断都有一个中断服务例程一样。

大多数信号的默认操作是结束接收信号的进程;

然而,一个进程通常可以请求系统采取某些代替的操作,各种代替操作是:

忽略信号。随着这一选项的设置,进程将忽略信号的出现。

有两个信号 不可以被忽略:SIGKILL,它将结束进程;

SIGSTOP,它是作业控制机制的一部分,将挂起作业的执行。

恢复信号的默认操作。

执行一个预先安排的信号处理函数。

进程可以登记特殊的信号处理函数。

当进程收到信号时,信号处理函数将像中断服务例程一样被调用,当从该信号处理函数返回时,控制被返回给主程序,并且继续正常执行。

但是,信号和中断有所不同。

中断的响应和处理都发生在内核空间,而信号的响应发生在内核空间,信号处理程序的执行却发生在用户空间。

那么,什么时候检测和响应信号呢?通常发生在两种情况下:

当前进程由于系统调用、中断或异常而进入内核空间以后,从内核空间返回到用户空间前夕;

当前进程在内核中进入睡眠以后刚被唤醒的时候,由于检测到信号的存在而提前返回到用户空间。

函数原型等详见:

http://www.cnblogs.com/biyeymyhjob/archive/2012/08/04/2622265.html

信号本质

信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。

信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。

信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了。

信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。

信号来源

信号事件的发生有两个来源:

硬件来源(比如我们按下了键盘或者其它硬件故障);

软件来源,最常用发送信号的系统函数是kill, raise, alarm和setitimer以及sigqueue函数,软件来源还包括一些非法运算等操作。

关于信号处理机制的原理(内核角度)

内核给一个进程发送软中断信号的方法,是在进程所在的进程表项的信号域设置对应于该信号的位。

这里要补充的是,

如果信号发送给一个正在睡眠的进程,那么要 看该进程进入睡眠的优先级,如果进程睡眠在可被中断的优先级上,则唤醒进程;

否则仅设置进程表中信号域相应的位,而不唤醒进程。

这一点比较重要,因为进程检查是否收到信号的时机是:一个进程在即将从内核态返回到用户态时;

或者,在一个进程要进入或离开一个适当的低调度优先级睡眠状态时。

内核处理一个进程收到的信号的时机是在一个进程从内核态返回用户态时。

所以,当一个进程在内核态下运行时,

软中断信号并不立即起作用,要等到将返回用户态时才处理。

进程只有处理完信号才会返回用户态(上面的例子程序中,在步骤5中,解除阻塞后,先打印caught SIGQUIT,再打印SIGQUIT unblocked,即在sigprocmask返回前,信号处理程序先执行),进程在用户态下不会有未处理完的信号。

内核处理一个进程收到的软中断信号是在该进程的上下文中,因此,进程必须处于运行状态。

如果进程收到一个要捕捉的信号,那么进程从内核态返回用户态时执行用户定义的函数。

而且执行用户定义的函数的方法很巧妙,内核是在用户栈上创建一个新的层,该层中将返回地址的值设置成用户定义的处理函数的地址,

这样进程从内核返回弹出栈顶时就返回到用户定义的函数处,从函数返回再弹出栈顶时,才返回原先进入内核的地方,接着原来的地方继续运行。

这样做的原因是用户定义的处理函数不能且不允许在内核态下执行(如果用户定义的函数在内核态下运行的话,用户就可以获得任何权限)。

在信号的处理方法中有几点特别要引起注意。

第一,在一些系统中,当一个进程处理完中断信号返回用户态之前,内核清除用户区中设定的对该信号的处理例程的地址,

即下一次进程对该信号的处理方法又改为默认值,除非在下一次信号到来之前再次使用signal系统调用。

这可能会使得进程在调用signal之前又得 到该信号而导致退出。在BSD中,内核不再清除该地址。

但不清除该地址可能使得进程因为过多过快的得到某个信号而导致堆栈溢出。为了避免出现上述情况。

在 BSD系统中,内核模拟了对硬件中断的处理方法,即在处理某个中断时,阻止接收新的该类中断。

第二个要引起注意的是,如果要捕捉的信号发生于进程正在一个系统调用中时,并且该进程睡眠在可中断的优先级上(若系统调用未睡眠而是在运行,根据上面的分 析,等该系统调用运行完毕后再处理信号),

这时该信号引起进程作一次longjmp,跳出睡眠状态,返回用户态并执行信号处理例程。

当从信号处理例程返回 时,进程就象从系统调用返回一样,但返回了一个错误如-1,并将errno设置为EINTR,指出该次系统调用曾经被中断。

这要注意的是,BSD系统中内 核可以自动地重新开始系统调用,或者手如上面所述手动设置重启。

第三个要注意的地方:若进程睡眠在可中断的优先级上,则当它收到一个要忽略的信号时,该进程被唤醒,但不做longjmp,一般是继续睡眠。

但用户感觉不 到进程曾经被唤醒,而是象没有发生过该信号一样。

所以能够使pause、sleep等函数从挂起态返回的信号必须要有信号处理函数,如果没有什么动作,可以将处理函数设为空。

第四个要注意的地方:内核对子进程终止(SIGCLD)信号的处理方法与其他信号有所区别。

当进程正常或异常终止时,内核都向其父进程发一个SIGCLD 信号,缺省情况下,父进程忽略该信号,就象没有收到该信号似的,

如果父进程希望获得子进程终止的状态,则应该事先用signal函数为SIGCLD信号设 置信号处理程序,在信号处理程序中调用wait。

SIGCLD信号的作用是唤醒一个睡眠在可被中断优先级上的进程。

如果该进程捕捉了这个信号,就象普通信号处理一样转到处理例程。

如果进程忽略该信号,则 什么也不做。

其实wait不一定放在信号处理函数中,但这样的话因为不知道子进程何时终止,在子进程终止前,wait将使父进程挂起休眠。

信号生命周期

db47662a-28a5-11eb-a64d-12bb97331649.jpg

责任编辑:lq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    88

    文章

    11628

    浏览量

    218013
  • 管道
    +关注

    关注

    3

    文章

    148

    浏览量

    18341
  • 数据结构
    +关注

    关注

    3

    文章

    573

    浏览量

    41378
  • signal
    +关注

    关注

    0

    文章

    110

    浏览量

    25817

原文标题:Linux 进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)

文章出处:【微信号:LinuxHub,微信公众号:Linux爱好者】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    飞凌嵌入式ElfBoard-Linux系统中的文件分类

    的主要功能和Windows系统中快捷方式类似,它是指向另一个文件或者目录,符号链接本身不包含数据。6.管道Pipe)和套接字(Socket)Linux提供了管道和套接字机制,用于不同
    发表于 12-09 08:59

    Linux进程通信(IPC)全解析:从管道到 Socket,一篇讲透

    在 Linux 世界里,进程并非孤立存在。无论是后台服务协作(如 Web 服务器与数据库)、命令行工具联动(如ps | grep),还是复杂应用的模块通信,都离不开 进程
    的头像 发表于 11-14 21:38 1.2w次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>进程</b><b class='flag-5'>间</b><b class='flag-5'>通信</b>(IPC)全解析:从<b class='flag-5'>管道</b>到 Socket,一篇讲透

    管道运输中的传感器:管道状态监测

    在现代工业体系中,管道运输承担着不可或缺的重任,无论是石油、天然气等能源的长距离输送,还是化工原料在不同生产环节的流转,管道运输都凭借其高效、连续、低成本的优势,成为工业生产和能源供应的关键环节。
    的头像 发表于 09-05 16:10 358次阅读

    【HZ-T536开发板免费体验】—— linux 进程创建

    Linux进程通信方式有这几种: 1。管道 2。信号量 3。消息队列 4。共享内存 在本帖子中,我会讲解fork(),exit()系统调用的
    发表于 09-01 20:49

    功率放大器赋能:基于正则模态展开的管道弯曲处导波散射研究

    管道网络中,导波(guidedwaves)的传播特性对于无损检测和结构健康监测等领域具有重要意义。当导波通过管道弯曲处时,会发生散射现象,这会影响导波的传播效率和信号质量。因此,研究管道
    的头像 发表于 08-28 11:29 314次阅读
    功率放大器赋能:基于正则模态展开的<b class='flag-5'>管道</b>弯曲处导波散射研究

    管道浮力平衡压袋(配重压袋)工作原理

    管道浮力平衡压袋(配重压袋)工作原理
    发表于 06-12 15:09 0次下载

    天然气管道保压密封测试应用方案

    天然气管道在铺设完成后需进行保压密封测试,以确保管道系统无泄漏、承压能力符合设计要求。根据国家标准 GB 50251《输气管道工程设计规范》 和 GB 50369《油气长输管道工程施
    的头像 发表于 05-28 09:15 1910次阅读

    才茂燃气管道远程监控方案

    燃气管道如同城市的"血管",大多数深埋地下,默默承担着能源输送的重任。然而,人类活动、地质变化、建筑沉降、管道腐蚀等因素时刻威胁着管网的安全。随着城市燃气管网规模
    的头像 发表于 04-30 08:45 456次阅读
    才茂燃气<b class='flag-5'>管道</b>远程监控方案

    安泰功率放大器在管道螺旋导波信号测量实验中的应用

    实验名称:功率放大器在管道螺旋导波信号测量实验中的应用 实验内容:测量沿着管道壁面以螺旋方式行进的准lamb导波,研究其传播规律。 研究方向:管道导波无损检测 测试设备:
    的头像 发表于 04-15 10:57 478次阅读
    安泰功率放大器在<b class='flag-5'>管道</b>螺旋导波<b class='flag-5'>信号</b>测量实验中的应用

    焊接熔池监控相机在管道焊接中的应用:高效监测,保障管道安全

    在油气输送、化工建设等重大工程领域,都对管道有着大量的需求,管道的焊接质量直接关系到设备的安全性与稳定性。传统的焊接作业,熔池形态、温度分布等关键参数的监测高度依赖人工经验,存在实时性差、难以全程
    的头像 发表于 04-14 16:06 566次阅读
    焊接熔池监控相机在<b class='flag-5'>管道</b>焊接中的应用:高效监测,保障<b class='flag-5'>管道</b>安全

    Aigtek功率放大器在管道定位系统硬件中的应用

    实验名称: 管道定位系统的硬件实现 研究方向: 管道无损检测 实验内容: 开展基于PCM的管道定位系统的可行性研究:采用函数发生器产生4Hz、8Hz和72Hz的多频电流,并将信号输出
    的头像 发表于 03-12 11:25 424次阅读
    Aigtek功率放大器在<b class='flag-5'>管道</b>定位系统硬件中的应用

    管道流量监测设备的原理

    在工业生产、城市供水供气以及能源传输等众多领域,准确监测管道内流体的流量至关重要。流量监测不仅有助于优化生产流程、提高能源利用效率,还能保障系统的安全稳定运行。为满足这一需求,多种管道流量监测设备
    的头像 发表于 03-04 17:46 1104次阅读
    <b class='flag-5'>管道</b>流量监测设备的原理

    智能电缆管道在线监测机器人详细解析科普

    智能电缆管道在线监测机器人详细解析科普 一、概述 智能电缆管道在线监测机器人是一种集成了现代电子技术和人工智能的创新巡检设备,是深圳鼎信智慧科技专为解决传统电缆管道巡检中存在的效率低、易出错等
    的头像 发表于 01-15 09:28 809次阅读

    燃气管道安全监测自动告警系统方案

    随着高校规模的不断扩大和燃气使用量的增加,燃气安全成为高校后勤管理中的重要一环。 燃气作为一种清洁、高效的能源,为高校的教学、科研和生活提供了有力保障。然而,由于燃气管道铺设复杂、使用环境多变,以及
    的头像 发表于 01-14 16:10 1008次阅读
    燃气<b class='flag-5'>管道</b>安全监测自动告警系统方案

    Aigtek功率放大器在管道悬跨诊断中的具体应用

    问题,进而引发事故。因此,对管道悬跨进行准确、及时地诊断显得尤为重要。功率放大器作为一种信号放大设备,能够提高检测信号的强度,从而增加管道悬跨诊断的准确性和可靠性。本文将探讨功率放大器
    的头像 发表于 01-02 13:55 510次阅读
    Aigtek功率放大器在<b class='flag-5'>管道</b>悬跨诊断中的具体应用