Linux通过匿名管道进行进程间通信
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><span>一、什么是管道</span>
</li><li>
<span>二、使用popen函数</span>
<ul class="second_class_ul"><li>
1、popen函数和pclose函数介绍</li><li>
2、例子</li><li>
3、popen的实现方式及优缺点</li></ul></li><li>
<span>三、pipe调用</span>
<ul class="second_class_ul"><li>
特别提醒:</li></ul></li><li>
<span>四、把管道用作标准输入和标准输出</span>
<ul class="second_class_ul"><li>
例子</li></ul></li><li>
<span>五、关于管道关闭后的读操作的讨论</span>
<ul class="second_class_ul"></ul></li><li>
<span>六、匿名管道的缺陷</span>
<ul class="second_class_ul"></ul></li><li>
<span>总结</span>
<ul class="second_class_ul"></ul></li></ul></div><p>
本文研究的主要是linux通过匿名管道进行进程间通信的相关内容,具体介绍如下。</p>
<p>
在前面,介绍了一种进程间的通信方式:使用信号,我们创建通知事件,并通过它引起响应,但传递的信息只是一个信号值。这里将介绍另一种进程间通信的方式——匿名管道,通过它进程间可以交换更多有用的数据。</p>
<p class="maodian"></p><h2>
<span>一、什么是管道</span>
</h2>
<p>
如果你使用过linux的命令,那么对于管道这个名词你一定不会感觉到陌生,因为我们通常通过符号“|"来使用管道,但是管理的真正定义是什么呢?管道是一个进程连接数据流到另一个进程的通道,它通常是用作把一个进程的输出通过管道连接到另一个进程的输入。</p>
<p>
举个例子,在shell中输入命令:ls -l | grep string,我们知道ls命令(其实也是一个进程)会把当前目录中的文件都列出来,但是它不会直接输出,而是把本来要输出到屏幕上的数据通过管道输出到grep这个进程中,作为grep这个进程的输入,然后这个进程对输入的信息进行筛选,把存在string的信息的字符串(以行为单位)打印在屏幕上。</p>
<p class="maodian"></p><h2>
<span>二、使用popen函数</span>
</h2>
<p class="maodian"></p><h3>
1、popen函数和pclose函数介绍</h3>
<p>
有静就有动,有开就有关,与此相同,与popen函数相对应的函数是pclose函数,它们的原型如下:</p>
<div class="jb51code">
<div>
<div class="syntaxhighlightercpp" id="highlighter_703908">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="cpp preprocessor">#include <stdio.h> </code>
</div>
<div class="line number2 index1 alt1">
<code class="cpp plain">file* popen (</code><code class="cpp keyword bold">const</code> <code class="cpp color1 bold">char</code> <code class="cpp plain">*command, </code><code class="cpp keyword bold">const</code> <code class="cpp color1 bold">char</code> <code class="cpp plain">*open_mode); </code>
</div>
<div class="line number3 index2 alt2">
<code class="cpp color1 bold">int</code> <code class="cpp plain">pclose(file *stream_to_close);</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
</div>
<p>
poen函数允许一个程序将另一个程序作为新进程来启动,并可以传递数据给它或者通过它接收数据。command是要运行的程序名和相应的参数。open_mode只能是"r(只读)"和"w(只写)"的其中之一。注意,popen函数的返回值是一个file类型的指针,而linux把一切都视为文件,也就是说我们可以使用stdio i/o库中的文件处理函数来对其进行操作。</p>
<p>
如果open_mode是"r",主调用程序就可以使用被调用程序的输出,通过函数返回的file指针,就可以能过stdio函数(如fread)来读取程序的输出;如果open_mode是"w",主调用程序就可以向被调用程序发送数据,即通过stdio函数(如fwrite)向被调用程序写数据,而被调用程序就可以在自己的标准输入中读取这些数据。</p>
<p>
pclose函数用于关闭由popen创建出的关联文件流。pclose只在popen启动的进程结束后才返回,如果调用pclose时被调用进程仍在运行,pclose调用将等待该进程结束。它返回关闭的文件流所在进程的退出码。</p>
<p class="maodian"></p><h3>
2、例子</h3>
<p>
很多时候,我们根本就不知道输出数据的长度,为了避免定义一个非常大的数组作为缓冲区,我们可以以块的方式来发送数据,一次读取一个块的数据并发送一个块的数据,直到把所有的数据都发送完。下面的例子就是采用这种方式的数据读取和发送方式。源文件名为popen.c,代码如下:</p>
<div class="jb51code">
<div>
<div class="syntaxhighlightercpp" id="highlighter_552828">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
<div class="line number8 index7 alt1">
8</div>
<div class="line number9 index8 alt2">
9</div>
<div class="line number10 index9 alt1">
10</div>
<div class="line number11 index10 alt2">
11</div>
<div class="line number12 index11 alt1">
12</div>
<div class="line number13 index12 alt2">
13</div>
<div class="line number14 index13 alt1">
14</div>
<div class="line number15 index14 alt2">
15</div>
<div class="line number16 index15 alt1">
16</div>
<div class="line number17 index16 alt2">
17</div>
<div class="line number18 index17 alt1">
18</div>
<div class="line number19 index18 alt2">
19</div>
<div class="line number20 index19 alt1">
20</div>
<div class="line number21 index20 alt2">
21</div>
<div class="line number22 index21 alt1">
22</div>
<div class="line number23 index22 alt2">
23</div>
<div class="line number24 index23 alt1">
24</div>
<div class="line number25 index24 alt2">
25</div>
<div class="line number26 index25 alt1">
26</div>
<div class="line number27 index26 alt2">
27</div>
<div class="line number28 index27 alt1">
28</div>
<div class="line number29 index28 alt2">
29</div>
<div class="line number30 index29 alt1">
30</div>
<div class="line number31 index30 alt2">
31</div>
<div class="line number32 index31 alt1">
32</div>
<div class="line number33 index32 alt2">
33</div>
<div class="line number34 index33 alt1">
34</div>
<div class="line number35 index34 alt2">
35</div>
<div class="line number36 index35 alt1">
36</div>
<div class="line number37 index36 alt2">
37</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="cpp preprocessor">#include <unistd.h> </code>
</div>
<div class="line number2 index1 alt1">
<code class="cpp preprocessor">#include <stdlib.h> </code>
</div>
<div class="line number3 index2 alt2">
<code class="cpp preprocessor">#include <stdio.h> </code>
</div>
<div class="line number4 index3 alt1">
<code class="cpp preprocessor">#include <string.h> </code>
</div>
<div class="line number5 index4 alt2">
<code class="cpp spaces"> </code>
</div>
<div class="line number6 index5 alt1">
<code class="cpp color1 bold">int</code> <code class="cpp plain">main() </code>
</div>
<div class="line number7 index6 alt2">
<code class="cpp plain">{ </code>
</div>
<div class="line number8 index7 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">file *read_fp = null; </code>
</div>
<div class="line number9 index8 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">file *write_fp = null; </code>
</div>
<div class="line number10 index9 alt1">
<code class="cpp spaces"> </code><code class="cpp color1 bold">char</code> <code class="cpp plain">buffer; </code>
</div>
<div class="line number11 index10 alt2">
<code class="cpp spaces"> </code><code class="cpp color1 bold">int</code> <code class="cpp plain">chars_read = 0; </code>
</div>
<div class="line number12 index11 alt1">
<code class="cpp spaces"> </code>
</div>
<div class="line number13 index12 alt2">
<code class="cpp spaces"> </code><code class="cpp comments">//初始化缓冲区 </code>
</div>
<div class="line number14 index13 alt1">
<code class="cpp spaces"> </code><code class="cpp functions bold">memset</code><code class="cpp plain">(buffer, </code><code class="cpp string">'\0'</code><code class="cpp plain">, </code><code class="cpp keyword bold">sizeof</code><code class="cpp plain">(buffer)); </code>
</div>
<div class="line number15 index14 alt2">
<code class="cpp spaces"> </code><code class="cpp comments">//打开ls和grep进程 </code>
</div>
<div class="line number16 index15 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">read_fp = popen(</code><code class="cpp string">"ls -l"</code><code class="cpp plain">, </code><code class="cpp string">"r"</code><code class="cpp plain">); </code>
</div>
<div class="line number17 index16 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">write_fp = popen(</code><code class="cpp string">"grep rwxrwxr-x"</code><code class="cpp plain">, </code><code class="cpp string">"w"</code><code class="cpp plain">); </code>
</div>
<div class="line number18 index17 alt1">
<code class="cpp spaces"> </code><code class="cpp comments">//两个进程都打开成功 </code>
</div>
<div class="line number19 index18 alt2">
<code class="cpp spaces"> </code><code class="cpp keyword bold">if</code><code class="cpp plain">(read_fp && write_fp) </code>
</div>
<div class="line number20 index19 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">{ </code>
</div>
<div class="line number21 index20 alt2">
<code class="cpp spaces"> </code><code class="cpp comments">//读取一个数据块 </code>
</div>
<div class="line number22 index21 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">chars_read = </code><code class="cpp functions bold">fread</code><code class="cpp plain">(buffer, </code><code class="cpp keyword bold">sizeof</code><code class="cpp plain">(</code><code class="cpp color1 bold">char</code><code class="cpp plain">), bufsiz, read_fp); </code>
</div>
<div class="line number23 index22 alt2">
<code class="cpp spaces"> </code><code class="cpp keyword bold">while</code><code class="cpp plain">(chars_read > 0) </code>
</div>
<div class="line number24 index23 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">{ </code>
</div>
<div class="line number25 index24 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">buffer = </code><code class="cpp string">'\0'</code><code class="cpp plain">; </code>
</div>
<div class="line number26 index25 alt1">
<code class="cpp spaces"> </code><code class="cpp comments">//把数据写入grep进程 </code>
</div>
<div class="line number27 index26 alt2">
<code class="cpp spaces"> </code><code class="cpp functions bold">fwrite</code><code class="cpp plain">(buffer, </code><code class="cpp keyword bold">sizeof</code><code class="cpp plain">(</code><code class="cpp color1 bold">char</code><code class="cpp plain">), chars_read, write_fp); </code>
</div>
<div class="line number28 index27 alt1">
<code class="cpp spaces"> </code><code class="cpp comments">//还有数据可读,循环读取数据,直到读完所有数据 </code>
</div>
<div class="line number29 index28 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">chars_read = </code><code class="cpp functions bold">fread</code><code class="cpp plain">(buffer, </code><code class="cpp keyword bold">sizeof</code><code class="cpp plain">(</code><code class="cpp color1 bold">char</code><code class="cpp plain">), bufsiz, read_fp); </code>
</div>
<div class="line number30 index29 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">} </code>
</div>
<div class="line number31 index30 alt2">
<code class="cpp spaces"> </code><code class="cpp comments">//关闭文件流 </code>
</div>
<div class="line number32 index31 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">pclose(read_fp); </code>
</div>
<div class="line number33 index32 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">pclose(write_fp); </code>
</div>
<div class="line number34 index33 alt1">
<code class="cpp spaces"> </code><code class="cpp functions bold">exit</code><code class="cpp plain">(exit_success); </code>
</div>
<div class="line number35 index34 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">} </code>
</div>
<div class="line number36 index35 alt1">
<code class="cpp spaces"> </code><code class="cpp functions bold">exit</code><code class="cpp plain">(exit_failure); </code>
</div>
<div class="line number37 index36 alt2">
<code class="cpp plain">}</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
</div>
<p>
运行结果如下:</p>
<p>
<img title="Linux通过匿名管道进行进程间通信" alt="Linux通过匿名管道进行进程间通信" id="theimg" src="https://zhuji.jb51.net/uploads/img/202305/cb72703a4063038c3842042d3b7c0ce5.jpg"></p>
<p>
从运行结果来看,达到了信息筛选的目的。程序在进程ls中读取数据,再把数据发送到进程grep中进行筛选处理,相当于在shell中直接输入命令:ls -l | grep rwxrwxr-x。</p>
<p class="maodian"></p><h3>
3、popen的实现方式及优缺点</h3>
<p>
当请求popen调用运行一个程序时,它首先启动shell,即系统中的sh命令,然后将command字符串作为一个参数传递给它。</p>
<p>
这样就带来了一个优点和一个缺点。优点是:在linux中所有的参数扩展都是由shell来完成的。所以在启动程序(command中的命令程序)之前先启动shell来分析命令字符串,也就可以使各种shell扩展(如通配符)在程序启动之前就全部完成,这样我们就可以通过popen启动非常复杂的shell命令。</p>
<p>
而它的缺点就是:对于每个popen调用,不仅要启动一个被请求的程序,还要启动一个shell,即每一个popen调用将启动两个进程,从效率和资源的角度看,popen函数的调用比正常方式要慢一些。</p>
<p class="maodian"></p><h2>
<span>三、pipe调用</span>
</h2>
<p>
如果说popen是一个高级的函数,pipe则是一个底层的调用。与popen函数不同的是,它在两个进程之间传递数据不需要启动一个shell来解释请求命令,同时它还提供对读写数据的更多的控制。</p>
<p>
pipe函数的原型如下:</p>
<div class="jb51code">
<div>
<div class="syntaxhighlightercpp" id="highlighter_278096">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="cpp preprocessor">#include <unistd.h> </code>
</div>
<div class="line number2 index1 alt1">
<code class="cpp color1 bold">int</code> <code class="cpp plain">pipe(</code><code class="cpp color1 bold">int</code> <code class="cpp plain">file_descriptor);</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
</div>
<p>
我们可以看到pipe函数的定义非常特别,该函数在数组中墙上两个新的文件描述符后返回0,如果返回返回-1,并设置errno来说明失败原因。</p>
<p>
数组中的两个文件描述符以一种特殊的方式连接起来,数据基于先进先出的原则,写到file_descriptor的所有数据都可以从file_descriptor读回来。由于数据基于先进先出的原则,所以读取的数据和写入的数据是一致的。</p>
<p class="maodian"></p><h3>
特别提醒:</h3>
<p>
1、从函数的原型我们可以看到,它跟popen函数的一个重大区别是,popen函数是基于文件流(file)工作的,而pipe是基于文件描述符工作的,所以在使用pipe后,数据必须要用底层的read和write调用来读取和发送。</p>
<p>
2、不要用file_descriptor写数据,也不要用file_descriptor读数据,其行为未定义的,但在有些系统上可能会返回-1表示调用失败。数据只能从file_descriptor中读取,数据也只能写入到file_descriptor,不能倒过来。</p>
<p>
例子:</p>
<p>
首先,我们在原先的进程中创建一个管道,然后再调用fork创建一个新的进程,最后通过管道在两个进程之间传递数据。源文件名为pipe.c,代码如下:</p>
<div class="jb51code">
<div>
<div class="syntaxhighlightercpp" id="highlighter_884629">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
<div class="line number8 index7 alt1">
8</div>
<div class="line number9 index8 alt2">
9</div>
<div class="line number10 index9 alt1">
10</div>
<div class="line number11 index10 alt2">
11</div>
<div class="line number12 index11 alt1">
12</div>
<div class="line number13 index12 alt2">
13</div>
<div class="line number14 index13 alt1">
14</div>
<div class="line number15 index14 alt2">
15</div>
<div class="line number16 index15 alt1">
16</div>
<div class="line number17 index16 alt2">
17</div>
<div class="line number18 index17 alt1">
18</div>
<div class="line number19 index18 alt2">
19</div>
<div class="line number20 index19 alt1">
20</div>
<div class="line number21 index20 alt2">
21</div>
<div class="line number22 index21 alt1">
22</div>
<div class="line number23 index22 alt2">
23</div>
<div class="line number24 index23 alt1">
24</div>
<div class="line number25 index24 alt2">
25</div>
<div class="line number26 index25 alt1">
26</div>
<div class="line number27 index26 alt2">
27</div>
<div class="line number28 index27 alt1">
28</div>
<div class="line number29 index28 alt2">
29</div>
<div class="line number30 index29 alt1">
30</div>
<div class="line number31 index30 alt2">
31</div>
<div class="line number32 index31 alt1">
32</div>
<div class="line number33 index32 alt2">
33</div>
<div class="line number34 index33 alt1">
34</div>
<div class="line number35 index34 alt2">
35</div>
<div class="line number36 index35 alt1">
36</div>
<div class="line number37 index36 alt2">
37</div>
<div class="line number38 index37 alt1">
38</div>
<div class="line number39 index38 alt2">
39</div>
<div class="line number40 index39 alt1">
40</div>
<div class="line number41 index40 alt2">
41</div>
<div class="line number42 index41 alt1">
42</div>
<div class="line number43 index42 alt2">
43</div>
<div class="line number44 index43 alt1">
44</div>
<div class="line number45 index44 alt2">
45</div>
<div class="line number46 index45 alt1">
46</div>
<div class="line number47 index46 alt2">
47</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="cpp preprocessor">#include <unistd.h> </code>
</div>
<div class="line number2 index1 alt1">
<code class="cpp preprocessor">#include <stdlib.h> </code>
</div>
<div class="line number3 index2 alt2">
<code class="cpp preprocessor">#include <stdio.h> </code>
</div>
<div class="line number4 index3 alt1">
<code class="cpp preprocessor">#include <string.h> </code>
</div>
<div class="line number5 index4 alt2">
<code class="cpp spaces"> </code>
</div>
<div class="line number6 index5 alt1">
<code class="cpp color1 bold">int</code> <code class="cpp plain">main() </code>
</div>
<div class="line number7 index6 alt2">
<code class="cpp plain">{ </code>
</div>
<div class="line number8 index7 alt1">
<code class="cpp spaces"> </code><code class="cpp color1 bold">int</code> <code class="cpp plain">data_processed = 0; </code>
</div>
<div class="line number9 index8 alt2">
<code class="cpp spaces"> </code><code class="cpp color1 bold">int</code> <code class="cpp plain">filedes; </code>
</div>
<div class="line number10 index9 alt1">
<code class="cpp spaces"> </code><code class="cpp keyword bold">const</code> <code class="cpp color1 bold">char</code> <code class="cpp plain">data[] = </code><code class="cpp string">"hello pipe!"</code><code class="cpp plain">; </code>
</div>
<div class="line number11 index10 alt2">
<code class="cpp spaces"> </code><code class="cpp color1 bold">char</code> <code class="cpp plain">buffer; </code>
</div>
<div class="line number12 index11 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">pid_t pid; </code>
</div>
<div class="line number13 index12 alt2">
<code class="cpp spaces"> </code><code class="cpp comments">//清空缓冲区 </code>
</div>
<div class="line number14 index13 alt1">
<code class="cpp spaces"> </code><code class="cpp functions bold">memset</code><code class="cpp plain">(buffer, </code><code class="cpp string">'\0'</code><code class="cpp plain">, </code><code class="cpp keyword bold">sizeof</code><code class="cpp plain">(buffer)); </code>
</div>
<div class="line number15 index14 alt2">
<code class="cpp spaces"> </code>
</div>
<div class="line number16 index15 alt1">
<code class="cpp spaces"> </code><code class="cpp keyword bold">if</code><code class="cpp plain">(pipe(filedes) == 0) </code>
</div>
<div class="line number17 index16 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">{ </code>
</div>
<div class="line number18 index17 alt1">
<code class="cpp spaces"> </code><code class="cpp comments">//创建管道成功 </code>
</div>
<div class="line number19 index18 alt2">
<code class="cpp spaces"> </code><code class="cpp comments">//通过调用fork创建子进程 </code>
</div>
<div class="line number20 index19 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">pid = fork(); </code>
</div>
<div class="line number21 index20 alt2">
<code class="cpp spaces"> </code><code class="cpp keyword bold">if</code><code class="cpp plain">(pid == -1) </code>
</div>
<div class="line number22 index21 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">{ </code>
</div>
<div class="line number23 index22 alt2">
<code class="cpp spaces"> </code><code class="cpp functions bold">fprintf</code><code class="cpp plain">(stderr, </code><code class="cpp string">"fork failure"</code><code class="cpp plain">); </code>
</div>
<div class="line number24 index23 alt1">
<code class="cpp spaces"> </code><code class="cpp functions bold">exit</code><code class="cpp plain">(exit_failure); </code>
</div>
<div class="line number25 index24 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">} </code>
</div>
<div class="line number26 index25 alt1">
<code class="cpp spaces"> </code><code class="cpp keyword bold">if</code><code class="cpp plain">(pid == 0) </code>
</div>
<div class="line number27 index26 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">{ </code>
</div>
<div class="line number28 index27 alt1">
<code class="cpp spaces"> </code><code class="cpp comments">//子进程中 </code>
</div>
<div class="line number29 index28 alt2">
<code class="cpp spaces"> </code><code class="cpp comments">//读取数据 </code>
</div>
<div class="line number30 index29 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">data_processed = read(filedes, buffer, bufsiz); </code>
</div>
<div class="line number31 index30 alt2">
<code class="cpp spaces"> </code><code class="cpp functions bold">printf</code><code class="cpp plain">(</code><code class="cpp string">"read %d bytes: %s\n"</code><code class="cpp plain">, data_processed, buffer); </code>
</div>
<div class="line number32 index31 alt1">
<code class="cpp spaces"> </code><code class="cpp functions bold">exit</code><code class="cpp plain">(exit_success); </code>
</div>
<div class="line number33 index32 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">} </code>
</div>
<div class="line number34 index33 alt1">
<code class="cpp spaces"> </code><code class="cpp keyword bold">else</code>
</div>
<div class="line number35 index34 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">{ </code>
</div>
<div class="line number36 index35 alt1">
<code class="cpp spaces"> </code><code class="cpp comments">//父进程中 </code>
</div>
<div class="line number37 index36 alt2">
<code class="cpp spaces"> </code><code class="cpp comments">//写数据 </code>
</div>
<div class="line number38 index37 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">data_processed = write(filedes, data, </code><code class="cpp functions bold">strlen</code><code class="cpp plain">(data)); </code>
</div>
<div class="line number39 index38 alt2">
<code class="cpp spaces"> </code><code class="cpp functions bold">printf</code><code class="cpp plain">(</code><code class="cpp string">"wrote %d bytes: %s\n"</code><code class="cpp plain">, data_processed, data); </code>
</div>
<div class="line number40 index39 alt1">
<code class="cpp spaces"> </code><code class="cpp comments">//休眠2秒,主要是为了等子进程先结束,这样做也只是纯粹为了输出好看而已 </code>
</div>
<div class="line number41 index40 alt2">
<code class="cpp spaces"> </code><code class="cpp comments">//父进程其实没有必要等等子进程结束 </code>
</div>
<div class="line number42 index41 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">sleep(2); </code>
</div>
<div class="line number43 index42 alt2">
<code class="cpp spaces"> </code><code class="cpp functions bold">exit</code><code class="cpp plain">(exit_success); </code>
</div>
<div class="line number44 index43 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">} </code>
</div>
<div class="line number45 index44 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">} </code>
</div>
<div class="line number46 index45 alt1">
<code class="cpp spaces"> </code><code class="cpp functions bold">exit</code><code class="cpp plain">(exit_failure); </code>
</div>
<div class="line number47 index46 alt2">
<code class="cpp plain">}</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
</div>
<p>
运行结果为:</p>
<p>
<img title="Linux通过匿名管道进行进程间通信" alt="Linux通过匿名管道进行进程间通信" id="theimg" src="https://zhuji.jb51.net/uploads/img/202305/a4472ffd815c4769d04b73c7b8c433de.jpg"></p>
<p>
可见,子进程读取了父进程写到filedes中的数据,如果在父进程中没有sleep语句,父进程可能在子进程结束前结束,这样你可能将看到两个输入之间有一个命令提示符分隔。</p>
<p class="maodian"></p><h2>
<span>四、把管道用作标准输入和标准输出</span>
</h2>
<p>
下面来介绍一种用管道来连接两个进程的更简洁方法,我们可以把文件描述符设置为一个已知值,一般是标准输入0或标准输出1。这样做最大的好处是可以调用标准程序,即那些不需要以文件描述符为参数的程序。</p>
<p>
为了完成这个工作,我们还需要两个函数的辅助,它们分别是dup函数或dup2函数,它们的原型如下</p>
<div class="jb51code">
<div>
<div class="syntaxhighlightercpp" id="highlighter_111136">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="cpp preprocessor">#include <unistd.h> </code>
</div>
<div class="line number2 index1 alt1">
<code class="cpp color1 bold">int</code> <code class="cpp plain">dup(</code><code class="cpp color1 bold">int</code> <code class="cpp plain">file_descriptor); </code>
</div>
<div class="line number3 index2 alt2">
<code class="cpp color1 bold">int</code> <code class="cpp plain">dup2(</code><code class="cpp color1 bold">int</code> <code class="cpp plain">file_descriptor_one, </code><code class="cpp color1 bold">int</code> <code class="cpp plain">file_descriptor_two);</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
</div>
<p>
dup调用创建一个新的文件描述符与作为它的参数的那个已有文件描述符指向同一个文件或管道。对于dup函数而言,新的文件描述总是取最小的可用值。而dup2所创建的新文件描述符或者与int file_descriptor_two相同,或者是第一个大于该参数的可用值。所以当我们首先关闭文件描述符0后调用dup,那么新的文件描述符将是数字0.</p>
<p class="maodian"></p><h3>
例子</h3>
<p>
在下面的例子中,首先打开管道,然后fork一个子进程,然后在子进程中,使标准输入指向读管道,然后关闭子进程中的读管道和写管道,只留下标准输入,最后调用execlp函数来启动一个新的进程od,但是od并不知道它的数据来源是管道还是终端。父进程则相对简单,它首先关闭读管道,然后在写管道中写入数据,再关闭写管道就完成了它的任务。源文件为pipe2.c,代码如下:</p>
<div class="jb51code">
<div>
<div class="syntaxhighlightercpp" id="highlighter_283414">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
<div class="line number8 index7 alt1">
8</div>
<div class="line number9 index8 alt2">
9</div>
<div class="line number10 index9 alt1">
10</div>
<div class="line number11 index10 alt2">
11</div>
<div class="line number12 index11 alt1">
12</div>
<div class="line number13 index12 alt2">
13</div>
<div class="line number14 index13 alt1">
14</div>
<div class="line number15 index14 alt2">
15</div>
<div class="line number16 index15 alt1">
16</div>
<div class="line number17 index16 alt2">
17</div>
<div class="line number18 index17 alt1">
18</div>
<div class="line number19 index18 alt2">
19</div>
<div class="line number20 index19 alt1">
20</div>
<div class="line number21 index20 alt2">
21</div>
<div class="line number22 index21 alt1">
22</div>
<div class="line number23 index22 alt2">
23</div>
<div class="line number24 index23 alt1">
24</div>
<div class="line number25 index24 alt2">
25</div>
<div class="line number26 index25 alt1">
26</div>
<div class="line number27 index26 alt2">
27</div>
<div class="line number28 index27 alt1">
28</div>
<div class="line number29 index28 alt2">
29</div>
<div class="line number30 index29 alt1">
30</div>
<div class="line number31 index30 alt2">
31</div>
<div class="line number32 index31 alt1">
32</div>
<div class="line number33 index32 alt2">
33</div>
<div class="line number34 index33 alt1">
34</div>
<div class="line number35 index34 alt2">
35</div>
<div class="line number36 index35 alt1">
36</div>
<div class="line number37 index36 alt2">
37</div>
<div class="line number38 index37 alt1">
38</div>
<div class="line number39 index38 alt2">
39</div>
<div class="line number40 index39 alt1">
40</div>
<div class="line number41 index40 alt2">
41</div>
<div class="line number42 index41 alt1">
42</div>
<div class="line number43 index42 alt2">
43</div>
<div class="line number44 index43 alt1">
44</div>
<div class="line number45 index44 alt2">
45</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="cpp preprocessor">#include <unistd.h> </code>
</div>
<div class="line number2 index1 alt1">
<code class="cpp preprocessor">#include <stdlib.h> </code>
</div>
<div class="line number3 index2 alt2">
<code class="cpp preprocessor">#include <stdio.h> </code>
</div>
<div class="line number4 index3 alt1">
<code class="cpp preprocessor">#include <string.h> </code>
</div>
<div class="line number5 index4 alt2">
<code class="cpp spaces"> </code>
</div>
<div class="line number6 index5 alt1">
<code class="cpp color1 bold">int</code> <code class="cpp plain">main() </code>
</div>
<div class="line number7 index6 alt2">
<code class="cpp plain">{ </code>
</div>
<div class="line number8 index7 alt1">
<code class="cpp spaces"> </code><code class="cpp color1 bold">int</code> <code class="cpp plain">data_processed = 0; </code>
</div>
<div class="line number9 index8 alt2">
<code class="cpp spaces"> </code><code class="cpp color1 bold">int</code> <code class="cpp plain">pipes; </code>
</div>
<div class="line number10 index9 alt1">
<code class="cpp spaces"> </code><code class="cpp keyword bold">const</code> <code class="cpp color1 bold">char</code> <code class="cpp plain">data[] = </code><code class="cpp string">"123"</code><code class="cpp plain">; </code>
</div>
<div class="line number11 index10 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">pid_t pid; </code>
</div>
<div class="line number12 index11 alt1">
<code class="cpp spaces"> </code>
</div>
<div class="line number13 index12 alt2">
<code class="cpp spaces"> </code><code class="cpp keyword bold">if</code><code class="cpp plain">(pipe(pipes) == 0) </code>
</div>
<div class="line number14 index13 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">{ </code>
</div>
<div class="line number15 index14 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">pid = fork(); </code>
</div>
<div class="line number16 index15 alt1">
<code class="cpp spaces"> </code><code class="cpp keyword bold">if</code><code class="cpp plain">(pid == -1) </code>
</div>
<div class="line number17 index16 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">{ </code>
</div>
<div class="line number18 index17 alt1">
<code class="cpp spaces"> </code><code class="cpp functions bold">fprintf</code><code class="cpp plain">(stderr, </code><code class="cpp string">"fork failure!\n"</code><code class="cpp plain">); </code>
</div>
<div class="line number19 index18 alt2">
<code class="cpp spaces"> </code><code class="cpp functions bold">exit</code><code class="cpp plain">(exit_failure); </code>
</div>
<div class="line number20 index19 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">} </code>
</div>
<div class="line number21 index20 alt2">
<code class="cpp spaces"> </code><code class="cpp keyword bold">if</code><code class="cpp plain">(pid == 0) </code>
</div>
<div class="line number22 index21 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">{ </code>
</div>
<div class="line number23 index22 alt2">
<code class="cpp spaces"> </code><code class="cpp comments">//子进程中 </code>
</div>
<div class="line number24 index23 alt1">
<code class="cpp spaces"> </code><code class="cpp comments">//使标准输入指向fildes </code>
</div>
<div class="line number25 index24 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">close(0); </code>
</div>
<div class="line number26 index25 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">dup(pipes); </code>
</div>
<div class="line number27 index26 alt2">
<code class="cpp spaces"> </code><code class="cpp comments">//关闭pipes和pipes,只剩下标准输入 </code>
</div>
<div class="line number28 index27 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">close(pipes); </code>
</div>
<div class="line number29 index28 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">close(pipes); </code>
</div>
<div class="line number30 index29 alt1">
<code class="cpp spaces"> </code><code class="cpp comments">//启动新进程od </code>
</div>
<div class="line number31 index30 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">execlp(</code><code class="cpp string">"od"</code><code class="cpp plain">, </code><code class="cpp string">"od"</code><code class="cpp plain">, </code><code class="cpp string">"-c"</code><code class="cpp plain">, 0); </code>
</div>
<div class="line number32 index31 alt1">
<code class="cpp spaces"> </code><code class="cpp functions bold">exit</code><code class="cpp plain">(exit_failure); </code>
</div>
<div class="line number33 index32 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">} </code>
</div>
<div class="line number34 index33 alt1">
<code class="cpp spaces"> </code><code class="cpp keyword bold">else</code>
</div>
<div class="line number35 index34 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">{ </code>
</div>
<div class="line number36 index35 alt1">
<code class="cpp spaces"> </code><code class="cpp comments">//关闭pipes,因为父进程不用读取数据 </code>
</div>
<div class="line number37 index36 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">close(pipes); </code>
</div>
<div class="line number38 index37 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">data_processed = write(pipes, data, </code><code class="cpp functions bold">strlen</code><code class="cpp plain">(data)); </code>
</div>
<div class="line number39 index38 alt2">
<code class="cpp spaces"> </code><code class="cpp comments">//写完数据后,关闭pipes </code>
</div>
<div class="line number40 index39 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">close(pipes); </code>
</div>
<div class="line number41 index40 alt2">
<code class="cpp spaces"> </code><code class="cpp functions bold">printf</code><code class="cpp plain">(</code><code class="cpp string">"%d - wrote %d bytes\n"</code><code class="cpp plain">, getpid(), data_processed); </code>
</div>
<div class="line number42 index41 alt1">
<code class="cpp spaces"> </code><code class="cpp plain">} </code>
</div>
<div class="line number43 index42 alt2">
<code class="cpp spaces"> </code><code class="cpp plain">} </code>
</div>
<div class="line number44 index43 alt1">
<code class="cpp spaces"> </code><code class="cpp functions bold">exit</code><code class="cpp plain">(exit_success); </code>
</div>
<div class="line number45 index44 alt2">
<code class="cpp plain">}</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
</div>
<p>
运行结果为:</p>
<p>
<img title="Linux通过匿名管道进行进程间通信" alt="Linux通过匿名管道进行进程间通信" id="theimg" src="https://zhuji.jb51.net/uploads/img/202305/1e552a1f224485cd3193afb4b17d6511.jpg"></p>
<p>
从运行结果中可以看出od进程正确地完成了它的任务,与在shell中直接输入od -c和123的效果一样。</p>
<p class="maodian"></p><h2>
<span>五、关于管道关闭后的读操作的讨论</span>
</h2>
<p>
现在有这样一个问题,假如父进程向管道file_pipe写数据,而子进程在管道file_pipe中读取数据,当父进程没有向file_pipe写数据时,子进程则没有数据可读,则子进程会发生什么呢?再者父进程把file_pipe关闭了,子进程又会有什么反应呢?</p>
<p>
当写数据的管道没有关闭,而又没有数据可读时,read调用通常会阻塞,但是当写数据的管道关闭时,read调用将会返回0而不是阻塞。注意,这与读取一个无效的文件描述符不同,read一个无效的文件描述符返回-1。</p>
<p class="maodian"></p><h2>
<span>六、匿名管道的缺陷</span>
</h2>
<p>
看了这么多相信大家也知道它的一个缺点,就是通信的进程,它们的关系一定是父子进程的关系,这就使得它的使用受到了一点的限制,但是我们可以使用命名管道来解决这个问题。命名管道将在下一篇文章:linux进程间通信——使用命名管道中介绍。</p>
<p class="maodian"></p><h2>
<span>总结</span>
</h2>
<p>
以上就是本文关于linux通过匿名管道进行进程间通信的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!</p>
<p>
原文链接:http://blog.csdn.net/ljianhui/article/details/10168031</p>
頁:
[1]