Linux 实现 IPC 其中的一种方式——管道
创新互联建站网站建设公司一直秉承“诚信做人,踏实做事”的原则,不欺瞒客户,是我们最起码的底线! 以服务为基础,以质量求生存,以技术求发展,成交一个客户多一个朋友!专注中小微企业官网定制,成都做网站、网站建设、外贸营销网站建设,塑造企业网络形象打造互联网企业效应。
管道又分:
1、无名管道:无名管道只能用于有亲缘关系的进程。
2、有名管道:有名管道用于任意两进程间通信。
你就可以把管道理解成位于进程内核空间的“文件”。
给文件加引号,是因为它和文件确实很像,因为它也有描述符。但是它确实又不是普通的本地文件,而是一种抽象的存在。
当进程使用 pipe 函数,就可以打开位于内核中的这个特殊“文件”。同时 pipe 函数会返回两个描述符,一个用于读,一个用于写。如果你使用 fstat函数来测试该描述符,可以发现此文件类型为 FIFO。
而无名管道的无名,指的就是这个虚幻的“文件”,它没有名字。本质上,pipe 函数会在进程内核空间申请一块内存(比如一个内存页,一般是 4KB),然后把这块内存当成一个先进先出(FIFO)的循环队列来存取数据,这一切都由操作系统帮助我们实现了。
pipe 函数打开的文件描述符是通过参数(数组)传递出来的,而返回值表示打开成功(0)或失败(-1)。
它的参数是一个大小为 2 的数组。此数组的第 0 个元素用来接收以读的方式打开的描述符,而第 1 个元素用来接收以写的方式打开的描述符。也就是说,pipefd[0] 是用于读的,而 pipefd[1] 是用于写的。
打开了文件描述符后,就可以使用 read(pipefd[0]) 和 write(pipefd[1]) 来读写数据了。
注意事项
1、这两个分别用于读写的描述符必须同时打开才行,否则会出问题。
2、如果关闭读 (close(pipefd[0])) 端保留写端,继续向写端 (pipefd[1]) 端写数据(write 函数)的进程会收到 SIGPIPE 信号。
3、如果关闭写 (close(pipefd[1])) 端保留读端,继续向读端 (pipefd[0]) 端读数据(read 函数),read 函数会返回 0。
当在进程用 pipe 函数打开两个描述符后,我们可以 fork 出一个子进程。这样,子进程也会继承这两个描述符,而且这两个文件描述符的引用计数会变成 2。
如果你需要父进程向子进程发送数据,那么得把父进程的 pipefd[0] (读端)关闭,而在子进程中把 pipefd[1] 写端关闭,反之亦然。为什么要这样做?实际上是避免出错。传统上 pipe 管道只能用于半双工通信(即一端只能发,不能收;而另一端只能收不能发),为了安全起见,各个进程需要把不用的那一端关闭(本质上是引用计数减 1)。
步骤一:fork 子进程
步骤二:关闭父进程读端,关闭子进程写端
父进程 fork 出一个子进程,通过无名管道向子进程发送字符,子进程收到数据后将字符串中的小写字符转换成大写并输出。
有名管道打破了无名管道的限制,进化出了一个实实在在的 FIFO 类型的文件。这意味着即使没有亲缘关系的进程也可以互相通信了。所以,只要不同的进程打开 FIFO 文件,往此文件读写数据,就可以达到通信的目的。
1、文件属性前面标注的文件类型是 p
2、代表管道文件大小是 0
3、fifo 文件需要有读写两端,否则在打开 fifo 文件时会阻塞
通过命令 mkfifo 创建
通过函数 mkfifo创建
函数返回 0 表示成功,-1 失败。
例如:
cat 命令打印 test文件内容
接下来你的 cat 命令被阻塞住。
开启另一个终端,执行:
然后你会看到被阻塞的 cat 又继续执行完毕,在屏幕打印 “hello world”。如果你反过来执行上面两个命令,会发现先执行的那个总是被阻塞。
有两个程序,分别是发送端 send 和接收端面 recv。程序 send 从标准输入接收字符,并发送到程序 recv,同时 recv 将接收到的字符打印到屏幕。
发送端
接收端
编译
运行
因为 recv 端还没打开test文件,这时候 send 是阻塞状态的。
再开启另一个终端:
这时候 send 端和 recv 端都在终端显示has opend fifo
此时在 send 端输入数据,recv 打印。
管道命令符 " | " 的作用是将前一个命令的标准输出当作后一个命令的标准输入,格式为"命令A|命令B"。
例如:
搜索rsyslogd进程: $ ps -ef | grep rsyslogd
用反野形式查看/etc 目录中有哪些文件: $ ls -l /etc/ | more
bash解释器支持多种文本通配符,包括:
我们之前在学文本查询搜索命令时就用到了通配符,例如,在 /etc 目录下搜索所有的以host开头的文件:
$ find /etc -name "host*"
另外,bash解释器还支持很多的特殊字符扩展:
例如:
先定义名称为PRICE的变量值为5
$ PRICE=5
输出“价格是5”:
输出“价格是$5”,但因为美元符号与代表变量取值的$符号冲突,所以需要用到转义字符:
使用单引号,变量将不再被取值:
执行 uname -a 后可以查看到本机内核的版本与架构信息(反引号里面的命令会被执行):
linux中管道符的使用非常普遍
管道符的用法是将第一个命令的结果传递给第二个命令作为输入
管道符的使用有两个条件:
格式: 命令1 | 命令2
常见应用举例:netstat -an | grep ESTABLISHED | wc -l #查看服务器上连接了多少客户端
wc -l 是查看输入了多少行
利用Linux所提供的管道符“|”将两个命令隔开,管道符左边命令的输出就会作为管道符右边命令的输入。 连续使用管道意味着第一个命令的输出会作为 第二个命令的输入,第二个命令的输出又会作为第三个命令的输入,依此类推。下面来看看管道是如何在构造一条Linux命令中得到应用的。
注意:
1、管道命令只处理前一个命令正确输出,不处理错误输出。
2、管道命令右边命令,必须能够接收标准输入流命令才行。
这条命令使用一个管道符“|”建立了一个管道。管道将rpm -qa命令的输出(包括系统中所有安装的RPM包)作为grep命令的输入,从而列出带有licq字符的RPM包来。
这条命令使用了两个管道,利用第一个管道将cat命令(显示passwd文件的内容)的输出送给grep命令,grep命令找出含有“/bin /bash”的所有行;第二个管道将grep的输出送给wc命令,wc命令统计出输入中的行数。这个命令的功能在于找出系统中有多少个用户使用bash。
区别是:
1、左边的命令应该有标准输出 | 右边的命令应该接受标准输入
左边的命令应该有标准输出 右边只能是文件
左边的命令应该需要标准输入 右边只能是文件
2、管道触发两个子进程执行"|"两边的程序;而重定向是在一个进程内执行
这些都是网上总结很多的,其实只要多加清楚用法,也一定有自己的一份不同描述。