﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-C++夜未眠-随笔分类-Linux</title><link>http://www.cppblog.com/zhouliang/category/15547.html</link><description /><language>zh-cn</language><lastBuildDate>Wed, 01 Dec 2010 06:37:57 GMT</lastBuildDate><pubDate>Wed, 01 Dec 2010 06:37:57 GMT</pubDate><ttl>60</ttl><item><title>深刻理解Linux进程间通信(管道、有名管道)</title><link>http://www.cppblog.com/zhouliang/archive/2010/12/01/135154.html</link><dc:creator>老周</dc:creator><author>老周</author><pubDate>Wed, 01 Dec 2010 04:42:00 GMT</pubDate><guid>http://www.cppblog.com/zhouliang/archive/2010/12/01/135154.html</guid><wfw:comment>http://www.cppblog.com/zhouliang/comments/135154.html</wfw:comment><comments>http://www.cppblog.com/zhouliang/archive/2010/12/01/135154.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zhouliang/comments/commentRss/135154.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zhouliang/services/trackbacks/135154.html</trackback:ping><description><![CDATA[<p><a name=N1003C>1、 管道概述及相关API应用</a></p>
<p><a name=N10042><span class=smalltitle>1.1 管道相关的关键概念</span></a></p>
<p>管道是Linux支持的最初Unix IPC形式之一，具有以下特点：</p>
<ul>
    <li>管道是半双工的，数据只能向一个方向流动；需要双方通信时，需要建立起两个管道；
    <li>只能用于父子进程或者兄弟进程之间（具有亲缘关系的进程）；
    <li>单独构成一种独立的文件系统：管道对于管道两端的进程而言，就是一个文件，但它不是普通的文件，它不属于某种文件系统，而是自立门户，单独构成一种文件系统，并且只存在与内存中。
    <li>数据的读出和写入：一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾，并且每次都是从缓冲区的头部读出数据。 </li>
</ul>
<p><a name=N1005A><span class=smalltitle>1.2管道的创建：</span></a><br><br>#include &lt;unistd.h&gt;<br>&nbsp;&nbsp;int pipe(int fd[2])<br><br></p>
<p>该函数创建的管道的两端处于一个进程中间，在实际应用中没有太大意义，因此，一个进程在由pipe()创建管道后，一般再fork一个子进程，然后通过管道实现父子进程间的通信（因此也不难推出，只要两个进程中存在亲缘关系，这里的亲缘关系指的是具有共同的祖先，都可以采用管道方式来进行通信）。</p>
<p><a name=N10067><span class=smalltitle>1.3管道的读写规则：</span></a></p>
<p>管道两端可分别用描述字fd[0]以及fd[1]来描述，需要注意的是，管道的两端是固定了任务的。即一端只能用于读，由描述字fd[0]表示，称其为管道读端；另一端则只能用于写，由描述字fd[1]来表示，称其为管道写端。如果试图从管道写端读取数据，或者向管道读端写入数据都将导致错误发生。一般文件的I/O函数都可以用于管道，如close、read、write等等。</p>
<p>从管道中读取数据：</p>
<ul>
    <li>如果管道的写端不存在，则认为已经读到了数据的末尾，读函数返回的读出字节数为0；
    <li>当管道的写端存在时，如果请求的字节数目大于PIPE_BUF，则返回管道中现有的数据字节数，如果请求的字节数目不大于PIPE_BUF，则返回管道中现有数据字节数（此时，管道中数据量小于请求的数据量）；或者返回请求的字节数（此时，管道中数据量不小于请求的数据量）。注：（PIPE_BUF在include/linux/limits.h中定义，不同的内核版本可能会有所不同。Posix.1要求PIPE_BUF至少为512字节，red hat 7.2中为4096）。 </li>
</ul>
<p>关于管道的读规则验证：<br></p>
<p dir=ltr style="MARGIN-RIGHT: 0px" align=left><span style="FONT-FAMILY: Verdana">/**************<br>* readtest.c *<br>**************/<br>#include &lt;unistd.h&gt;<br>#include &lt;sys/types.h&gt;<br>#include &lt;errno.h&gt;<br>main()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int pipe_fd[2];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pid_t pid;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char r_buf[100];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char w_buf[4];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char* p_wbuf;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int r_num;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int cmd;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memset(r_buf,0,sizeof(r_buf));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memset(w_buf,0,sizeof(r_buf));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p_wbuf=w_buf;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(pipe(pipe_fd)&lt;0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("pipe create error\n");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return -1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if((pid=fork())==0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("\n");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close(pipe_fd[1]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sleep(3);//确保父进程关闭写端<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r_num=read(pipe_fd[0],r_buf,100);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf( "read num is %d&nbsp;&nbsp; the data read from the pipe is %d\n",r_num,atoi(r_buf));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close(pipe_fd[0]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else if(pid&gt;0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close(pipe_fd[0]);//read<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strcpy(w_buf,"111");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(write(pipe_fd[1],w_buf,4)!=-1)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("parent write over\n");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close(pipe_fd[1]);//write<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("parent close fd[1] over\n");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sleep(10);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <br>}<br></span>/**************************************************<br>* 程序输出结果：<br>* parent write over<br>* parent close fd[1] over<br>* read num is 4&nbsp;&nbsp; the data read from the pipe is 111<br>* 附加结论：<br>* 管道写端关闭后，写入的数据将一直存在，直到读出为止.<br>****************************************************/ <br><br>向管道中写入数据：</p>
<ul>
    <li>向管道中写入数据时，linux将不保证写入的原子性，管道缓冲区一有空闲区域，写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据，那么写操作将一直阻塞。 <br>注：只有在管道的读端存在时，向管道中写入数据才有意义。否则，向管道中写入数据的进程将收到内核传来的SIFPIPE信号，应用程序可以处理该信号，也可以忽略（默认动作则是应用程序终止）。 </li>
</ul>
<p>对管道的写规则的验证1：写端对读端存在的依赖性<br></p>
<pre class=displaycode style="FONT-FAMILY: Verdana">#include &lt;unistd.h&gt;
#include &lt;sys/types.h&gt;
main()
{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int pipe_fd[2];
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pid_t pid;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char r_buf[4];
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char* w_buf;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int writenum;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int cmd;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memset(r_buf,0,sizeof(r_buf));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(pipe(pipe_fd)&lt;0)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("pipe create error\n");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return -1;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if((pid=fork())==0)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close(pipe_fd[0]);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close(pipe_fd[1]);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sleep(10);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else if(pid&gt;0)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sleep(1);  //等待子进程完成关闭读端的操作
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close(pipe_fd[0]);//write
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;w_buf="111";
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if((writenum=write(pipe_fd[1],w_buf,4))==-1)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("write to pipe error\n");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("the bytes write to pipe is %d \n", writenum);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close(pipe_fd[1]);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
}
</pre>
<p><br><br><br><br>则输出结果为： Broken pipe,原因就是该管道以及它的所有fork()产物的读端都已经被关闭。如果在父进程中保留读端，即在写完pipe后，再关闭父进程的读端，也会正常写入pipe，读者可自己验证一下该结论。因此，在向管道写入数据时，至少应该存在某一个进程，其中管道读端没有被关闭，否则就会出现上述错误（管道断裂,进程收到了SIGPIPE信号，默认动作是进程终止）</p>
<p>对管道的写规则的验证2：linux不保证写管道的原子性验证<br><br><br></p>
<p>结论：</p>
<p>写入数目小于4096时写入是非原子的！ <br>如果把父进程中的两次写入字节数都改为5000，则很容易得出下面结论： <br>写入管道的数据量大于4096字节时，缓冲区的空闲空间将被写入数据（补齐），直到写完所有数据为止，如果没有进程读数据，则一直阻塞。 </p>
<p><a name=N100A9><span class=smalltitle>1.4管道应用实例：</span></a></p>
<p>实例一：用于shell </p>
<p>管道可用于输入输出重定向，它将一个命令的输出直接定向到另一个命令的输入。比如，当在某个shell程序（Bourne shell或C shell等）键入who│wc -l后，相应shell程序将创建who以及wc两个进程和这两个进程间的管道。考虑下面的命令行：</p>
<p>$kill -l <br>$kill -l | grep SIGRTMIN </p>
<p>实例二：用于具有亲缘关系的进程间通信 </p>
<p>下面例子给出了管道的具体应用，父进程通过管道发送一些命令给子进程，子进程解析命令，并根据命令作相应处理。<br><br></p>
<pre class=displaycode style="FONT-FAMILY: Verdana">#include &lt;unistd.h&gt;
#include &lt;sys/types.h&gt;
main()
{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int pipe_fd[2];
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pid_t pid;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char r_buf[4];
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char* w_buf;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int writenum;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int cmd;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memset(r_buf,0,sizeof(r_buf));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(pipe(pipe_fd)&lt;0)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("pipe create error\n");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return -1;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if((pid=fork())==0)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close(pipe_fd[0]);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close(pipe_fd[1]);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sleep(10);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else if(pid&gt;0)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sleep(1);  //等待子进程完成关闭读端的操作
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close(pipe_fd[0]);//write
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;w_buf="111";
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if((writenum=write(pipe_fd[1],w_buf,4))==-1)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("write to pipe error\n");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("the bytes write to pipe is %d \n", writenum);&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close(pipe_fd[1]);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
}
</pre>
<p><br>输出结果：<br>the bytes write to pipe 1000<br>the bytes write to pipe 1000&nbsp; //注意，此行输出说明了写入的非原子性<br>the bytes write to pipe 1000<br>the bytes write to pipe 1000<br>the bytes write to pipe 1000<br>the bytes write to pipe 120&nbsp; //注意，此行输出说明了写入的非原子性<br>the bytes write to pipe 0<br>the bytes write to pipe 0<br>......<br><br></p>
<p><a name=N100D3><span class=smalltitle>1.5 管道的局限性</span></a></p>
<p>管道的主要局限性正体现在它的特点上：</p>
<ul>
    <li>只支持单向数据流；
    <li>只能用于具有亲缘关系的进程之间；
    <li>没有名字；
    <li>管道的缓冲区是有限的（管道制存在于内存中，在管道创建时，为缓冲区分配一个页面大小）；
    <li>管道所传送的是无格式字节流，这就要求管道的读出方和写入方必须事先约定好数据的格式，比如多少字节算作一个消息（或命令、或记录）等等 </li>
</ul>
<img src ="http://www.cppblog.com/zhouliang/aggbug/135154.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zhouliang/" target="_blank">老周</a> 2010-12-01 12:42 <a href="http://www.cppblog.com/zhouliang/archive/2010/12/01/135154.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深刻理解Linux进程间通信(序)</title><link>http://www.cppblog.com/zhouliang/archive/2010/12/01/135149.html</link><dc:creator>老周</dc:creator><author>老周</author><pubDate>Wed, 01 Dec 2010 04:16:00 GMT</pubDate><guid>http://www.cppblog.com/zhouliang/archive/2010/12/01/135149.html</guid><wfw:comment>http://www.cppblog.com/zhouliang/comments/135149.html</wfw:comment><comments>http://www.cppblog.com/zhouliang/archive/2010/12/01/135149.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zhouliang/comments/commentRss/135149.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zhouliang/services/trackbacks/135149.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 14pt">linux下的进程通信手段基本上是从Unix平台上的进程通信手段继承而来的。而对Unix发展做出重大贡献的两大主力AT&amp;T的贝尔实验室及BSD（加州大学伯克利分校的伯克利软件发布中心）在进程间通信方面的侧重点有所不同。前者对Unix早期的进程间通信手段进行了系统的改进和扩充，形成了&#8220;system V IPC&#8221;，通信进程局限在单个计算机内；后者则跳过了该限制，形成了基于套接口（socket）的进程间通信机制。Linux则把两者继承了下来，如图示：</p>
<p style="FONT-SIZE: 14pt"><br>
<div style="FONT-SIZE: 14pt" align=center><img height=217 alt="" src="http://www.ibm.com/developerworks/cn/linux/l-ipc/1.gif" width=370 border=0></div>
<br>
<p style="FONT-SIZE: 14pt">&nbsp;</p>
<p style="FONT-SIZE: 14pt">其中，最初Unix IPC包括：管道、FIFO、信号；System V IPC包括：System V消息队列、System V信号灯、System V共享内存区；Posix IPC包括： Posix消息队列、Posix信号灯、Posix共享内存区。有两点需要简单说明一下：1）由于Unix版本的多样性，电子电气工程协会（IEEE）开发了一个独立的Unix标准，这个新的ANSI Unix标准被称为计算机环境的可移植性操作系统界面（PSOIX）。现有大部分Unix和流行版本都是遵循POSIX标准的，而Linux从一开始就遵循POSIX标准；2）BSD并不是没有涉足单机内的进程间通信（socket本身就可以用于单机内的进程间通信）。事实上，很多Unix版本的单机IPC留有BSD的痕迹，如4.4BSD支持的匿名内存映射、4.3+BSD对可靠信号语义的实现等等。</p>
<p style="FONT-SIZE: 14pt">图一给出了linux 所支持的各种IPC手段，在本文接下来的讨论中，为了避免概念上的混淆，在尽可能少提及Unix的各个版本的情况下，所有问题的讨论最终都会归结到Linux环境下的进程间通信上来。并且，对于Linux所支持通信手段的不同实现版本（如对于共享内存来说，有Posix共享内存区以及System V共享内存区两个实现版本），将主要介绍Posix API。</p>
<p style="FONT-SIZE: 14pt">linux下进程间通信的几种主要手段简介：</p>
<ol>
    <li><span style="FONT-SIZE: 14pt">管道（Pipe）及有名管道（named pipe）：管道可用于具有亲缘关系进程间的通信，有名管道克服了管道没有名字的限制，因此，除具有管道所具有的功能外，它还允许无亲缘关系进程间的通信； </span>
    <li><span style="FONT-SIZE: 14pt">信号（Signal）：信号是比较复杂的通信方式，用于通知接受进程有某种事件发生，除了用于进程间通信外，进程还可以发送信号给进程本身；linux除了支持Unix早期信号语义函数sigal外，还支持语义符合Posix.1标准的信号函数sigaction（实际上，该函数是基于BSD的，BSD为了实现可靠信号机制，又能够统一对外接口，用sigaction函数重新实现了signal函数）； </span>
    <li><span style="FONT-SIZE: 14pt">报文（Message）队列（消息队列）：消息队列是消息的链接表，包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息，被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少，管道只能承载无格式字节流以及缓冲区大小受限等缺点。 </span>
    <li><span style="FONT-SIZE: 14pt">共享内存：使得多个进程可以访问同一块内存空间，是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制，如信号量结合使用，来达到进程间的同步及互斥。 </span>
    <li><span style="FONT-SIZE: 14pt">信号量（semaphore）：主要作为进程间以及同一进程不同线程之间的同步手段。 </span>
    <li>套接口（Socket）：更为一般的进程间通信机制，可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的，但现在一般可以移植到其它类Unix系统上：Linux和System V的变种都支持套接字。 </li>
</ol>
<p style="FONT-SIZE: 14pt">下面将对上述通信机制做具体阐述。</p>
<p style="FONT-SIZE: 14pt">一般来说，linux下的进程包含以下几个关键要素：</p>
<ul>
    <li><span style="FONT-SIZE: 14pt">有一段可执行程序； </span>
    <li><span style="FONT-SIZE: 14pt">有专用的系统堆栈空间； </span>
    <li><span style="FONT-SIZE: 14pt">内核中有它的控制块（进程控制块），描述进程所占用的资源，这样，进程才能接受内核的调度； </span>
    <li style="FONT-SIZE: 14pt">具有独立的存储空间 </li>
</ul>
<p style="FONT-SIZE: 14pt">进程和线程有时候并不完全区分，而往往根据上下文理解其含义</p>
<img src ="http://www.cppblog.com/zhouliang/aggbug/135149.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zhouliang/" target="_blank">老周</a> 2010-12-01 12:16 <a href="http://www.cppblog.com/zhouliang/archive/2010/12/01/135149.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>