龙空技术网

「多进程」一文看懂Linux进程通信的FIFO管道及多进程如何实现

波波说运维 1826

前言:

而今小伙伴们对“linux fifo”都比较看重,小伙伴们都想要分析一些“linux fifo”的相关资讯。那么小编在网上网罗了一些有关“linux fifo””的相关知识,希望各位老铁们能喜欢,兄弟们一起来学习一下吧!

概述

类似JAVA的多线程,Linux也可以通过FIFO来实现多线程,因为这块概念不好理解,所以我弄了个实验来帮助大家理解,这一块理解稍微有点难度,包括我的理解也是有一些可能不正确的地方,希望大家指正。

先简单介绍下概念内容。

有名管道--FIFO

进程间通信的方式有:信号通信机制; 共享存储区通信机制;共享文件通信机制;消息传递通信机制.

管道是连接读写进程的一个特殊文件,允许进程按先进先出方式传送数据,也能使进程同步执行操作。发送进程以字符流形式把大量数据送入管道,接收进程从管道中接收数据,所以叫管道通信.

管道的实质是一个共享文件,基本上可借助于文件系统的机制实现,包括(管道)文件的创建、打开、关闭和读写.进程对通信机构的使用应该互斥,一个进程正在使用某个管道写入或读出数据时,另一个进程就必须等待.发送者和接收者双方必须能够知道对方是否存在,如果对方已经不存在,就没有必要再发送信息.管道长度有限,发送信息和接收信息之间要实现正确的同步关系,当写进程把一定数量的数据写入管道,就去睡眠等待,直到读进程取走数据后,把它唤醒。管道是一种文件,可以调用read、write和close等操作文件的接口来操作管道。另一方面管道又不是一种普通的文件,它属于一种独特的文件系统:pipefs。管道的本质是内核维护了一块缓冲区与管道文件相关联,对管道文件的操作,被内核转换成对这块缓冲区内存的操作。

管道是第一个广泛应用的进程间通信手段。日常在终端执行shell命令时,会大量用到管道。但管道的缺陷在于只能在有亲缘关系(有共同的祖先)的进程之间使用。为了突破这个限制,后来引入了命名管道。

FIFO也称为有名管道,它是一种文件类型。FIFO简单理解,就是它能把两个不相关的进程联系起来,FIFO就像一个公共通道,解决了不同进程之间的“代沟”。普通的无名管道只能让相关的进程进行沟通(比如父shell和子shell之间)。

实验--FIFO通信

这里简单模拟一个FIFO通信的过程,主要有两个进程。

#!/bin/bash#Client.sh#不断向fifo写入数据 tmp_fifo="/tmp/test.fifo"rm -f $tmp_fifomkfifo $tmp_fifoexec 6<>$tmp_fifo #这句话能把管道变成非阻塞! i=0while :do	sleep 1 # 1秒写一次	echo "$i" >&6 echo "$i" #输出到终端的时候要小心,会覆盖你的终端,所以要停止,最好直接挂起进程!!	let i++done  exec 6>&-
#!/bin/bash#Server.sh#不断从fifo中读出数据 tmp_fifo="/tmp/test.fifo"echo "$tmp_fifo"exec 6<>$tmp_fifo #建立一个绑定 while :do read TEXT	sleep 1	echo "$TEXT" #每1s就读取一个数据,并且打印到终端,要停止,最好挂起进程!done <&6

开启两个终端,分别执行两个脚本,就能看到在终端B中从fifo中读取到的数据,如果在终端B中把每次读fifo的间隔给改成2秒,就更能说明问题了。

上面的程序,在实验的时候最好通过不断挂起A进程来查看,然后观察B进程的输出。你会知道FIFO整个是怎么跑的。。。

下面简单分了4种情况,也是通过实验来测试,难度会稍微大一点。

实验测试--不同写法对性能的影响1、多进程情况1:

#!/bin/bashtemp_fifo_file=$$.info #以当前进程号,为临时管道取名mkfifo $temp_fifo_file #创建临时管道exec 6<>$temp_fifo_file #创建标识为6,可以对管道进行读写rm $temp_fifo_file #清空管道内容 function f_sleep { sleep 2}  temp_thread=2 #进程数#for循环:往fifo管道文件中写入2个空行(这个必须大于或等于temp_thread,要不进程写哪里?) for ((i=0;i<temp_thread;i++)) #为进程创建相应的占位do echo #每个echo输出一个回车,为每个进程创建一个占位done >&6 #将占位信息写入标识为6的管道#{}这部分语句被放入后台作为一个子进程执行,所以不必每次等待5秒后执行下一个,{}这部分的echo $$,$i,`date`几乎是同时完成的,当fifo中2个空行#读完后for循环继续等待read中读取fifo数据,当后台的2个子进程等待5秒后,按次序排队往fifo输入空行,这样fifo又有数据,for语句继续执行for ((i=0;i<6;i++)) do read -u6 #获取标识为6的占位,从文件描述符6中读取行(实际指向fifo管道)  { f_sleep echo $$,$i,`date +%H:%M:%S` sleep 5  echo $$,$i,`date` echo >&6 #>>>>>当任务执行完后,会释放管道占位,所以补充一个占位,其实是再次往fifo管道文件中写入一个空行 }& done <&6 #将标识为6的管道作为标准输入 wait #等待所有任务完成exec 6>&- #关闭标识为6的管道

注意观察每个日期之间的时间间隔。

简单理解:同时开两个进程,用了两个循环在后台跑,过了2s先跑了date +%H:%M:%S,然后过了5s后两个进程跑了date,然后重新从fifo管道读取两个空行,继续for循环。

2、多进程情况2:

#!/bin/bashtemp_fifo_file=$$.info #以当前进程号,为临时管道取名mkfifo $temp_fifo_file #创建临时管道exec 6<>$temp_fifo_file #创建标识为6,可以对管道进行读写rm $temp_fifo_file #清空管道内容 function f_sleep { sleep 2}  temp_thread=2 #进程数 for ((i=0;i<temp_thread;i++)) #为进程创建相应的占位do echo #每个echo输出一个回车,为每个进程创建一个占位done >&6 #将占位信息写入标识为6的管道 for ((i=0;i<6;i++)) do read #获取标识为6的占位 { f_sleep echo $$,$i,`date +%H:%M:%S` }& #>>>>>在后台执行{}中的任务 sleep 5 echo $$,$i,`date` echo >&6 #>>>>>当任务执行完后,会释放管道占位,所以补充一个占位done <&6 #将标识为6的管道作为标准输入 wait #等待所有任务完成exec 6>&- #关闭标识为6的管道

这里观察我红色标记的部分,基本就是改变的地方了。

合理要观察每个日期之间间隔的时间,然后来分析。

简单理解:开了2个进程,1个在后台执行,1个在前台执行,(过了2s先跑了date +%H:%M:%S)这个在后台跑,(过了5s后两个进程跑了date)这个在前台跑,一次总共是5s循环,总共跑6次。

3、多进程情况3:

#!/bin/bashtemp_fifo_file=$$.info #以当前进程号,为临时管道取名mkfifo $temp_fifo_file #创建临时管道exec 6<>$temp_fifo_file #创建标识为6,可以对管道进行读写rm $temp_fifo_file #清空管道内容 function f_sleep { sleep 2 echo >&6 #>>>>>当任务执行完后,会释放管道占位,所以补充一个占位}  temp_thread=2 #进程数 for ((i=0;i<temp_thread;i++)) #为进程创建相应的占位do echo #每个echo输出一个回车,为每个进程创建一个占位done >&6 #将占位信息写入标识为6的管道 for ((i=0;i<6;i++)) do read #获取标识为6的占位 { f_sleep echo $$,$i,`date +%H:%M:%S` }& #>>>>>在后台执行{}中的任务 sleep 5 echo $$,$i,`date`done <&6 #将标识为6的管道作为标准输入 wait #等待所有任务完成exec 6>&- #关闭标识为6的管道

注意观察红色框部分。

结果输出:

简单理解:开了2个进程,1个进程在后台执行,其中后台进程在管道有2个占位,但实际只用了1个,(过了2s先跑了date +%H:%M:%S)这个在后台跑,(过了5s后两个进程跑了date)这个在前台跑,一次5s循环,总共跑6次。

4、多进程情况4:

#!/bin/bashtemp_fifo_file=$$.info #以当前进程号,为临时管道取名mkfifo $temp_fifo_file #创建临时管道exec 6<>$temp_fifo_file #创建标识为6,可以对管道进行读写rm $temp_fifo_file #清空管道内容 function f_sleep { sleep 2 echo >&6 #>>>>>当任务执行完后,会释放管道占位,所以补充一个占位}  temp_thread=2 #进程数 for ((i=0;i<temp_thread;i++)) #为进程创建相应的占位do echo #每个echo输出一个回车,为每个进程创建一个占位done >&6 #将占位信息写入标识为6的管道 for ((i=0;i<6;i++)) do read #获取标识为6的占位 { f_sleep echo $$,$i,`date +%H:%M:%S` sleep 5 echo $$,$i,`date` }& #>>>>>在后台执行{}中的任务done <&6 #将标识为6的管道作为标准输入 wait #等待所有任务完成exec 6>&- #关闭标识为6的管道

简单理解:两个后台进程在跑,echo >&6在管道增加一个占位,现在变成3个占位,

从管道读第1个占位,先用2个后台子进程跑循环1(过了2s先跑了循环1的date +%H:%M:%S,从循环1一开始过了5s后循环1就跑到date了),然后从管道读第2个占位,再用2个后台子进程跑循环2,最后读第3个占位,用2个后台子进程跑循环3,因为是fifo(先进先出),所以每次读取占位都需要算上f_sleep的时间2s。

总结

FIFO的出现,极好地解决了系统在应用过程中产生的大量的中间临时文件的问题。FIFO可以被shell调用使数据从一个进程到另一个进程,系统不必为该中间通道去烦恼清理不必要的垃圾,或者去释放该通道的资源,它可以被留做后来的进程使用。并且规避了匿名管道在作用域的限制,可应用于不相关的进程之间。

上面的理解应该是有些不对的地方,希望大家指正,下方留言一起讨论~

后面会分享更多devops和DBA方面的内容,感兴趣的朋友可以关注下~

标签: #linux fifo