<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet href='http://feeds.feedsky.com/styles/temp01.xsl' type='text/xsl' ?><!--这是一个由Feedsy提供技术支持的Feed，为了提高读者阅读的体验，以及满足用户美化自己Feed的需要，我们设计了多种精美的Feed模板，提供给大家选择，所有最终呈现出来的样式，皆由用户自愿选择使用，未经许可，任何团体和个人，请不要擅自修改样式或者盗用，这是对于用户选择权的尊重。--><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:fs="http://www.feedsky.com/namespace/feed" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><atom:link href="http://feeds.feedsky.com/mcublog.com/user1/5" type="application/rss+xml" rel="self"></atom:link><fs:self_link href="http://feeds.feedsky.com/mcublog.com/user1/5" type="application/rss+xml"></fs:self_link><lastBuildDate>Tue, 02 Dec 2008 13:42:00 GMT</lastBuildDate><title>MCUBLOG的电子博客</title><description>MCUBLOG的电子博客</description><link>http://www.mcublog.com/blog/user1/5/index.html</link><item><title>list_head结构的介绍</title><link>http://item.feedsky.com/~mcublog.com/user1/5/~691605/147011329/691605/1/item.html</link><description>&lt;P&gt;&lt;SPAN style=&quot;FONT-SIZE: 12pt; COLOR: #ff0000&quot;&gt;&lt;STRONG&gt;list_head结构的介绍&lt;/STRONG&gt;&lt;/SPAN&gt;&lt;BR&gt;list_head结构定义在 &lt;LINUX list.h=&quot;&quot; /&gt;里，它是一个double linked list的结构。 底下是它的结构宣告: &lt;/P&gt;
&lt;P&gt;
&lt;TABLE width=&quot;88%&quot;&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width=&quot;6%&quot;&gt;&lt;BR&gt;&lt;/TD&gt;
&lt;TD bgColor=#c0ffc0&gt;&lt;SPAN style=&quot;FONT-SIZE: 10pt&quot;&gt;struct&amp;nbsp;list_head&amp;nbsp;{&lt;BR&gt;struct&amp;nbsp;list_head&amp;nbsp;*next,&amp;nbsp;*prev;&lt;BR&gt;};&lt;/SPAN&gt;&lt;/TD&gt;
&lt;TD width=&quot;6%&quot;&gt;&lt;BR&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/P&gt;
&lt;P&gt;有的人可能看到这样的结构会觉得很奇怪这样的结构可以存放资料吗? 当然是不行的棉，因为这个结构根本是拿来让人当资料存的。 首先， 我们先来看看两个macro， &lt;/P&gt;
&lt;P&gt;
&lt;TABLE width=&quot;88%&quot;&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width=&quot;6%&quot;&gt;&lt;BR&gt;&lt;/TD&gt;
&lt;TD bgColor=#c0ffc0&gt;&lt;SPAN style=&quot;FONT-SIZE: 10pt&quot;&gt;#define&amp;nbsp;LIST_HEAD(name)&amp;nbsp;\&lt;BR&gt;struct&amp;nbsp;list_head&amp;nbsp;name&amp;nbsp;=&amp;nbsp;{&amp;nbsp;&amp;amp;name,&amp;nbsp;&amp;amp;name&amp;nbsp;}&lt;BR&gt;#define&amp;nbsp;INIT_LIST_HEAD(ptr)&amp;nbsp;do&amp;nbsp;{&amp;nbsp;\&lt;BR&gt;(ptr)-&amp;gt;next&amp;nbsp;=&amp;nbsp;(ptr);&amp;nbsp;(ptr)-&amp;gt;prev&amp;nbsp;=&amp;nbsp;(ptr);&amp;nbsp;\&lt;BR&gt;}&amp;nbsp;while&amp;nbsp;(0)&lt;/SPAN&gt;&lt;/TD&gt;
&lt;TD width=&quot;6%&quot;&gt;&lt;BR&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/P&gt;
&lt;P&gt;这两个macro在Kernel里也算蛮常出现的， 是用来将list_head做初始化的，它的初始化就是将next和prev这两个栏位设为跟结构的地址相同。 所以， 如果我们在程序里看到这样的程序， 它的意思就是宣告一个list_head结构的变数hello，并将prev和next都设成hello的地址。 &lt;/P&gt;
&lt;P&gt;
&lt;TABLE width=&quot;88%&quot;&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width=&quot;6%&quot;&gt;&lt;BR&gt;&lt;/TD&gt;
&lt;TD bgColor=#c0ffc0&gt;&lt;SPAN style=&quot;FONT-SIZE: 10pt&quot;&gt;LIST_HEAD(hello)&lt;/SPAN&gt;&lt;/TD&gt;
&lt;TD width=&quot;6%&quot;&gt;&lt;BR&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/P&gt;
&lt;P&gt;因此， 如果要检查这个list是否是空的， 只要检查hello.next是否等于&amp;amp;hello就可以了。事实上， Linux也提供了一个叫list_empty()的函式来检查list是否为空的。 &lt;/P&gt;
&lt;P&gt;
&lt;TABLE width=&quot;88%&quot;&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width=&quot;6%&quot;&gt;&lt;BR&gt;&lt;/TD&gt;
&lt;TD bgColor=#c0ffc0&gt;&lt;SPAN style=&quot;FONT-SIZE: 10pt&quot;&gt;static&amp;nbsp;__inline__&amp;nbsp;int&amp;nbsp;list_empty(struct&amp;nbsp;list_head&amp;nbsp;*head)&lt;BR&gt;{&lt;BR&gt;return&amp;nbsp;head-&amp;gt;next&amp;nbsp;==&amp;nbsp;head;&lt;BR&gt;}&lt;/SPAN&gt;&lt;/TD&gt;
&lt;TD width=&quot;6%&quot;&gt;&lt;BR&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/P&gt;
&lt;P&gt;现在我们来介绍如何加入或删除list_head到上面的hello串行里。 Linux提供二个函式来做这些事， 分别是list_add()和lis_del()。 这两个函式的定义都放在 &lt;LINUX list.h=&quot;&quot; /&gt;里， 而且其程序码也都很简单，只是单纯double linked list的串接和删除而已， 因此我们不对它们做介绍。 有关于这个结构， 其实最重要的应该是它提供的这个macro。 &lt;/P&gt;
&lt;P&gt;
&lt;TABLE width=&quot;88%&quot;&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width=&quot;6%&quot;&gt;&lt;BR&gt;&lt;/TD&gt;
&lt;TD bgColor=#c0ffc0&gt;&lt;SPAN style=&quot;FONT-SIZE: 10pt&quot;&gt;#define&amp;nbsp;list_entry(ptr,&amp;nbsp;type,&amp;nbsp;member)&amp;nbsp;\&lt;BR&gt;((type&amp;nbsp;*)((char&amp;nbsp;*)(ptr)-(unsigned&amp;nbsp;long)(&amp;amp;((type&amp;nbsp;*)0)-&amp;gt;member)))&lt;/SPAN&gt;&lt;/TD&gt;
&lt;TD width=&quot;6%&quot;&gt;&lt;BR&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;IMG alt=&quot;&quot; src=&quot;http://mcublog.com/blog/UploadFiles/2008-12/81539974796.gif&quot; border=0&gt;&lt;/P&gt;
&lt;P&gt;下面这段程序码。 &lt;/P&gt;
&lt;P&gt;
&lt;TABLE width=&quot;88%&quot;&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width=&quot;6%&quot;&gt;&lt;BR&gt;&lt;/TD&gt;
&lt;TD bgColor=#c0ffc0&gt;&lt;SPAN style=&quot;FONT-SIZE: 10pt&quot;&gt;struct&amp;nbsp;HelloWorld&amp;nbsp;{&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;int&amp;nbsp;x,&amp;nbsp;y;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;struct&amp;nbsp;list_head&amp;nbsp;list;&lt;BR&gt;}&amp;nbsp;hello;&lt;/SPAN&gt;&lt;/TD&gt;
&lt;TD width=&quot;6%&quot;&gt;&lt;BR&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/P&gt;
&lt;P&gt;假设int是4个byte。 那么以下这一行会得到8， 如图5所示 &lt;/P&gt;
&lt;P&gt;
&lt;TABLE width=&quot;88%&quot;&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width=&quot;6%&quot;&gt;&lt;BR&gt;&lt;/TD&gt;
&lt;TD bgColor=#c0ffc0&gt;&lt;SPAN style=&quot;FONT-SIZE: 10pt&quot;&gt;(unsigned&amp;nbsp;long)&amp;nbsp;(&amp;amp;((struct&amp;nbsp;HelloWorld&amp;nbsp;*)0)-&amp;gt;list)&lt;/SPAN&gt;&lt;/TD&gt;
&lt;TD width=&quot;6%&quot;&gt;&lt;BR&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/P&gt;
&lt;P&gt;有的人会对这一行程序感到奇怪， (struct HelloWorld*)0不就是一个NULL的指标吗? 怎么可以用0-&amp;gt;list去参考list这个栏位呢? 难道不怕造成segmentation fault吗? 请注意一下， 我们在0-&amp;gt;list的前面还加上了一个&amp;amp;。 如果没有&amp;amp;， 那上面这一行就会segmentation fault了。 如果你加上了&amp;amp;， 那就没问题棉。 Segmentation fault通常是去参考到不合法的记忆体地址内容所造成的， 如果我们加上了&amp;amp;就表示我们没有要去参考这个不合法地址的内容，我们只是要那个栏位的地址而已， 因此， 不会造成segmentation fault。 其实， 结构的配置在记忆体里是连续的。 所以， 如果我们去读取某个栏位时，像&amp;amp;hello-&amp;gt;list。 会先取得hello变数的地址， 再然后再计算HelloWorld结构里list栏位所在的offset， 再将hello的地址加上list栏位的offset，求得list栏位真正的地址。 然后再去读list栏位的内容。 这是compiler帮我们做的。 那我们现在就来看看上面那一行究竟是什么意思。 首先， 我们先把上面那一行想象成下面这个样子。 &lt;/P&gt;
&lt;P&gt;
&lt;TABLE width=&quot;88%&quot;&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width=&quot;6%&quot;&gt;&lt;BR&gt;&lt;/TD&gt;
&lt;TD bgColor=#c0ffc0&gt;&lt;SPAN style=&quot;FONT-SIZE: 10pt&quot;&gt;ptr&amp;nbsp;=&amp;nbsp;0;&lt;BR&gt;(unsigned&amp;nbsp;long)&amp;nbsp;(&amp;amp;((struct&amp;nbsp;HelloWorld&amp;nbsp;*)ptr)-&amp;gt;list)&lt;/SPAN&gt;&lt;/TD&gt;
&lt;TD width=&quot;6%&quot;&gt;&lt;BR&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/P&gt;
&lt;P&gt;这样是不是容易懂了吗， 就是要取得&amp;amp;ptr-&amp;gt;list的地址而已。所以， 如果ptr是100的话， 那会得到100+8=108。 因为前面有二个int， 每一个int是4个byte。 经过转型， 就得到了(unsigned long)型态的108。 如果ptr是0的话， 那同理， 我们会得到0+8=8。 也就是这个栏位在HelloWorld结构里的offset。 &lt;/P&gt;
&lt;P&gt;现在， 如果我们已经知道了list在HelloWorld结构中的offset，而且我们现在也知道hello这个变数里list的地址的话， 那有没有办法得到hello本身的地址呢? 可以的， 就像图6一样， 如果我们知道list的地址， 只要将list的地址减8就可以知道了hello的地址了嘛。 &lt;/P&gt;
&lt;P&gt;
&lt;TABLE width=&quot;88%&quot;&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width=&quot;6%&quot;&gt;&lt;BR&gt;&lt;/TD&gt;
&lt;TD bgColor=#c0ffc0&gt;&lt;SPAN style=&quot;FONT-SIZE: 10pt&quot;&gt;struct&amp;nbsp;list_head&amp;nbsp;*plist&amp;nbsp;=&amp;nbsp;&amp;amp;hello.list;&lt;BR&gt;printf(&amp;nbsp;&quot;&amp;amp;hello&amp;nbsp;=&amp;nbsp;%x\n&quot;,&amp;nbsp;(char*)plist&amp;nbsp;-&amp;nbsp;(unsigned&amp;nbsp;long)&amp;nbsp;8&amp;nbsp;));&lt;/SPAN&gt;&lt;/TD&gt;
&lt;TD width=&quot;6%&quot;&gt;&lt;BR&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/P&gt;
&lt;P&gt;而这种方式就是list_head的用法， 它是专门用来当作别的结构的栏位，只要我们得到这个栏位的位置和包含这个栏位的结构是那一种， 我们可以很轻易的算出包含此栏位的结构地址， 图6就是super block在使用list_head所得到的结果。只要我们知道s_list的地址， 只要呼叫 &lt;/P&gt;
&lt;P&gt;
&lt;TABLE width=&quot;88%&quot;&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width=&quot;6%&quot;&gt;&lt;BR&gt;&lt;/TD&gt;
&lt;TD bgColor=#c0ffc0&gt;&lt;SPAN style=&quot;FONT-SIZE: 10pt&quot;&gt;list_entry(&amp;nbsp;&amp;amp;sb1.s_list,&amp;nbsp;struct&amp;nbsp;super_block,&amp;nbsp;s_list)&lt;/SPAN&gt;&lt;/TD&gt;
&lt;TD width=&quot;6%&quot;&gt;&lt;BR&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/P&gt;
&lt;P&gt;就可以得到其sb1这个super_block结构的地址。 &lt;/P&gt;</description><pubDate>Tue, 02 Dec 2008 21:42:00 +0800</pubDate><author>MCUBLOG</author><guid isPermaLink="false">http://www.mcublog.com/blog/user1/5/archives/2008/29815.html</guid><dc:creator>MCUBLOG</dc:creator><fs:srclink>http://www.mcublog.com/blog/user1/5/archives/2008/29815.html</fs:srclink><fs:srcfeed>http://www.mcublog.com/blog/user1/5/rss2.xml</fs:srcfeed><fs:itemid>mcublog.com/user1/5/~691605/147011329/691605</fs:itemid></item><item><title>static的作用</title><link>http://item.feedsky.com/~mcublog.com/user1/5/~691605/147011330/691605/1/item.html</link><description>&lt;P style=&quot;TEXT-ALIGN: left&quot; align=left&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;在&lt;/SPAN&gt;C&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;语言中，&lt;/SPAN&gt;static&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;的字面意思很容易把我们导入歧途，其实它的作用有三条。&lt;/SPAN&gt;&lt;/P&gt;
&lt;P style=&quot;TEXT-ALIGN: left&quot; align=left&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;（&lt;/SPAN&gt;1&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;）先来介绍它的第一条也是最重要的一条：隐藏。&lt;/SPAN&gt;&lt;/P&gt;
&lt;P style=&quot;TEXT-ALIGN: left&quot; align=left&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;当我们同时编译多个文件时，所有未加&lt;/SPAN&gt;static&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;前缀的全局变量和函数都具有全局可见性。为理解这句话，我举例来说明。我们要同时编译两个源文件，一个是&lt;/SPAN&gt;a.c&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;，另一个是&lt;/SPAN&gt;main.c&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;。&lt;/SPAN&gt;&lt;/P&gt;
&lt;P style=&quot;TEXT-ALIGN: left&quot; align=left&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;下面是&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-FAMILY: Arial&quot;&gt;a.c&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;的内容&lt;/SPAN&gt;&lt;/P&gt;
&lt;DIV style=&quot;BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee&quot;&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;char&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;a&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;=&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;'&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;A&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;'&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #008000&quot;&gt;//&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #008000&quot;&gt;&amp;nbsp;global&amp;nbsp;variable&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #008000&quot;&gt;&lt;BR&gt;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;void&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;msg()&amp;nbsp;&lt;BR&gt;{&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf(&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&quot;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;Hello\n&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&quot;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;);&amp;nbsp;&lt;BR&gt;}&lt;BR&gt;&lt;/SPAN&gt;&lt;/DIV&gt;&amp;nbsp; 
&lt;P class=MsoNormal style=&quot;TEXT-ALIGN: left; mso-layout-grid-align: none&quot; align=left&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial&quot;&gt;下面是&lt;/SPAN&gt;&lt;SPAN lang=EN-US style=&quot;FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'&quot;&gt;main.c&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial&quot;&gt;的内容&lt;/SPAN&gt;&lt;SPAN lang=EN-US style=&quot;FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'&quot;&gt;&lt;?XML:NAMESPACE PREFIX = O /&gt;&lt;O:P&gt;&lt;/O:P&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;DIV style=&quot;BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee&quot;&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;int&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;main(&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;void&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;)&lt;BR&gt;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;extern&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;char&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;a;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #008000&quot;&gt;//&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #008000&quot;&gt;&amp;nbsp;extern&amp;nbsp;variable&amp;nbsp;must&amp;nbsp;be&amp;nbsp;declared&amp;nbsp;before&amp;nbsp;use&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #008000&quot;&gt;&lt;BR&gt;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf(&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&quot;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;%c&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&quot;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;,&amp;nbsp;a);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;void&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;)msg();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;return&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;0&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;;&lt;BR&gt;}&lt;BR&gt;&lt;/SPAN&gt;&lt;/DIV&gt;&amp;nbsp; 
&lt;P style=&quot;TEXT-ALIGN: left&quot; align=left&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;程序的运行结果是：&lt;/SPAN&gt;&lt;/P&gt;
&lt;P style=&quot;TEXT-ALIGN: left&quot; align=left&gt;&lt;SPAN style=&quot;FONT-FAMILY: Arial&quot;&gt;A Hello&lt;/SPAN&gt;&lt;/P&gt;
&lt;P style=&quot;TEXT-ALIGN: left&quot; align=left&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;你可能会问：为什么在&lt;/SPAN&gt;a.c&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;中定义的全局变量&lt;/SPAN&gt;a&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;和函数&lt;/SPAN&gt;msg&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;能在&lt;/SPAN&gt;main.c&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;中使用？前面说过，所有未加&lt;/SPAN&gt;static&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;前缀的全局变量和函数都具有全局可见性，其它的源文件也能访问。此例中，&lt;/SPAN&gt;a&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;是全局变量，&lt;/SPAN&gt;msg&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;是函数，并且都没有加&lt;/SPAN&gt;static&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;前缀，因此对于另外的源文件&lt;/SPAN&gt;main.c&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;是可见的。&lt;/SPAN&gt;&lt;/P&gt;
&lt;P style=&quot;TEXT-ALIGN: left&quot; align=left&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;如果加了&lt;/SPAN&gt;static&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;，就会对其它源文件隐藏。例如在&lt;/SPAN&gt;a&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;和&lt;/SPAN&gt;msg&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;的定义前加上&lt;/SPAN&gt;static&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;，&lt;/SPAN&gt;main.c&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;就看不到它们了。利用这一特性可以在不同的文件中定义同名函数和同名变量，而不必担心命名冲突。&lt;/SPAN&gt;Static&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;可以用作函数和变量的前缀，对于函数来讲，&lt;/SPAN&gt;static&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;的作用仅限于隐藏，而对于变量，&lt;/SPAN&gt;static&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;还有下面两个作用。&lt;/SPAN&gt;&lt;/P&gt;&lt;SPAN style=&quot;FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体&quot;&gt;（&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-SIZE: 10.5pt; FONT-FAMILY: 'Times New Roman'&quot;&gt;2&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体&quot;&gt;）&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-SIZE: 10.5pt; FONT-FAMILY: 'Times New Roman'&quot;&gt;static&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体&quot;&gt;的第二个作用是保持变量内容的持久。存储在静态数据区的变量会在程序刚开始运行时就完成初始化，也是唯一的一次初始化。共有两种变量存储在静态存储区：全局变量和&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-SIZE: 10.5pt; FONT-FAMILY: 'Times New Roman'&quot;&gt;static&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体&quot;&gt;变量，只不过和全局变量比起来，&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-SIZE: 10.5pt; FONT-FAMILY: 'Times New Roman'&quot;&gt;static&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体&quot;&gt;可以控制变量的可见范围，说到底&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-SIZE: 10.5pt; FONT-FAMILY: 'Times New Roman'&quot;&gt;static&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体&quot;&gt;还是用来隐藏的。虽然这种用法不常见，但我还是举一个例子。&lt;/SPAN&gt;&lt;BR&gt;
&lt;DIV style=&quot;BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee&quot;&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;#include&lt;/SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;stdio.h&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;gt;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&lt;BR&gt;&lt;BR&gt;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;int&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;fun(&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;void&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;){&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;static&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;int&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;count&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;=&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;10&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #008000&quot;&gt;//&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #008000&quot;&gt;&amp;nbsp;事实上此赋值语句从来没有执行过&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #008000&quot;&gt;&lt;BR&gt;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;return&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;count&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;--&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;;&lt;BR&gt;}&lt;BR&gt;&lt;BR&gt;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;int&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;count&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;=&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;1&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;;&lt;BR&gt;&lt;BR&gt;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;int&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;main(&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;void&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;)&lt;BR&gt;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf(&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&quot;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;global\t\tlocal&amp;nbsp;static\n&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&quot;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;for&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;(;&amp;nbsp;count&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;lt;=&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;10&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;++&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;count)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf(&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&quot;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;%d\t\t%d\n&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&quot;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;,&amp;nbsp;count,&amp;nbsp;fun());&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;return&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;0&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;;&lt;BR&gt;}&lt;BR&gt;&lt;/SPAN&gt;&lt;/DIV&gt;&amp;nbsp; 
&lt;P style=&quot;TEXT-ALIGN: left&quot; align=left&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;程序的运行结果是：&lt;/SPAN&gt;&lt;/P&gt;
&lt;P style=&quot;TEXT-ALIGN: left&quot; align=left&gt;global&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; local static&lt;/P&gt;
&lt;P style=&quot;TEXT-ALIGN: left&quot; align=left&gt;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 10&lt;/P&gt;
&lt;P style=&quot;TEXT-ALIGN: left&quot; align=left&gt;2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 9&lt;/P&gt;
&lt;P style=&quot;TEXT-ALIGN: left&quot; align=left&gt;3&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 8&lt;/P&gt;
&lt;P style=&quot;TEXT-ALIGN: left&quot; align=left&gt;4&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 7&lt;/P&gt;
&lt;P style=&quot;TEXT-ALIGN: left&quot; align=left&gt;5&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 6&lt;/P&gt;
&lt;P style=&quot;TEXT-ALIGN: left&quot; align=left&gt;6&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 5&lt;/P&gt;
&lt;P style=&quot;TEXT-ALIGN: left&quot; align=left&gt;7&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4&lt;/P&gt;
&lt;P style=&quot;TEXT-ALIGN: left&quot; align=left&gt;8&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;3&lt;/P&gt;
&lt;P style=&quot;TEXT-ALIGN: left&quot; align=left&gt;9&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2&lt;/P&gt;
&lt;P style=&quot;TEXT-ALIGN: left&quot; align=left&gt;10&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&lt;/P&gt;&amp;nbsp; 
&lt;P class=MsoNormal style=&quot;TEXT-ALIGN: left; mso-layout-grid-align: none&quot; align=left&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'&quot;&gt;（&lt;/SPAN&gt;&lt;SPAN lang=EN-US&gt;3&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'&quot;&gt;）&lt;/SPAN&gt;&lt;SPAN lang=EN-US&gt;static&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'&quot;&gt;的第三个作用是默认初始化为&lt;/SPAN&gt;&lt;SPAN lang=EN-US&gt;0&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'&quot;&gt;。其实全局变量也具备这一属性，因为全局变量也存储在静态数据区。在静态数据区，内存中所有的字节默认值都是&lt;/SPAN&gt;&lt;SPAN lang=EN-US&gt;0x00&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'&quot;&gt;，某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵，我们可以一个一个地把所有元素都置&lt;/SPAN&gt;&lt;SPAN lang=EN-US&gt;0&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'&quot;&gt;，然后把不是&lt;/SPAN&gt;&lt;SPAN lang=EN-US&gt;0&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'&quot;&gt;的几个元素赋值。如果定义成静态的，就省去了一开始置&lt;/SPAN&gt;&lt;SPAN lang=EN-US&gt;0&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'&quot;&gt;的操作。再比如要把一个字符数组当字符串来用，但又觉得每次在字符数组末尾加&lt;/SPAN&gt;&lt;SPAN lang=EN-US&gt;’\&lt;?XML:NAMESPACE PREFIX = ST1 /&gt;&lt;ST1:CHMETCNV tcsc=&quot;0&quot; numbertype=&quot;1&quot; negative=&quot;False&quot; hasspace=&quot;False&quot; sourcevalue=&quot;0&quot; unitname=&quot;’&quot; w:st=&quot;on&quot;&gt;0’&lt;/ST1:CHMETCNV&gt;&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'&quot;&gt;太麻烦。如果把字符串定义成静态的，就省去了这个麻烦，因为那里本来就是&lt;/SPAN&gt;&lt;SPAN lang=EN-US&gt;’\&lt;ST1:CHMETCNV tcsc=&quot;0&quot; numbertype=&quot;1&quot; negative=&quot;False&quot; hasspace=&quot;False&quot; sourcevalue=&quot;0&quot; unitname=&quot;’&quot; w:st=&quot;on&quot;&gt;0’&lt;/ST1:CHMETCNV&gt;&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'&quot;&gt;。不妨做个小实验验证一下。&lt;/SPAN&gt;&lt;/P&gt;
&lt;DIV style=&quot;BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee&quot;&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;#include&lt;/SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;stdio.h&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;gt;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&lt;BR&gt;&lt;BR&gt;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;int&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;a;&lt;BR&gt;&lt;BR&gt;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;int&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;main(&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;void&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;)&lt;BR&gt;{&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;int&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;i;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;static&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;char&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;str[&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;10&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;];&lt;BR&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf(&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&quot;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;integer:&amp;nbsp;%d;&amp;nbsp;&amp;nbsp;string:&amp;nbsp;(begin)%s(end)&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&quot;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;,&amp;nbsp;a,&amp;nbsp;str);&lt;BR&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #0000ff&quot;&gt;return&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;0&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;;&lt;BR&gt;}&lt;BR&gt;&lt;/SPAN&gt;&lt;/DIV&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体&quot;&gt;程序的运行结果如下&lt;/SPAN&gt; 
&lt;P style=&quot;TEXT-ALIGN: left&quot; align=left&gt;integer: 0;&amp;nbsp;string: (begin)(end)&lt;/P&gt;
&lt;P class=MsoNormal style=&quot;TEXT-ALIGN: left; mso-layout-grid-align: none&quot; align=left&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'&quot;&gt;最后对&lt;/SPAN&gt;&lt;SPAN lang=EN-US&gt;static&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'&quot;&gt;的三条作用做一句话总结。首先&lt;/SPAN&gt;&lt;SPAN lang=EN-US&gt;static&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'&quot;&gt;的最主要功能是隐藏，其次因为&lt;/SPAN&gt;&lt;SPAN lang=EN-US&gt;static&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'&quot;&gt;变量存放在静态存储区，所以它具备持久性和默认值&lt;/SPAN&gt;&lt;SPAN lang=EN-US&gt;0&lt;/SPAN&gt;&lt;SPAN style=&quot;FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'&quot;&gt;。&lt;/SPAN&gt;&lt;/P&gt;</description><pubDate>Tue, 02 Dec 2008 09:50:00 +0800</pubDate><author>MCUBLOG</author><guid isPermaLink="false">http://www.mcublog.com/blog/user1/5/archives/2008/29811.html</guid><dc:creator>MCUBLOG</dc:creator><fs:srclink>http://www.mcublog.com/blog/user1/5/archives/2008/29811.html</fs:srclink><fs:srcfeed>http://www.mcublog.com/blog/user1/5/rss2.xml</fs:srcfeed><fs:itemid>mcublog.com/user1/5/~691605/147011330/691605</fs:itemid></item><item><title>Unix环境高级编程之signal笔记(下)</title><link>http://item.feedsky.com/~mcublog.com/user1/5/~691605/147011331/691605/1/item.html</link><description>&lt;BLOCKQUOTE&gt;在信号（上）中，讨论了linux信号种类、来源、如何安装一个信号以及对信号集的操作。本部分则首先讨论从信号的生命周期上认识信号，或者宏观上看似简单的信号机制（进程收到信号后，作相应的处理，看上去再简单不过了），在微观上究竟是如何实现的，也是在更深层次上理解信号。接下来还讨论了信号编程的一些注意事项，最后给出了信号编程的一些实例。&lt;/BLOCKQUOTE&gt;&lt;!--START RESERVED FOR FUTURE USE INCLUDE FILES--&gt;&lt;!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --&gt;&lt;!--END RESERVED FOR FUTURE USE INCLUDE FILES--&gt;
&lt;P&gt;&lt;A name=1&gt;&lt;SPAN class=atitle&gt;一、信号生命周期&lt;/SPAN&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;从信号发送到信号处理函数的执行完毕&lt;/P&gt;
&lt;P&gt;对于一个完整的信号生命周期(从信号发送到相应的处理函数执行完毕)来说，可以分为三个重要的阶段，这三个阶段由四个重要事件来刻画：信号诞生；信号在进程中注册完毕；信号在进程中的注销完毕；信号处理函数执行完毕。相邻两个事件的时间间隔构成信号生命周期的一个阶段。&lt;/P&gt;&lt;BR&gt;&lt;IMG height=34 alt=&quot;&quot; src=&quot;http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/3.gif&quot; width=594&gt; &lt;BR&gt;
&lt;P&gt;下面阐述四个事件的实际意义：&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;信号&quot;诞生&quot;。信号的诞生指的是触发信号的事件发生（如检测到硬件异常、定时器超时以及调用信号发送函数kill()或sigqueue()等）。 
&lt;LI&gt;信号在目标进程中&quot;注册&quot;；进程的task_struct结构中有关于本进程中未决信号的数据成员： 
&lt;TABLE cellSpacing=0 cellPadding=0 width=500 border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class=code-outline&gt;&lt;PRE class=displaycode&gt;struct sigpending pending：
struct sigpending{
	struct sigqueue *head, **tail;
	sigset_t signal;
};
&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;第三个成员是进程中所有未决信号集，第一、第二个成员分别指向一个sigqueue类型的结构链（称之为&quot;未决信号信息链&quot;）的首尾，信息链中的每个sigqueue结构刻画一个特定信号所携带的信息，并指向下一个sigqueue结构: 
&lt;TABLE cellSpacing=0 cellPadding=0 width=500 border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class=code-outline&gt;&lt;PRE class=displaycode&gt;struct sigqueue{
	struct sigqueue *next;
	siginfo_t info;
}
&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;信号在进程中注册指的就是信号值加入到进程的未决信号集中（sigpending结构的第二个成员sigset_t signal），并且信号所携带的信息被保留到未决信号信息链的某个sigqueue结构中。只要信号在进程的未决信号集中，表明进程已经知道这些信号的存在，但还没来得及处理，或者该信号被进程阻塞。 
&lt;P&gt;&lt;B&gt;注：&lt;/B&gt; &lt;BR&gt;当一个实时信号发送给一个进程时，不管该信号是否已经在进程中注册，都会被再注册一次，因此，信号不会丢失，因此，实时信号又叫做&quot;可靠信号&quot;。这意味着同一个实时信号可以在同一个进程的未决信号信息链中占有多个sigqueue结构（进程每收到一个实时信号，都会为它分配一个结构来登记该信号信息，并把该结构添加在未决信号链尾，即所有诞生的实时信号都会在目标进程中注册）； &lt;BR&gt;当一个非实时信号发送给一个进程时，如果该信号已经在进程中注册，则该信号将被丢弃，造成信号丢失。因此，非实时信号又叫做&quot;不可靠信号&quot;。这意味着同一个非实时信号在进程的未决信号信息链中，至多占有一个sigqueue结构（一个非实时信号诞生后，（1）、如果发现相同的信号已经在目标结构中注册，则不再注册，对于进程来说，相当于不知道本次信号发生，信号丢失；（2）、如果进程的未决信号中没有相同信号，则在进程中注册自己）。 &lt;/P&gt;
&lt;LI&gt;信号在进程中的注销。在目标进程执行过程中，会检测是否有信号等待处理（每次从系统空间返回到用户空间时都做这样的检查）。如果存在未决信号等待处理且该信号没有被进程阻塞，则在运行相应的信号处理函数前，进程会把信号在未决信号链中占有的结构卸掉。是否将信号从进程未决信号集中删除对于实时与非实时信号是不同的。对于非实时信号来说，由于在未决信号信息链中最多只占用一个sigqueue结构，因此该结构被释放后，应该把信号在进程未决信号集中删除（信号注销完毕）；而对于实时信号来说，可能在未决信号信息链中占用多个sigqueue结构，因此应该针对占用sigqueue结构的数目区别对待：如果只占用一个sigqueue结构（进程只收到该信号一次），则应该把信号在进程的未决信号集中删除（信号注销完毕）。否则，不应该在进程的未决信号集中删除该信号（信号注销完毕）。 &lt;BR&gt;进程在执行信号相应处理函数之前，首先要把信号在进程中注销。 
&lt;LI&gt;信号生命终止。进程注销信号后，立即执行相应的信号处理函数，执行完毕后，信号的本次发送对进程的影响彻底结束。 
&lt;P&gt;&lt;B&gt;注：&lt;/B&gt; &lt;BR&gt;1）信号注册与否，与发送信号的函数（如kill()或sigqueue()等）以及信号安装函数（signal()及sigaction()）无关，只与信号值有关（信号值小于SIGRTMIN的信号最多只注册一次，信号值在SIGRTMIN及SIGRTMAX之间的信号，只要被进程接收到就被注册）。 &lt;BR&gt;2）在信号被注销到相应的信号处理函数执行完毕这段时间内，如果进程又收到同一信号多次，则对实时信号来说，每一次都会在进程中注册；而对于非实时信号来说，无论收到多少次信号，都会视为只收到一个信号，只在进程中注册一次。&lt;/P&gt;&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;&lt;A name=2&gt;&lt;SPAN class=atitle&gt;二、信号编程注意事项&lt;/SPAN&gt;&lt;/A&gt;&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;防止不该丢失的信号丢失。如果对八中所提到的信号生命周期理解深刻的话，很容易知道信号会不会丢失，以及在哪里丢失。 
&lt;LI&gt;&lt;B&gt;程序的可移植性&lt;/B&gt; &lt;BR&gt;考虑到程序的可移植性，应该尽量采用POSIX信号函数，POSIX信号函数主要分为两类： 
&lt;UL&gt;
&lt;LI&gt;POSIX 1003.1信号函数： Kill()、sigaction()、sigaddset()、sigdelset()、sigemptyset()、sigfillset()、sigismember()、sigpending()、sigprocmask()、sigsuspend()。 
&lt;LI&gt;POSIX 1003.1b信号函数。POSIX 1003.1b在信号的实时性方面对POSIX 1003.1做了扩展，包括以下三个函数： sigqueue()、sigtimedwait()、sigwaitinfo()。其中，sigqueue主要针对信号发送，而sigtimedwait及sigwaitinfo()主要用于取代sigsuspend()函数，后面有相应实例。 
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class=code-outline&gt;&lt;PRE class=displaycode&gt;#include &amp;lt;signal.h&amp;gt;
int sigwaitinfo(sigset_t *set, siginfo_t *info).
&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;该函数与sigsuspend()类似，阻塞一个进程直到特定信号发生，但信号到来时不执行信号处理函数，而是返回信号值。因此为了避免执行相应的信号处理函数，必须在调用该函数前，使进程屏蔽掉set指向的信号，因此调用该函数的典型代码是： 
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class=code-outline&gt;&lt;PRE class=displaycode&gt;sigset_t newmask;
int rcvd_sig; 
siginfo_t info;
sigemptyset(&amp;amp;newmask);
sigaddset(&amp;amp;newmask, SIGRTMIN);
sigprocmask(SIG_BLOCK, &amp;amp;newmask, NULL);
rcvd_sig = sigwaitinfo(&amp;amp;newmask, &amp;amp;info) 
if (rcvd_sig == -1) {
	..
}
&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;调用成功返回信号值，否则返回-1。sigtimedwait()功能相似，只不过增加了一个进程等待的时间。 &lt;/LI&gt;&lt;/UL&gt;
&lt;LI&gt;&lt;B&gt;程序的稳定性。&lt;/B&gt; &lt;BR&gt;为了增强程序的稳定性，在信号处理函数中应使用可重入函数。 
&lt;P&gt;信号处理程序中应当使用可再入（可重入）函数（注：所谓可重入函数是指一个可以被多个任务调用的过程，任务在调用时不必担心数据是否会出错）。因为进程在收到信号后，就将跳转到信号处理函数去接着执行。如果信号处理函数中使用了不可重入函数，那么信号处理函数可能会修改原来进程中不应该被修改的数据，这样进程从信号处理函数中返回接着执行时，可能会出现不可预料的后果。不可再入函数在信号处理函数中被视为不安全函数。&lt;/P&gt;
&lt;P&gt;满足下列条件的函数多数是不可再入的：（1）使用静态的数据结构，如getlogin()，gmtime()，getgrgid()，getgrnam()，getpwuid()以及getpwnam()等等；（2）函数实现时，调用了malloc（）或者free()函数；（3）实现时使用了标准I/O函数的。The Open Group视下列函数为可再入的：&lt;/P&gt;&lt;CODE&gt;&lt;SPAN style=&quot;FONT-FAMILY: NSimsun&quot;&gt;_exit（）、access（）、alarm（）、cfgetispeed（）、cfgetospeed（）、cfsetispeed（）、cfsetospeed（）、chdir（）、chmod（）、chown（）、close（）、creat（）、dup（）、dup2（）、execle（）、execve（）、fcntl（）、fork（）、fpathconf（）、fstat（）、fsync（）、getegid（）、 geteuid（）、getgid（）、getgroups（）、getpgrp（）、getpid（）、getppid（）、getuid（）、kill（）、link（）、lseek（）、mkdir（）、mkfifo（）、 open（）、pathconf（）、pause（）、pipe（）、raise（）、read（）、rename（）、rmdir（）、setgid（）、setpgid（）、setsid（）、setuid（）、 sigaction（）、sigaddset（）、sigdelset（）、sigemptyset（）、sigfillset（）、sigismember（）、signal（）、sigpending（）、sigprocmask（）、sigsuspend（）、sleep（）、stat（）、sysconf（）、tcdrain（）、tcflow（）、tcflush（）、tcgetattr（）、tcgetpgrp（）、tcsendbreak（）、tcsetattr（）、tcsetpgrp（）、time（）、times（）、 umask（）、uname（）、unlink（）、utime（）、wait（）、waitpid（）、write（）。 &lt;/SPAN&gt;&lt;/CODE&gt;
&lt;P&gt;即使信号处理函数使用的都是&quot;安全函数&quot;，同样要注意进入处理函数时，首先要保存errno的值，结束时，再恢复原值。因为，信号处理过程中，errno值随时可能被改变。另外，longjmp()以及siglongjmp()没有被列为可再入函数，因为不能保证紧接着两个函数的其它调用是安全的。&lt;/P&gt;&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;&lt;BR&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;IMG height=1 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/v14/rules/blue_rule.gif&quot; width=&quot;http://mcublog.com/blog/100%&quot;&gt;&lt;BR&gt;&lt;IMG height=6 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/c.gif&quot; width=8 border=0&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;TABLE class=no-print cellSpacing=0 cellPadding=0 align=right&gt;
&lt;TBODY&gt;
&lt;TR align=right&gt;
&lt;TD&gt;&lt;IMG height=4 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/c.gif&quot; width=&quot;http://mcublog.com/blog/100%&quot;&gt;&lt;BR&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD vAlign=center&gt;&lt;BR&gt;&lt;/TD&gt;
&lt;TD vAlign=top align=right&gt;&lt;B&gt;&lt;SPAN style=&quot;COLOR: #5c81a7; TEXT-DECORATION: underline&quot;&gt;&lt;/SPAN&gt;&lt;/B&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;&lt;BR&gt;&lt;/P&gt;
&lt;P&gt;&lt;A name=3&gt;&lt;SPAN class=atitle&gt;三、深入浅出：信号应用实例&lt;/SPAN&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;linux下的信号应用并没有想象的那么恐怖，程序员所要做的最多只有三件事情：&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;安装信号（推荐使用sigaction()）； 
&lt;LI&gt;实现三参数信号处理函数，handler(int signal,struct siginfo *info, void *)； 
&lt;LI&gt;发送信号，推荐使用sigqueue()。 &lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;实际上，对有些信号来说，只要安装信号就足够了（信号处理方式采用缺省或忽略）。其他可能要做的无非是与信号集相关的几种操作。&lt;/P&gt;
&lt;P&gt;&lt;B&gt;实例一：信号发送及处理&lt;/B&gt; &lt;BR&gt;实现一个信号接收程序sigreceive（其中信号安装由sigaction（））。 &lt;/P&gt;
&lt;P&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class=code-outline&gt;&lt;PRE class=displaycode&gt;#include &amp;lt;signal.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
void new_op(int,siginfo_t*,void*);
int main(int argc,char**argv)
{
	struct sigaction act;	
	int sig;
	sig=atoi(argv[1]);
	
	sigemptyset(&amp;amp;act.sa_mask);
	act.sa_flags=SA_SIGINFO;
	act.sa_sigaction=new_op;
	
	if(sigaction(sig,&amp;amp;act,NULL) &amp;lt; 0)
	{
		printf(&quot;install sigal errorn&quot;);
	}
	
	while(1)
	{
		sleep(2);
		printf(&quot;wait for the signaln&quot;);
	}
}
void new_op(int signum,siginfo_t *info,void *myact)
{
	printf(&quot;receive signal %d&quot;, signum);
	sleep(5);
}
&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;&lt;/P&gt;
&lt;P&gt;说明，命令行参数为信号值，后台运行sigreceive signo &amp;amp;，可获得该进程的ID，假设为pid，然后再另一终端上运行kill -s signo pid验证信号的发送接收及处理。同时，可验证信号的排队问题。 &lt;BR&gt;&lt;B&gt;注：&lt;/B&gt;可以用sigqueue实现一个命令行信号发送程序sigqueuesend&lt;/P&gt;
&lt;P&gt;&lt;B&gt;实例二：信号传递附加信息&lt;/B&gt; &lt;BR&gt;主要包括两个实例： &lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;向进程本身发送信号，并传递指针参数； 
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class=code-outline&gt;&lt;PRE class=displaycode&gt;#include &amp;lt;signal.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
void new_op(int,siginfo_t*,void*);
int main(int argc,char**argv)
{
	struct sigaction act;	
	union sigval mysigval;
	int i;
	int sig;
	pid_t pid;		
	char data[10];
	memset(data,0,sizeof(data));
	for(i=0;i &amp;lt; 5;i++)
		data[i]='2';
	mysigval.sival_ptr=data;
	
	sig=atoi(argv[1]);
	pid=getpid();
	
	sigemptyset(&amp;amp;act.sa_mask);
	act.sa_sigaction=new_op;//三参数信号处理函数
	act.sa_flags=SA_SIGINFO;//信息传递开关
	if(sigaction(sig,&amp;amp;act,NULL) &amp;lt; 0)
	{
		printf(&quot;install sigal errorn&quot;);
	}
	while(1)
	{
		sleep(2);
		printf(&quot;wait for the signaln&quot;);
		sigqueue(pid,sig,mysigval);//向本进程发送信号，并传递附加信息
	}
}
void new_op(int signum,siginfo_t *info,void *myact)//三参数信号处理函数的实现
{
	int i;
	for(i=0;i&amp;lt;10;i++)
	{
		printf(&quot;%cn &quot;,(*( (char*)((*info).si_ptr)+i)));
	}
	printf(&quot;handle signal %d over;&quot;,signum);
}
&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;
&lt;P&gt;这个例子中，信号实现了附加信息的传递，信号究竟如何对这些信息进行处理则取决于具体的应用。&lt;/P&gt;
&lt;LI&gt;2、 不同进程间传递整型参数：把1中的信号发送和接收放在两个程序中，并且在发送过程中传递整型参数。 &lt;BR&gt;信号接收程序： 
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class=code-outline&gt;&lt;PRE class=displaycode&gt;#include &amp;lt;signal.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
void new_op(int,siginfo_t*,void*);
int main(int argc,char**argv)
{
	struct sigaction act;
	int sig;
	pid_t pid;		
	
	pid=getpid();
	sig=atoi(argv[1]);	
	
	sigemptyset(&amp;amp;act.sa_mask);
	act.sa_sigaction=new_op;
	act.sa_flags=SA_SIGINFO;
	if(sigaction(sig,&amp;amp;act,NULL)&amp;lt;0)
	{
		printf(&quot;install sigal errorn&quot;);
	}
	while(1)
	{
		sleep(2);
		printf(&quot;wait for the signaln&quot;);
	}
}
void new_op(int signum,siginfo_t *info,void *myact)
{
	printf(&quot;the int value is %d n&quot;,info-&amp;gt;si_int);
}
&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;
&lt;P&gt;信号发送程序：命令行第二个参数为信号值，第三个参数为接收进程ID。&lt;/P&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class=code-outline&gt;&lt;PRE class=displaycode&gt;#include &amp;lt;signal.h&amp;gt;
#include &amp;lt;sys/time.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
main(int argc,char**argv)
{
	pid_t pid;
	int signum;
	union sigval mysigval;
	signum=atoi(argv[1]);
	pid=(pid_t)atoi(argv[2]);
	mysigval.sival_int=8;//不代表具体含义，只用于说明问题
	if(sigqueue(pid,signum,mysigval)==-1)
		printf(&quot;send errorn&quot;);
	sleep(2);
}
&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;
&lt;P&gt;&lt;B&gt;注：&lt;/B&gt;实例2的两个例子侧重点在于用信号来传递信息，目前关于在linux下通过信号传递信息的实例非常少，倒是Unix下有一些，但传递的基本上都是关于传递一个整数，传递指针的我还没看到。我一直没有实现不同进程间的指针传递（实际上更有意义），也许在实现方法上存在问题吧，请实现者email我。 &lt;/P&gt;&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;&lt;B&gt;实例三：信号阻塞及信号集操作&lt;/B&gt; &lt;/P&gt;
&lt;P&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class=code-outline&gt;&lt;PRE class=displaycode&gt;#include &quot;signal.h&quot;
#include &quot;unistd.h&quot;
static void my_op(int);
main()
{
	sigset_t new_mask,old_mask,pending_mask;
	struct sigaction act;
	sigemptyset(&amp;amp;act.sa_mask);
	act.sa_flags=SA_SIGINFO;
	act.sa_sigaction=(void*)my_op;
	if(sigaction(SIGRTMIN+10,&amp;amp;act,NULL))
		printf(&quot;install signal SIGRTMIN+10 errorn&quot;);
	sigemptyset(&amp;amp;new_mask);
	sigaddset(&amp;amp;new_mask,SIGRTMIN+10);
	if(sigprocmask(SIG_BLOCK, &amp;amp;new_mask,&amp;amp;old_mask))
		printf(&quot;block signal SIGRTMIN+10 errorn&quot;);
	sleep(10);	
	printf(&quot;now begin to get pending mask and unblock SIGRTMIN+10n&quot;);
	if(sigpending(&amp;amp;pending_mask)&amp;lt;0)
		printf(&quot;get pending mask errorn&quot;);
	if(sigismember(&amp;amp;pending_mask,SIGRTMIN+10))
		printf(&quot;signal SIGRTMIN+10 is pendingn&quot;);
	if(sigprocmask(SIG_SETMASK,&amp;amp;old_mask,NULL)&amp;lt;0)
		printf(&quot;unblock signal errorn&quot;);
	printf(&quot;signal unblockedn&quot;);
	sleep(10);
}
static void my_op(int signum)
{
	printf(&quot;receive signal %d n&quot;,signum);
}
&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;&lt;/P&gt;
&lt;P&gt;编译该程序，并以后台方式运行。在另一终端向该进程发送信号(运行kill -s 42 pid，SIGRTMIN+10为42)，查看结果可以看出几个关键函数的运行机制，信号集相关操作比较简单。&lt;/P&gt;
&lt;P&gt;&lt;B&gt;注：&lt;/B&gt;在上面几个实例中，使用了printf()函数，只是作为诊断工具，pringf()函数是不可重入的，不应在信号处理函数中使用。 &lt;/P&gt;
&lt;P&gt;&lt;BR&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;IMG height=1 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/v14/rules/blue_rule.gif&quot; width=&quot;http://mcublog.com/blog/100%&quot;&gt;&lt;BR&gt;&lt;IMG height=6 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/c.gif&quot; width=8 border=0&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;TABLE class=no-print cellSpacing=0 cellPadding=0 align=right&gt;
&lt;TBODY&gt;
&lt;TR align=right&gt;
&lt;TD&gt;&lt;IMG height=4 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/c.gif&quot; width=&quot;http://mcublog.com/blog/100%&quot;&gt;&lt;BR&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD vAlign=center&gt;&lt;BR&gt;&lt;/TD&gt;
&lt;TD vAlign=top align=right&gt;&lt;B&gt;&lt;SPAN style=&quot;COLOR: #5c81a7; TEXT-DECORATION: underline&quot;&gt;&lt;/SPAN&gt;&lt;/B&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;&lt;BR&gt;&lt;/P&gt;
&lt;P&gt;&lt;A name=4&gt;&lt;SPAN class=atitle&gt;结束语：&lt;/SPAN&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;系统地对linux信号机制进行分析、总结使我受益匪浅！感谢王小乐等网友的支持！ &lt;BR&gt;Comments and suggestions are greatly welcome! &lt;/P&gt;
&lt;P&gt;&lt;BR&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;IMG height=1 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/v14/rules/blue_rule.gif&quot; width=&quot;http://mcublog.com/blog/100%&quot;&gt;&lt;BR&gt;&lt;IMG height=6 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/c.gif&quot; width=8 border=0&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;TABLE class=no-print cellSpacing=0 cellPadding=0 align=right&gt;
&lt;TBODY&gt;
&lt;TR align=right&gt;
&lt;TD&gt;&lt;IMG height=4 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/c.gif&quot; width=&quot;http://mcublog.com/blog/100%&quot;&gt;&lt;BR&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD vAlign=center&gt;&lt;BR&gt;&lt;/TD&gt;
&lt;TD vAlign=top align=right&gt;&lt;B&gt;&lt;SPAN style=&quot;COLOR: #5c81a7; TEXT-DECORATION: underline&quot;&gt;&lt;/SPAN&gt;&lt;/B&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;&lt;BR&gt;&lt;/P&gt;
&lt;P&gt;&lt;A name=5&gt;&lt;SPAN class=atitle&gt;附录1：&lt;/SPAN&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;用sigqueue实现的命令行信号发送程序sigqueuesend，命令行第二个参数是发送的信号值，第三个参数是接收该信号的进程ID，可以配合实例一使用：&lt;/P&gt;
&lt;P&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class=code-outline&gt;&lt;PRE class=displaycode&gt;#include &amp;lt;signal.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
int main(int argc,char**argv)
{
	pid_t pid;
	int sig;
	sig=atoi(argv[1]);
	pid=atoi(argv[2]);
	sigqueue(pid,sig,NULL);
	sleep(2);
}
&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/P&gt;</description><pubDate>Mon, 01 Dec 2008 17:33:00 +0800</pubDate><author>MCUBLOG</author><guid isPermaLink="false">http://www.mcublog.com/blog/user1/5/archives/2008/29808.html</guid><dc:creator>MCUBLOG</dc:creator><fs:srclink>http://www.mcublog.com/blog/user1/5/archives/2008/29808.html</fs:srclink><fs:srcfeed>http://www.mcublog.com/blog/user1/5/rss2.xml</fs:srcfeed><fs:itemid>mcublog.com/user1/5/~691605/147011331/691605</fs:itemid></item><item><title>Unix环境高级编程之Signal (上)</title><link>http://item.feedsky.com/~mcublog.com/user1/5/~691605/147011332/691605/1/item.html</link><description>&lt;P&gt;&lt;A name=1&gt;&lt;SPAN class=atitle&gt;一、信号及信号来源&lt;/SPAN&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;B&gt;信号本质&lt;/B&gt; &lt;/P&gt;
&lt;P&gt;信号是在软件层次上对中断机制的一种模拟，在原理上，一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的，一个进程不必通过任何操作来等待信号的到达，事实上，进程也不知道信号到底什么时候到达。&lt;/P&gt;
&lt;P&gt;信号是进程间通信机制中唯一的异步通信机制，可以看作是异步通知，通知接收信号的进程有哪些事情发生了。信号机制经过POSIX实时扩展后，功能更加强大，除了基本通知功能外，还可以传递附加信息。&lt;/P&gt;
&lt;P&gt;&lt;B&gt;信号来源&lt;/B&gt; &lt;/P&gt;
&lt;P&gt;信号事件的发生有两个来源：硬件来源(比如我们按下了键盘或者其它硬件故障)；软件来源，最常用发送信号的系统函数是kill, raise, alarm和setitimer以及sigqueue函数，软件来源还包括一些非法运算等操作。&lt;/P&gt;&lt;BR&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;IMG height=1 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/v14/rules/blue_rule.gif&quot; width=&quot;http://mcublog.com/blog/100%&quot;&gt;&lt;BR&gt;&lt;IMG height=6 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/c.gif&quot; width=8 border=0&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;TABLE class=no-print cellSpacing=0 cellPadding=0 align=right&gt;
&lt;TBODY&gt;
&lt;TR align=right&gt;
&lt;TD&gt;&lt;IMG height=4 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/c.gif&quot; width=&quot;http://mcublog.com/blog/100%&quot;&gt;&lt;BR&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD vAlign=center&gt;&lt;IMG height=16 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/v14/icons/u_bold.gif&quot; width=16 border=0&gt;&lt;BR&gt;&lt;/TD&gt;
&lt;TD vAlign=top align=right&gt;&lt;A class=fbox href=&quot;http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index1.html#main&quot; cmImpressionSent=&quot;http://mcublog.com/blog/1&quot;&gt;&lt;B&gt;&lt;SPAN style=&quot;COLOR: #5c81a7; TEXT-DECORATION: underline&quot;&gt;回页首&lt;/SPAN&gt;&lt;/B&gt;&lt;/A&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;&lt;BR&gt;
&lt;P&gt;&lt;A name=2&gt;&lt;SPAN class=atitle&gt;二、信号的种类&lt;/SPAN&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;可以从两个不同的分类角度对信号进行分类：（1）可靠性方面：可靠信号与不可靠信号；（2）与时间的关系上：实时信号与非实时信号。在《Linux环境进程间通信（一）：管道及有名管道》的附1中列出了系统所支持的所有信号。&lt;/P&gt;
&lt;P&gt;&lt;A name=N10060&gt;&lt;SPAN class=smalltitle&gt;&lt;STRONG&gt;&lt;SPAN style=&quot;FONT-SIZE: 12pt; FONT-FAMILY: Arial&quot;&gt;1、可靠信号与不可靠信号&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/SPAN&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&quot;不可靠信号&quot;&lt;/STRONG&gt; &lt;/P&gt;
&lt;P&gt;Linux信号机制基本上是从Unix系统中继承过来的。早期Unix系统中的信号机制比较简单和原始，后来在实践中暴露出一些问题，因此，把那些建立在早期机制上的信号叫做&quot;不可靠信号&quot;，信号值小于SIGRTMIN(Red hat 7.2中，SIGRTMIN=32，SIGRTMAX=63)的信号都是不可靠信号。这就是&quot;不可靠信号&quot;的来源。它的主要问题是：&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;进程每次处理信号后，就将对信号的响应设置为默认动作。在某些情况下，将导致对信号的错误处理；因此，用户如果不希望这样的操作，那么就要在信号处理函数结尾再一次调用signal()，重新安装该信号。 
&lt;LI&gt;信号可能丢失，后面将对此详细阐述。 &lt;BR&gt;因此，早期unix下的不可靠信号主要指的是进程可能对信号做出错误的反应以及信号可能丢失。 &lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Linux支持不可靠信号，但是对不可靠信号机制做了改进：在调用完信号处理函数后，不必重新调用该信号的安装函数（信号安装函数是在可靠机制上的实现）。因此，Linux下的不可靠信号问题主要指的是信号可能丢失。&lt;/P&gt;
&lt;P&gt;&lt;B&gt;&quot;可靠信号&quot;&lt;/B&gt; &lt;/P&gt;
&lt;P&gt;随着时间的发展，实践证明了有必要对信号的原始机制加以改进和扩充。所以，后来出现的各种Unix版本分别在这方面进行了研究，力图实现&quot;可靠信号&quot;。由于原来定义的信号已有许多应用，不好再做改动，最终只好又新增加了一些信号，并在一开始就把它们定义为可靠信号，这些信号支持排队，不会丢失。同时，信号的发送和安装也出现了新版本：信号发送函数sigqueue()及信号安装函数sigaction()。POSIX.4对可靠信号机制做了标准化。但是，POSIX只对可靠信号机制应具有的功能以及信号机制的对外接口做了标准化，对信号机制的实现没有作具体的规定。&lt;/P&gt;
&lt;P&gt;信号值位于SIGRTMIN和SIGRTMAX之间的信号都是可靠信号，可靠信号克服了信号可能丢失的问题。Linux在支持新版本的信号安装函数sigation（）以及信号发送函数sigqueue()的同时，仍然支持早期的signal（）信号安装函数，支持信号发送函数kill()。&lt;/P&gt;
&lt;P&gt;注：不要有这样的误解：由sigqueue()发送、sigaction安装的信号就是可靠的。事实上，可靠信号是指后来添加的新信号（信号值位于SIGRTMIN及SIGRTMAX之间）；不可靠信号是信号值小于SIGRTMIN的信号。信号的可靠与不可靠只与信号值有关，与信号的发送及安装函数无关。目前linux中的signal()是通过sigation()函数实现的，因此，即使通过signal（）安装的信号，在信号处理函数的结尾也不必再调用一次信号安装函数。同时，由signal()安装的实时信号支持排队，同样不会丢失。&lt;/P&gt;
&lt;P&gt;对于目前linux的两个信号安装函数:signal()及sigaction()来说，它们都不能把SIGRTMIN以前的信号变成可靠信号（都不支持排队，仍有可能丢失，仍然是不可靠信号），而且对SIGRTMIN以后的信号都支持排队。这两个函数的最大区别在于，经过sigaction安装的信号都能传递信息给信号处理函数（对所有信号这一点都成立），而经过signal安装的信号却不能向信号处理函数传递信息。对于信号发送函数来说也是一样的。&lt;/P&gt;
&lt;P&gt;&lt;A name=N1008F&gt;&lt;SPAN class=smalltitle&gt;&lt;STRONG&gt;&lt;SPAN style=&quot;FONT-SIZE: 12pt; FONT-FAMILY: Arial&quot;&gt;2、实时信号与非实时信号&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/SPAN&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;早期Unix系统只定义了32种信号，Ret hat7.2支持64种信号，编号0-63(SIGRTMIN=31，SIGRTMAX=63)，将来可能进一步增加，这需要得到内核的支持。前32种信号已经有了预定义值，每个信号有了确定的用途及含义，并且每种信号都有各自的缺省动作。如按键盘的CTRL ^C时，会产生SIGINT信号，对该信号的默认反应就是进程终止。后32个信号表示实时信号，等同于前面阐述的可靠信号。这保证了发送的多个实时信号都被接收。实时信号是POSIX标准的一部分，可用于应用进程。&lt;/P&gt;
&lt;P&gt;非实时信号都不支持排队，都是不可靠信号；实时信号都支持排队，都是可靠信号。&lt;/P&gt;
&lt;TABLE class=no-print cellSpacing=0 cellPadding=0 align=right&gt;
&lt;TBODY&gt;
&lt;TR align=right&gt;
&lt;TD&gt;&lt;IMG height=4 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/c.gif&quot; width=&quot;http://mcublog.com/blog/100%&quot;&gt;&lt;BR&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD vAlign=center&gt;&lt;BR&gt;&lt;/TD&gt;
&lt;TD vAlign=top align=right&gt;&lt;B&gt;&lt;SPAN style=&quot;COLOR: #5c81a7; TEXT-DECORATION: underline&quot;&gt;&lt;/SPAN&gt;&lt;/B&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;&lt;BR&gt;
&lt;P&gt;&lt;A name=3&gt;&lt;SPAN class=atitle&gt;三、进程对信号的响应&lt;/SPAN&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;进程可以通过三种方式来响应一个信号：（1）忽略信号，即对信号不做任何处理，其中，有两个信号不能忽略：SIGKILL及SIGSTOP；（2）捕捉信号。定义信号处理函数，当信号发生时，执行相应的处理函数；（3）执行缺省操作，Linux对每种信号都规定了默认操作，详细情况请参考[2]以及其它资料。注意，进程对实时信号的缺省反应是进程终止。&lt;/P&gt;
&lt;P&gt;Linux究竟采用上述三种方式的哪一个来响应信号，取决于传递给相应API函数的参数。&lt;/P&gt;&lt;BR&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;IMG height=1 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/v14/rules/blue_rule.gif&quot; width=&quot;http://mcublog.com/blog/100%&quot;&gt;&lt;BR&gt;&lt;IMG height=6 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/c.gif&quot; width=8 border=0&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;TABLE class=no-print cellSpacing=0 cellPadding=0 align=right&gt;
&lt;TBODY&gt;
&lt;TR align=right&gt;
&lt;TD&gt;&lt;IMG height=4 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/c.gif&quot; width=&quot;http://mcublog.com/blog/100%&quot;&gt;&lt;BR&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD vAlign=center&gt;&lt;/TD&gt;
&lt;TD vAlign=top align=right&gt;&lt;A class=fbox href=&quot;http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index1.html#main&quot; cmImpressionSent=&quot;http://mcublog.com/blog/1&quot;&gt;&lt;B&gt;&lt;SPAN style=&quot;COLOR: #5c81a7&quot;&gt;&lt;/SPAN&gt;&lt;/B&gt;&lt;/A&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;P&gt;&lt;A name=4&gt;&lt;SPAN class=atitle&gt;四、信号的发送&lt;/SPAN&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;发送信号的主要函数有：kill()、raise()、 sigqueue()、alarm()、setitimer()以及abort()。&lt;/P&gt;
&lt;P&gt;1、kill() &lt;BR&gt;#include &amp;lt;sys/types.h&amp;gt; &lt;BR&gt;#include &amp;lt;signal.h&amp;gt; &lt;BR&gt;int kill(pid_t pid,int signo) &lt;BR&gt;&lt;/P&gt;
&lt;TABLE width=&quot;60%&quot; border=1&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD&gt;参数pid的值&lt;/TD&gt;
&lt;TD&gt;信号的接收进程&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;pid&amp;gt;0&lt;/TD&gt;
&lt;TD&gt;进程ID为pid的进程&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;pid=0&lt;/TD&gt;
&lt;TD&gt;同一个进程组的进程&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;pid&amp;lt;0 pid!=-1&lt;/TD&gt;
&lt;TD&gt;进程组ID为 -pid的所有进程&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;pid=-1&lt;/TD&gt;
&lt;TD&gt;除发送进程自身外，所有进程ID大于1的进程&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;P&gt;Sinno是信号值，当为0时（即空信号），实际不发送任何信号，但照常进行错误检查，因此，可用于检查目标进程是否存在，以及当前进程是否具有向目标发送信号的权限（root权限的进程可以向任何进程发送信号，非root权限的进程只能向属于同一个session或者同一个用户的进程发送信号）。&lt;/P&gt;
&lt;P&gt;Kill()最常用于pid&amp;gt;0时的信号发送，调用成功返回 0； 否则，返回 -1。注：对于pid&amp;lt;0时的情况，对于哪些进程将接受信号，各种版本说法不一，其实很简单，参阅内核源码kernal/signal.c即可，上表中的规则是参考red hat 7.2。&lt;/P&gt;
&lt;P&gt;2、raise（） &lt;BR&gt;#include &amp;lt;signal.h&amp;gt; &lt;BR&gt;int raise(int signo) &lt;BR&gt;向进程本身发送信号，参数为即将发送的信号值。调用成功返回 0；否则，返回 -1。 &lt;/P&gt;
&lt;P&gt;3、sigqueue（） &lt;BR&gt;#include &amp;lt;sys/types.h&amp;gt; &lt;BR&gt;#include &amp;lt;signal.h&amp;gt; &lt;BR&gt;int sigqueue(pid_t pid, int sig, const union sigval val) &lt;BR&gt;调用成功返回 0；否则，返回 -1。 &lt;/P&gt;
&lt;P&gt;sigqueue()是比较新的发送信号系统调用，主要是针对实时信号提出的（当然也支持前32种），支持信号带有参数，与函数sigaction()配合使用。&lt;/P&gt;
&lt;P&gt;sigqueue的第一个参数是指定接收信号的进程ID，第二个参数确定即将发送的信号，第三个参数是一个联合数据结构union sigval，指定了信号传递的参数，即通常所说的4字节值。&lt;/P&gt;
&lt;P&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class=code-outline&gt;&lt;PRE class=displaycode&gt; 	typedef union sigval {
 		int  sival_int;
 		void *sival_ptr;
 	}sigval_t;
&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;&lt;/P&gt;
&lt;P&gt;sigqueue()比kill()传递了更多的附加信息，但sigqueue()只能向一个进程发送信号，而不能发送信号给一个进程组。如果signo=0，将会执行错误检查，但实际上不发送任何信号，0值信号可用于检查pid的有效性以及当前进程是否有权限向目标进程发送信号。&lt;/P&gt;
&lt;P&gt;在调用sigqueue时，sigval_t指定的信息会拷贝到3参数信号处理函数（3参数信号处理函数指的是信号处理函数由sigaction安装，并设定了sa_sigaction指针，稍后将阐述）的siginfo_t结构中，这样信号处理函数就可以处理这些信息了。由于sigqueue系统调用支持发送带参数信号，所以比kill()系统调用的功能要灵活和强大得多。&lt;/P&gt;
&lt;P&gt;注：sigqueue（）发送非实时信号时，第三个参数包含的信息仍然能够传递给信号处理函数； sigqueue（）发送非实时信号时，仍然不支持排队，即在信号处理函数执行过程中到来的所有相同信号，都被合并为一个信号。&lt;/P&gt;
&lt;P&gt;4、alarm（） &lt;BR&gt;#include &amp;lt;unistd.h&amp;gt; &lt;BR&gt;unsigned int alarm(unsigned int seconds) &lt;BR&gt;专门为SIGALRM信号而设，在指定的时间seconds秒后，将向进程本身发送SIGALRM信号，又称为闹钟时间。进程调用alarm后，任何以前的alarm()调用都将无效。如果参数seconds为零，那么进程内将不再包含任何闹钟时间。 &lt;BR&gt;返回值，如果调用alarm（）前，进程中已经设置了闹钟时间，则返回上一个闹钟时间的剩余时间，否则返回0。 &lt;/P&gt;
&lt;P&gt;5、setitimer（） &lt;BR&gt;#include &amp;lt;sys/time.h&amp;gt; &lt;BR&gt;int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue)); &lt;BR&gt;setitimer()比alarm功能强大，支持3种类型的定时器： &lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;ITIMER_REAL： 设定绝对时间；经过指定的时间后，内核将发送SIGALRM信号给本进程； 
&lt;LI&gt;ITIMER_VIRTUAL 设定程序执行时间；经过指定的时间后，内核将发送SIGVTALRM信号给本进程； 
&lt;LI&gt;ITIMER_PROF 设定进程执行以及内核因本进程而消耗的时间和，经过指定的时间后，内核将发送ITIMER_VIRTUAL信号给本进程； &lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Setitimer()第一个参数which指定定时器类型（上面三种之一）；第二个参数是结构itimerval的一个实例，结构itimerval形式见附录1。第三个参数可不做处理。&lt;/P&gt;
&lt;P&gt;Setitimer()调用成功返回0，否则返回-1。&lt;/P&gt;
&lt;P&gt;6、abort() &lt;BR&gt;#include &amp;lt;stdlib.h&amp;gt; &lt;BR&gt;void abort(void); &lt;/P&gt;
&lt;P&gt;向进程发送SIGABORT信号，默认情况下进程会异常退出，当然可定义自己的信号处理函数。即使SIGABORT被进程设置为阻塞信号，调用abort()后，SIGABORT仍然能被进程接收。该函数无返回值。&lt;/P&gt;
&lt;P&gt;&lt;BR&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;IMG height=1 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/v14/rules/blue_rule.gif&quot; width=&quot;http://mcublog.com/blog/100%&quot;&gt;&lt;BR&gt;&lt;IMG height=6 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/c.gif&quot; width=8 border=0&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;TABLE class=no-print cellSpacing=0 cellPadding=0 align=right&gt;
&lt;TBODY&gt;
&lt;TR align=right&gt;
&lt;TD&gt;&lt;IMG height=4 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/c.gif&quot; width=&quot;http://mcublog.com/blog/100%&quot;&gt;&lt;BR&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD vAlign=center&gt;&lt;BR&gt;&lt;/TD&gt;
&lt;TD vAlign=top align=right&gt;&lt;B&gt;&lt;SPAN style=&quot;COLOR: #5c81a7&quot;&gt;&lt;/SPAN&gt;&lt;/B&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/P&gt;
&lt;P&gt;&lt;A name=5&gt;&lt;SPAN class=atitle&gt;五、信号的安装（设置信号关联动作）&lt;/SPAN&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;如果进程要处理某一信号，那么就要在进程中安装该信号。安装信号主要用来确定信号值及进程针对该信号值的动作之间的映射关系，即进程将要处理哪个信号；该信号被传递给进程时，将执行何种操作。&lt;/P&gt;
&lt;P&gt;linux主要有两个函数实现信号的安装：signal()、sigaction()。其中signal()在可靠信号系统调用的基础上实现, 是库函数。它只有两个参数，不支持信号传递信息，主要是用于前32种非实时信号的安装；而sigaction()是较新的函数（由两个系统调用实现：sys_signal以及sys_rt_sigaction），有三个参数，支持信号传递信息，主要用来与 sigqueue() 系统调用配合使用，当然，sigaction()同样支持非实时信号的安装。sigaction()优于signal()主要体现在支持信号带有参数。&lt;/P&gt;
&lt;P&gt;1、signal() &lt;BR&gt;#include &amp;lt;signal.h&amp;gt; &lt;BR&gt;void (*signal(int signum, void (*handler))(int)))(int); &lt;BR&gt;如果该函数原型不容易理解的话，可以参考下面的分解方式来理解： &lt;BR&gt;typedef void (*sighandler_t)(int)； &lt;BR&gt;sighandler_t signal(int signum, sighandler_t handler)); &lt;BR&gt;第一个参数指定信号的值，第二个参数指定针对前面信号值的处理，可以忽略该信号（参数设为SIG_IGN）；可以采用系统默认方式处理信号(参数设为SIG_DFL)；也可以自己实现处理方式(参数指定一个函数地址)。 &lt;BR&gt;如果signal()调用成功，返回最后一次为安装信号signum而调用signal()时的handler值；失败则返回SIG_ERR。 &lt;/P&gt;
&lt;P&gt;2、sigaction() &lt;BR&gt;#include &amp;lt;signal.h&amp;gt; &lt;BR&gt;int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact)); &lt;/P&gt;
&lt;P&gt;sigaction函数用于改变进程接收到特定信号后的行为。该函数的第一个参数为信号的值，可以为除SIGKILL及SIGSTOP外的任何一个特定有效的信号（为这两个信号定义自己的处理函数，将导致信号安装错误）。第二个参数是指向结构sigaction的一个实例的指针，在结构sigaction的实例中，指定了对特定信号的处理，可以为空，进程会以缺省方式对信号处理；第三个参数oldact指向的对象用来保存原来对相应信号的处理，可指定oldact为NULL。如果把第二、第三个参数都设为NULL，那么该函数可用于检查信号的有效性。&lt;/P&gt;
&lt;P&gt;第二个参数最为重要，其中包含了对指定信号的处理、信号所传递的信息、信号处理函数执行过程中应屏蔽掉哪些函数等等。&lt;/P&gt;
&lt;P&gt;sigaction结构定义如下：&lt;/P&gt;
&lt;P&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class=code-outline&gt;&lt;PRE class=displaycode&gt;	struct sigaction {
					union{
						__sighandler_t _sa_handler;
						void (*_sa_sigaction)(int,struct siginfo *, void *)；
&lt;!-- code sample is too wide --&gt;						}_u
                   	sigset_t sa_mask；
                  	unsigned long sa_flags； 
    	          	void (*sa_restorer)(void)；
        	      	}
					&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;&lt;/P&gt;
&lt;P&gt;其中，sa_restorer，已过时，POSIX不支持它，不应再被使用。&lt;/P&gt;
&lt;P&gt;1、联合数据结构中的两个元素_sa_handler以及*_sa_sigaction指定信号关联函数，即用户指定的信号处理函数。除了可以是用户自定义的处理函数外，还可以为SIG_DFL(采用缺省的处理方式)，也可以为SIG_IGN（忽略信号）。&lt;/P&gt;
&lt;P&gt;2、由_sa_handler指定的处理函数只有一个参数，即信号值，所以信号不能传递除信号值之外的任何信息；由_sa_sigaction是指定的信号处理函数带有三个参数，是为实时信号而设的（当然同样支持非实时信号），它指定一个3参数信号处理函数。第一个参数为信号值，第三个参数没有使用（posix没有规范使用该参数的标准），第二个参数是指向siginfo_t结构的指针，结构中包含信号携带的数据值，参数所指向的结构如下：&lt;/P&gt;
&lt;P&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class=code-outline&gt;&lt;PRE class=displaycode&gt;siginfo_t {
                  int      si_signo;  /* 信号值，对所有信号有意义*/
                  int      si_errno;  /* errno值，对所有信号有意义*/
                  int      si_code;   /* 信号产生的原因，对所有信号有意义*/
				union{				  /* 联合数据结构，不同成员适应不同信号 */	
&lt;!-- code sample is too wide --&gt;					//确保分配足够大的存储空间
					int _pad[SI_PAD_SIZE];
					//对SIGKILL有意义的结构
					struct{
							...
						  }...
						... ...
						... ...					
					//对SIGILL, SIGFPE, SIGSEGV, SIGBUS有意义的结构
      				struct{
							...
						  }...
						... ...
					  }
			}
			&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;&lt;/P&gt;
&lt;P&gt;注：为了更便于阅读，在说明问题时常把该结构表示为附录2所表示的形式。&lt;/P&gt;
&lt;P&gt;siginfo_t结构中的联合数据成员确保该结构适应所有的信号，比如对于实时信号来说，则实际采用下面的结构形式：&lt;/P&gt;
&lt;P&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class=code-outline&gt;&lt;PRE class=displaycode&gt;	typedef struct {
		int si_signo;
		int si_errno;			
		int si_code;			
		union sigval si_value;	
		} siginfo_t;
		&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;&lt;/P&gt;
&lt;P&gt;结构的第四个域同样为一个联合数据结构：&lt;/P&gt;
&lt;P&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class=code-outline&gt;&lt;PRE class=displaycode&gt;	union sigval {
		int sival_int;		
		void *sival_ptr;	
		}
&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;&lt;/P&gt;
&lt;P&gt;采用联合数据结构，说明siginfo_t结构中的si_value要么持有一个4字节的整数值，要么持有一个指针，这就构成了与信号相关的数据。在信号的处理函数中，包含这样的信号相关数据指针，但没有规定具体如何对这些数据进行操作，操作方法应该由程序开发人员根据具体任务事先约定。&lt;/P&gt;
&lt;P&gt;前面在讨论系统调用sigqueue发送信号时，sigqueue的第三个参数就是sigval联合数据结构，当调用sigqueue时，该数据结构中的数据就将拷贝到信号处理函数的第二个参数中。这样，在发送信号同时，就可以让信号传递一些附加信息。信号可以传递信息对程序开发是非常有意义的。&lt;/P&gt;
&lt;P&gt;信号参数的传递过程可图示如下：&lt;/P&gt;
&lt;P&gt;&lt;BR&gt;&lt;IMG height=178 alt=&quot;&quot; src=&quot;http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/2.gif&quot; width=586 border=0&gt; &lt;BR&gt;&lt;/P&gt;
&lt;P&gt;3、sa_mask指定在信号处理程序执行过程中，哪些信号应当被阻塞。缺省情况下当前信号本身被阻塞，防止信号的嵌套发送，除非指定SA_NODEFER或者SA_NOMASK标志位。&lt;/P&gt;
&lt;P&gt;注：请注意sa_mask指定的信号阻塞的前提条件，是在由sigaction（）安装信号的处理函数执行过程中由sa_mask指定的信号才被阻塞。&lt;/P&gt;
&lt;P&gt;4、sa_flags中包含了许多标志位，包括刚刚提到的SA_NODEFER及SA_NOMASK标志位。另一个比较重要的标志位是SA_SIGINFO，当设定了该标志位时，表示信号附带的参数可以被传递到信号处理函数中，因此，应该为sigaction结构中的sa_sigaction指定处理函数，而不应该为sa_handler指定信号处理函数，否则，设置该标志变得毫无意义。即使为sa_sigaction指定了信号处理函数，如果不设置SA_SIGINFO，信号处理函数同样不能得到信号传递过来的数据，在信号处理函数中对这些信息的访问都将导致段错误（Segmentation fault）。&lt;/P&gt;
&lt;P&gt;注：很多文献在阐述该标志位时都认为，如果设置了该标志位，就必须定义三参数信号处理函数。实际不是这样的，验证方法很简单：自己实现一个单一参数信号处理函数，并在程序中设置该标志位，可以察看程序的运行结果。实际上，可以把该标志位看成信号是否传递参数的开关，如果设置该位，则传递参数；否则，不传递参数。&lt;/P&gt;
&lt;P&gt;&lt;BR&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;IMG height=1 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/v14/rules/blue_rule.gif&quot; width=&quot;http://mcublog.com/blog/100%&quot;&gt;&lt;BR&gt;&lt;IMG height=6 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/c.gif&quot; width=8 border=0&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;&lt;BR&gt;&lt;/P&gt;
&lt;P&gt;&lt;A name=6&gt;&lt;SPAN class=atitle&gt;六、信号集及信号集操作函数：&lt;/SPAN&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;信号集被定义为一种数据类型：&lt;/P&gt;
&lt;P&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class=code-outline&gt;&lt;PRE class=displaycode&gt;	typedef struct {
			unsigned long sig[_NSIG_WORDS]；
			} sigset_t
&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;&lt;/P&gt;
&lt;P&gt;信号集用来描述信号的集合，linux所支持的所有信号可以全部或部分的出现在信号集中，主要与信号阻塞相关函数配合使用。下面是为信号集操作定义的相关函数：&lt;/P&gt;
&lt;P&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class=code-outline&gt;&lt;PRE class=displaycode&gt;	#include &amp;lt;signal.h&amp;gt;
int sigemptyset(sigset_t *set)；
int sigfillset(sigset_t *set)；
int sigaddset(sigset_t *set, int signum)
int sigdelset(sigset_t *set, int signum)；
int sigismember(const sigset_t *set, int signum)；
sigemptyset(sigset_t *set)初始化由set指定的信号集，信号集里面的所有信号被清空；
sigfillset(sigset_t *set)调用该函数后，set指向的信号集中将包含linux支持的64种信号；
sigaddset(sigset_t *set, int signum)在set指向的信号集中加入signum信号；
sigdelset(sigset_t *set, int signum)在set指向的信号集中删除signum信号；
sigismember(const sigset_t *set, int signum)判定信号signum是否在set指向的信号集中。
&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;&lt;BR&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;IMG height=1 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/v14/rules/blue_rule.gif&quot; width=&quot;http://mcublog.com/blog/100%&quot;&gt;&lt;BR&gt;&lt;IMG height=6 alt=&quot;&quot; src=&quot;http://www.ibm.com/i/c.gif&quot; width=8 border=0&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;TABLE class=no-print cellSpacing=0 cellPadding=0 align=right&gt;
&lt;TBODY&gt;
&lt;TR align=right&gt;
&lt;TD&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/P&gt;
&lt;P&gt;&lt;A name=7&gt;&lt;SPAN class=atitle&gt;七、信号阻塞与信号未决:&lt;/SPAN&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;每个进程都有一个用来描述哪些信号递送到进程时将被阻塞的信号集，该信号集中的所有信号在递送到进程后都将被阻塞。下面是与信号阻塞相关的几个函数：&lt;/P&gt;
&lt;P&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class=code-outline&gt;&lt;PRE class=displaycode&gt;#include &amp;lt;signal.h&amp;gt;
int  sigprocmask(int  how,  const  sigset_t *set, sigset_t *oldset))；
int sigpending(sigset_t *set));
int sigsuspend(const sigset_t *mask))；
&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;&lt;/P&gt;
&lt;P&gt;sigprocmask()函数能够根据参数how来实现对信号集的操作，操作主要有三种：&lt;/P&gt;
&lt;P&gt;
&lt;TABLE width=&quot;70%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD&gt;参数how&lt;/TD&gt;
&lt;TD&gt;进程当前信号集&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;SIG_BLOCK&lt;/TD&gt;
&lt;TD&gt;在进程当前阻塞信号集中添加set指向信号集中的信号&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;SIG_UNBLOCK&lt;/TD&gt;
&lt;TD&gt;如果进程阻塞信号集中包含set指向信号集中的信号，则解除对该信号的阻塞&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;SIG_SETMASK&lt;/TD&gt;
&lt;TD&gt;更新进程阻塞信号集为set指向的信号集&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/P&gt;
&lt;P&gt;sigpending(sigset_t *set))获得当前已递送到进程，却被阻塞的所有信号，在set指向的信号集中返回结果。&lt;/P&gt;
&lt;P&gt;sigsuspend(const sigset_t *mask))用于在接收到某个信号之前, 临时用mask替换进程的信号掩码, 并暂停进程执行，直到收到信号为止。sigsuspend 返回后将恢复调用之前的信号掩码。信号处理函数完成后，进程将继续执行。该系统调用始终返回-1，并将errno设置为EINTR。&lt;/P&gt;
&lt;P&gt;附录1：结构itimerval：&lt;/P&gt;
&lt;P&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class=code-outline&gt;&lt;PRE class=displaycode&gt;            struct itimerval {
                struct timeval it_interval; /* next value */
                struct timeval it_value;    /* current value */
            };
            struct timeval {
                long tv_sec;                /* seconds */
                long tv_usec;               /* microseconds */
            };
&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;&lt;/P&gt;
&lt;P&gt;附录2：三参数信号处理函数中第二个参数的说明性描述：&lt;/P&gt;
&lt;P&gt;
&lt;TABLE cellSpacing=0 cellPadding=0 width=&quot;100%&quot; border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class=code-outline&gt;&lt;PRE class=displaycode&gt;siginfo_t {
int      si_signo;  /* 信号值，对所有信号有意义*/
int      si_errno;  /* errno值，对所有信号有意义*/
int      si_code;   /* 信号产生的原因，对所有信号有意义*/
pid_t    si_pid;    /* 发送信号的进程ID,对kill(2),实时信号以及SIGCHLD有意义 */
uid_t    si_uid;    /* 发送信号进程的真实用户ID，对kill(2),实时信号以及SIGCHLD有意义 */
int      si_status; /* 退出状态，对SIGCHLD有意义*/
clock_t  si_utime;  /* 用户消耗的时间，对SIGCHLD有意义 */
clock_t  si_stime;  /* 内核消耗的时间，对SIGCHLD有意义 */
sigval_t si_value;  /* 信号值，对所有实时有意义，是一个联合数据结构，可以为一个整数（由si_int标示，也可以为一个指针，由si_ptr标示）*/
	
void *   si_addr;   /* 触发fault的内存地址，对SIGILL,SIGFPE,SIGSEGV,SIGBUS 信号有意义*/
int      si_band;   /* 对SIGPOLL信号有意义 */
int      si_fd;     /* 对SIGPOLL信号有意义 */
}
&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;BR&gt;&lt;/P&gt;
&lt;P&gt;实际上，除了前三个元素外，其他元素组织在一个联合结构中，在联合数据结构中，又根据不同的信号组织成不同的结构。注释中提到的对某种信号有意义指的是，在该信号的处理函数中可以访问这些域来获得与信号相关的有意义的信息，只不过特定信号只对特定信息感兴趣而已。&lt;/P&gt;</description><pubDate>Mon, 01 Dec 2008 17:31:00 +0800</pubDate><author>MCUBLOG</author><guid isPermaLink="false">http://www.mcublog.com/blog/user1/5/archives/2008/29807.html</guid><dc:creator>MCUBLOG</dc:creator><fs:srclink>http://www.mcublog.com/blog/user1/5/archives/2008/29807.html</fs:srclink><fs:srcfeed>http://www.mcublog.com/blog/user1/5/rss2.xml</fs:srcfeed><fs:itemid>mcublog.com/user1/5/~691605/147011332/691605</fs:itemid></item><item><title>Linux下的多线程编程</title><link>http://item.feedsky.com/~mcublog.com/user1/5/~691605/147011333/691605/1/item.html</link><description>&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&lt;B&gt;1 引言&lt;/B&gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;　　线程（thread）技术早在60年代就被提出，但真正应用多线程到操作系统中去，是在80年代中期，solaris是这方面的佼佼者。传统的Unix也支持线程的概念，但是在一个进程（process）中只允许有一个线程，这样多线程就意味着多进程。现在，多线程技术已经被许多操作系统所支持，包括Windows/NT，当然，也包括Linux。&lt;BR&gt;　　为什么有了进程的概念后，还要再引入线程呢？使用多线程到底有哪些好处？什么的系统应该选用多线程？我们首先必须回答这些问题。&lt;BR&gt;　　使用多线程的理由之一是和进程相比，它是一种非常&quot;节俭&quot;的多任务操作方式。我们知道，在Linux系统下，启动一个新的进程必须分配给它独立的地址空间，建立众多的数据表来维护它的代码段、堆栈段和数据段，这是一种&quot;昂贵&quot;的多任务工作方式。而运行于一个进程中的多个线程，它们彼此之间使用相同的地址空间，共享大部分数据，启动一个线程所花费的空间远远小于启动一个进程所花费的空间，而且，线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计，总的说来，一个进程的开销大约是一个线程开销的30倍左右，当然，在具体的系统上，这个数据可能会有较大的区别。&lt;BR&gt;　　使用多线程的理由之二是线程间方便的通信机制。对不同进程来说，它们具有独立的数据空间，要进行数据的传递只能通过通信的方式进行，这种方式不仅费时，而且很不方便。线程则不然，由于同一进程下的线程之间共享数据空间，所以一个线程的数据可以直接为其它线程所用，这不仅快捷，而且方便。当然，数据的共享也带来其他一些问题，有的变量不能同时被两个线程所修改，有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击，这些正是编写多线程程序时最需要注意的地方。&lt;BR&gt;　　除了以上所说的优点外，不和进程比较，多线程程序作为一种多任务、并发的工作方式，当然有以下的优点：&lt;BR&gt;　　1) 提高应用程序响应。这对图形界面的程序尤其有意义，当一个操作耗时很长时，整个系统都会等待这个操作，此时程序不会响应键盘、鼠标、菜单的操作，而使用多线程技术，将耗时长的操作（time consuming）置于一个新的线程，可以避免这种尴尬的情况。&lt;BR&gt;　　2) 使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时，不同的线程运行于不同的CPU上。&lt;BR&gt;　　3) 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程，成为几个独立或半独立的运行部分，这样的程序会利于理解和修改。&lt;BR&gt;　　下面我们先来尝试编写一个简单的多线程程序。&lt;BR&gt;&lt;BR&gt;&lt;/SPAN&gt;&lt;B&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;2 简单的多线程编程&lt;/B&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&lt;/SPAN&gt;&lt;/B&gt;&lt;BR&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;　　Linux系统下的多线程遵循POSIX线程接口，称为pthread。编写Linux下的多线程程序，需要使用头文件pthread.h，连接时需要使用库libpthread.a。顺便说一下，Linux下pthread的实现是通过系统调用clone（）来实现的。clone（）是Linux所特有的系统调用，它的使用方式类似fork，关于clone（）的详细情况，有兴趣的读者可以去查看有关文档说明。下面我们展示一个最简单的多线程程序example1.c。 &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;
&lt;P&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;/* example.c*/&lt;BR&gt;#include &amp;lt;stdio.h&amp;gt;&lt;BR&gt;#include &amp;lt;pthread.h&amp;gt;&lt;BR&gt;void thread(void)&lt;BR&gt;{&lt;BR&gt;int i;&lt;BR&gt;for(i=0;i&amp;lt;3;i++)&lt;BR&gt;printf(&quot;This is a pthread.\n&quot;);&lt;BR&gt;}&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;int main(void)&lt;BR&gt;{&lt;BR&gt;pthread_t id;&lt;BR&gt;int i,ret;&lt;BR&gt;ret=pthread_create(&amp;amp;id,NULL,(void *) thread,NULL);&lt;BR&gt;if(ret!=0){&lt;BR&gt;printf (&quot;Create pthread error!\n&quot;);&lt;BR&gt;exit (1);&lt;BR&gt;}&lt;BR&gt;for(i=0;i&amp;lt;3;i++)&lt;BR&gt;printf(&quot;This is the main process.\n&quot;);&lt;BR&gt;pthread_join(id,NULL);&lt;BR&gt;return (0);&lt;BR&gt;}&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;我们编译此程序：&lt;BR&gt;gcc example1.c -lpthread -o example1&lt;BR&gt;运行example1，我们得到如下结果：&lt;BR&gt;This is the main process.&lt;BR&gt;This is a pthread.&lt;BR&gt;This is the main process.&lt;BR&gt;This is the main process.&lt;BR&gt;This is a pthread.&lt;BR&gt;This is a pthread.&lt;BR&gt;再次运行，我们可能得到如下结果：&lt;BR&gt;This is a pthread.&lt;BR&gt;This is the main process.&lt;BR&gt;This is a pthread.&lt;BR&gt;This is the main process.&lt;BR&gt;This is a pthread.&lt;BR&gt;This is the main process.&lt;BR&gt;&lt;BR&gt;　　前后两次结果不一样，这是两个线程争夺CPU资源的结果。上面的示例中，我们使用到了两个函数，　　pthread_create和pthread_join，并声明了一个pthread_t型的变量。&lt;BR&gt;　　pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义：&lt;BR&gt;　　typedef unsigned long int pthread_t;&lt;BR&gt;　　它是一个线程的标识符。函数pthread_create用来创建一个线程，它的原型为：&lt;BR&gt;　　extern int pthread_create __P ((pthread_t *__thread, __const pthread_attr_t *__attr,&lt;BR&gt;　　void *(*__start_routine) (void *), void *__arg));&lt;BR&gt;　　第一个参数为指向线程标识符的指针，第二个参数用来设置线程属性，第三个参数是线程运行函数的起始地址，最后一个参数是运行函数的参数。这里，我们的函数thread不需要参数，所以最后一个参数设为空指针。第二个参数我们也设为空指针，这样将生成默认属性的线程。对线程属性的设定和修改我们将在下一节阐述。当创建线程成功时，函数返回0，若不为0则说明创建线程失败，常见的错误返回代码为EAGAIN和EINVAL。前者表示系统限制创建新的线程，例如线程数目过多了；后者表示第二个参数代表的线程属性值非法。创建线程成功后，新创建的线程则运行参数三和参数四确定的函数，原来的线程则继续运行下一行代码。&lt;BR&gt;　　函数pthread_join用来等待一个线程的结束。函数原型为：&lt;BR&gt;　　extern int pthread_join __P ((pthread_t __th, void **__thread_return));&lt;BR&gt;　　第一个参数为被等待的线程标识符，第二个参数为一个用户定义的指针，它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数，调用它的函数将一直等待到被等待的线程结束为止，当函数返回时，被等待线程的资源被收回。一个线程的结束有两种途径，一种是象我们上面的例子一样，函数结束了，调用它的线程也就结束了；另一种方式是通过函数pthread_exit来实现。它的函数原型为：&lt;BR&gt;　　extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__));&lt;BR&gt;　　唯一的参数是函数的返回代码，只要pthread_join中的第二个参数thread_return不是NULL，这个值将被传递给thread_return。最后要说明的是，一个线程不能被多个线程等待，否则第一个接收到信号的线程成功返回，其余调用pthread_join的线程则返回错误代码ESRCH。&lt;BR&gt;　　在这一节里，我们编写了一个最简单的线程，并掌握了最常用的三个函数pthread_create，pthread_join和pthread_exit。下面，我们来了解线程的一些常用属性以及如何设置这些属性。&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&lt;B&gt;3 修改线程的属性&lt;/B&gt;&lt;/SPAN&gt;&lt;BR&gt;　　在上一节的例子里，我们用pthread_create函数创建了一个线程，在这个线程中，我们使用了默认参数，即将该函数的第二个参数设为NULL。的确，对大多数程序来说，使用默认属性就够了，但我们还是有必要来了解一下线程的有关属性。&lt;BR&gt;　　属性结构为pthread_attr_t，它同样在头文件/usr/include/pthread.h中定义，喜欢追根问底的人可以自己去查看。属性值不能直接设置，须使用相关函数进行操作，初始化的函数为pthread_attr_init，这个函数必须在pthread_create函数之前调用。属性对象主要包括是否绑定、是否分离、堆栈地址、堆栈大小、优先级。默认的属性为非绑定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。&lt;BR&gt;　　关于线程的绑定，牵涉到另外一个概念：轻进程（LWP：Light Weight Process）。轻进程可以理解为内核线程，它位于用户层和系统层之间。系统对线程资源的分配、对线程的控制是通过轻进程来实现的，一个轻进程可以控制一个或多个线程。默认状况下，启动多少轻进程、哪些轻进程来控制哪些线程是由系统来控制的，这种状况即称为非绑定的。绑定状况下，则顾名思义，即某个线程固定的&quot;绑&quot;在一个轻进程之上。被绑定的线程具有较高的响应速度，这是因为CPU时间片的调度是面向轻进程的，绑定的线程可以保证在需要的时候它总有一个轻进程可用。通过设置被绑定的轻进程的优先级和调度级可以使得绑定的线程满足诸如实时反应之类的要求。&lt;BR&gt;　　设置线程绑定状态的函数为pthread_attr_setscope，它有两个参数，第一个是指向属性结构的指针，第二个是绑定类型，它有两个取值：PTHREAD_SCOPE_SYSTEM（绑定的）和PTHREAD_SCOPE_PROCESS（非绑定的）。下面的代码即创建了一个绑定的线程。&lt;BR&gt;#include &amp;lt;pthread.h&amp;gt;&lt;BR&gt;pthread_attr_t attr;&lt;BR&gt;pthread_t tid;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;/*初始化属性值，均设为默认值*/&lt;BR&gt;pthread_attr_init(&amp;amp;attr);&lt;BR&gt;pthread_attr_setscope(&amp;amp;attr, PTHREAD_SCOPE_SYSTEM);&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;pthread_create(&amp;amp;tid, &amp;amp;attr, (void *) my_function, NULL);&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;　　线程的分离状态决定一个线程以什么样的方式来终止自己。在上面的例子中，我们采用了线程的默认属性，即为非分离状态，这种情况下，原有的线程等待创建的线程结束。只有当pthread_join（）函数返回时，创建的线程才算终止，才能释放自己占用的系统资源。而分离线程不是这样子的，它没有被其他的线程所等待，自己运行结束了，线程也就终止了，马上释放系统资源。程序员应该根据自己的需要，选择适当的分离状态。设置线程分离状态的函数为pthread_attr_setdetachstate（pthread_attr_t *attr, int detachstate）。第二个参数可选为PTHREAD_CREATE_DETACHED（分离线程）和 PTHREAD _CREATE_JOINABLE（非分离线程）。这里要注意的一点是，如果设置一个线程为分离线程，而这个线程运行又非常快，它很可能在pthread_create函数返回之前就终止了，它终止以后就可能将线程号和系统资源移交给其他的线程使用，这样调用pthread_create的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施，最简单的方法之一是可以在被创建的线程里调用pthread_cond_timewait函数，让这个线程等待一会儿，留出足够的时间让函数pthread_create返回。设置一段等待时间，是在多线程编程里常用的方法。但是注意不要使用诸如wait（）之类的函数，它们是使整个进程睡眠，并不能解决线程同步的问题。&lt;BR&gt;　　另外一个可能常用的属性是线程的优先级，它存放在结构sched_param中。用函数pthread_attr_getschedparam和函数pthread_attr_setschedparam进行存放，一般说来，我们总是先取优先级，对取得的值修改后再存放回去。下面即是一段简单的例子。&lt;BR&gt;#include &amp;lt;pthread.h&amp;gt;&lt;BR&gt;#include &amp;lt;sched.h&amp;gt;&lt;BR&gt;pthread_attr_t attr;&lt;BR&gt;pthread_t tid;&lt;BR&gt;sched_param param;&lt;BR&gt;int newprio=20;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;pthread_attr_init(&amp;amp;attr);&lt;BR&gt;pthread_attr_getschedparam(&amp;amp;attr, &amp;amp;param);&lt;BR&gt;param.sched_priority=newprio;&lt;BR&gt;pthread_attr_setschedparam(&amp;amp;attr, &amp;amp;param);&lt;BR&gt;pthread_create(&amp;amp;tid, &amp;amp;attr, (void *)myfunction, myarg);&lt;BR&gt;　　&lt;BR&gt;&lt;SPAN style=&quot;COLOR: #000000&quot;&gt;&lt;B&gt;4 线程的数据处理&lt;/B&gt;&lt;/SPAN&gt;&lt;BR&gt;　　和进程相比，线程的最大优点之一是数据的共享性，各个进程共享父进程处沿袭的数据段，可以方便的获得、修改数据。但这也给多线程编程带来了许多问题。我们必须当心有多个不同的进程访问相同的变量。许多函数是不可重入的，即同时不能运行一个函数的多个拷贝（除非使用不同的数据段）。在函数中声明的静态变量常常带来问题，函数的返回值也会有问题。因为如果返回的是函数内部静态声明的空间的地址，则在一个线程调用该函数得到地址后使用该地址指向的数据时，别的线程可能调用此函数并修改了这一段数据。在进程中共享的变量必须用关键字volatile来定义，这是为了防止编译器在优化时（如gcc中使用-OX参数）改变它们的使用方式。为了保护变量，我们必须使用信号量、互斥等方法来保证我们对变量的正确使用。下面，我们就逐步介绍处理线程数据时的有关知识。&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;B&gt;4.1 线程数据&lt;/B&gt;&lt;BR&gt;　　在单线程的程序里，有两种基本的数据：全局变量和局部变量。但在多线程程序里，还有第三种数据类型：线程数据（TSD: Thread-Specific Data）。它和全局变量很象，在线程内部，各个函数可以象使用全局变量一样调用它，但它对线程外部的其它线程是不可见的。这种数据的必要性是显而易见的。例如我们常见的变量errno，它返回标准的出错信息。它显然不能是一个局部变量，几乎每个函数都应该可以调用它；但它又不能是一个全局变量，否则在A线程里输出的很可能是B线程的出错信息。要实现诸如此类的变量，我们就必须使用线程数据。我们为每个线程数据创建一个键，它和这个键相关联，在各个线程里，都使用这个键来指代线程数据，但在不同的线程里，这个键代表的数据是不同的，在同一个线程里，它代表同样的数据内容。&lt;BR&gt;　　和线程数据相关的函数主要有4个：创建一个键；为一个键指定线程数据；从一个键读取线程数据；删除键。&lt;BR&gt;　　创建键的函数原型为：&lt;BR&gt;　　extern int pthread_key_create __P ((pthread_key_t *__key,&lt;BR&gt;　　void (*__destr_function) (void *)));&lt;BR&gt;　　第一个参数为指向一个键值的指针，第二个参数指明了一个destructor函数，如果这个参数不为空，那么当每个线程结束时，系统将调用这个函数来释放绑定在这个键上的内存块。这个函数常和函数pthread_once ((pthread_once_t*once_control, void (*initroutine) (void)))一起使用，为了让这个键只被创建一次。函数pthread_once声明一个初始化函数，第一次调用pthread_once时它执行这个函数，以后的调用将被它忽略。&lt;BR&gt;&lt;BR&gt;　　在下面的例子中，我们创建一个键，并将它和某个数据相关联。我们要定义一个函数createWindow，这个函数定义一个图形窗口（数据类型为Fl_Window *，这是图形界面开发工具FLTK中的数据类型）。由于各个线程都会调用这个函数，所以我们使用线程数据。&lt;BR&gt;/* 声明一个键*/&lt;BR&gt;pthread_key_t myWinKey;&lt;BR&gt;/* 函数 createWindow */&lt;BR&gt;void createWindow ( void ) {&lt;BR&gt;Fl_Window * win;&lt;BR&gt;static pthread_once_t once= PTHREAD_ONCE_INIT;&lt;BR&gt;/* 调用函数createMyKey，创建键*/&lt;BR&gt;pthread_once ( &amp;amp; once, createMyKey) ;&lt;BR&gt;/*win指向一个新建立的窗口*/&lt;BR&gt;win=new Fl_Window( 0, 0, 100, 100, &quot;MyWindow&quot;);&lt;BR&gt;/* 对此窗口作一些可能的设置工作，如大小、位置、名称等*/&lt;BR&gt;setWindow(win);&lt;BR&gt;/* 将窗口指针值绑定在键myWinKey上*/&lt;BR&gt;pthread_setpecific ( myWinKey, win);&lt;BR&gt;}&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;/* 函数 createMyKey，创建一个键，并指定了destructor */&lt;BR&gt;void createMyKey ( void ) {&lt;BR&gt;pthread_keycreate(&amp;amp;myWinKey, freeWinKey);&lt;BR&gt;}&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;/* 函数 freeWinKey，释放空间*/&lt;BR&gt;void freeWinKey ( Fl_Window * win){&lt;BR&gt;delete win;&lt;BR&gt;}&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;　　这样，在不同的线程中调用函数createMyWin，都可以得到在线程内部均可见的窗口变量，这个变量通过函数pthread_getspecific得到。在上面的例子中，我们已经使用了函数pthread_setspecific来将线程数据和一个键绑定在一起。这两个函数的原型如下：&lt;BR&gt;　　extern int pthread_setspecific __P ((pthread_key_t __key,__const void *__pointer));&lt;BR&gt;　　extern void *pthread_getspecific __P ((pthread_key_t __key));&lt;BR&gt;　　这两个函数的参数意义和使用方法是显而易见的。要注意的是，用pthread_setspecific为一个键指定新的线程数据时，必须自己释放原有的线程数据以回收空间。这个过程函数pthread_key_delete用来删除一个键，这个键占用的内存将被释放，但同样要注意的是，它只释放键占用的内存，并不释放该键关联的线程数据所占用的内存资源，而且它也不会触发函数pthread_key_create中定义的destructor函数。线程数据的释放必须在释放键之前完成。&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;B&gt;4.2 互斥锁&lt;/B&gt;&lt;BR&gt;　　互斥锁用来保证一段时间内只有一个线程在执行一段代码。必要性显而易见：假设各个线程向同一个文件顺序写入数据，最后得到的结果一定是灾难性的。&lt;BR&gt;　　我们先看下面一段代码。这是一个读/写程序，它们公用一个缓冲区，并且我们假定一个缓冲区只能保存一条信息。即缓冲区只有两个状态：有信息或没有信息。&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;void reader_function ( void );&lt;BR&gt;void writer_function ( void ); &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;char buffer;&lt;BR&gt;int buffer_has_item=0;&lt;BR&gt;pthread_mutex_t mutex;&lt;BR&gt;struct timespec delay;&lt;BR&gt;void main ( void ){&lt;BR&gt;pthread_t reader;&lt;BR&gt;/* 定义延迟时间*/&lt;BR&gt;delay.tv_sec = 2;&lt;BR&gt;delay.tv_nec = 0;&lt;BR&gt;/* 用默认属性初始化一个互斥锁对象*/&lt;BR&gt;pthread_mutex_init (&amp;amp;mutex,NULL);&lt;BR&gt;pthread_create(&amp;amp;reader, pthread_attr_default, (void *)&amp;amp;reader_function), NULL);&lt;BR&gt;writer_function( );&lt;BR&gt;}&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;void writer_function (void){&lt;BR&gt;while(1){&lt;BR&gt;/* 锁定互斥锁*/&lt;BR&gt;pthread_mutex_lock (&amp;amp;mutex);&lt;BR&gt;if (buffer_has_item==0){&lt;BR&gt;buffer=make_new_item( );&lt;BR&gt;buffer_has_item=1;&lt;BR&gt;}&lt;BR&gt;/* 打开互斥锁*/&lt;BR&gt;pthread_mutex_unlock(&amp;amp;mutex);&lt;BR&gt;pthread_delay_np(&amp;amp;delay);&lt;BR&gt;}&lt;BR&gt;}&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;void reader_function(void){&lt;BR&gt;while(1){&lt;BR&gt;pthread_mutex_lock(&amp;amp;mutex);&lt;BR&gt;if(buffer_has_item==1){&lt;BR&gt;consume_item(buffer);&lt;BR&gt;buffer_has_item=0;&lt;BR&gt;}&lt;BR&gt;pthread_mutex_unlock(&amp;amp;mutex);&lt;BR&gt;pthread_delay_np(&amp;amp;delay);&lt;BR&gt;}&lt;BR&gt;}&lt;BR&gt;　　这里声明了互斥锁变量mutex，结构pthread_mutex_t为不公开的数据类型，其中包含一个系统分配的属性对象。函数pthread_mutex_init用来生成一个互斥锁。NULL参数表明使用默认属性。如果需要声明特定属性的互斥锁，须调用函数pthread_mutexattr_init。函数pthread_mutexattr_setpshared和函数pthread_mutexattr_settype用来设置互斥锁属性。前一个函数设置属性pshared，它有两个取值，PTHREAD_PROCESS_PRIVATE和PTHREAD_PROCESS_SHARED。前者用来不同进程中的线程同步，后者用于同步本进程的不同线程。在上面的例子中，我们使用的是默认属性PTHREAD_PROCESS_ PRIVATE。后者用来设置互斥锁类型，可选的类型有PTHREAD_MUTEX_NORMAL、PTHREAD_MUTEX_ERRORCHECK、PTHREAD_MUTEX_RECURSIVE和PTHREAD _MUTEX_DEFAULT。它们分别定义了不同的上所、解锁机制，一般情况下，选用最后一个默认属性。&lt;BR&gt;　　pthread_mutex_lock声明开始用互斥锁上锁，此后的代码直至调用pthread_mutex_unlock为止，均被上锁，即同一时间只能被一个线程调用执行。当一个线程执行到pthread_mutex_lock处时，如果该锁此时被另一个线程使用，那此线程被阻塞，即程序将等待到另一个线程释放此互斥锁。在上面的例子中，我们使用了pthread_delay_np函数，让线程睡眠一段时间，就是为了防止一个线程始终占据此函数。&lt;BR&gt;　　上面的例子非常简单，就不再介绍了，需要提出的是在使用互斥锁的过程中很有可能会出现死锁：两个线程试图同时占用两个资源，并按不同的次序锁定相应的互斥锁，例如两个线程都需要锁定互斥锁1和互斥锁2，a线程先锁定互斥锁1，b线程先锁定互斥锁2，这时就出现了死锁。此时我们可以使用函数pthread_mutex_trylock，它是函数pthread_mutex_lock的非阻塞版本，当它发现死锁不可避免时，它会返回相应的信息，程序员可以针对死锁做出相应的处理。另外不同的互斥锁类型对死锁的处理不一样，但最主要的还是要程序员自己在程序设计注意这一点。&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;B&gt;4.3 条件变量&lt;BR&gt;&lt;/B&gt;　　前一节中我们讲述了如何使用互斥锁来实现线程间数据的共享和通信，互斥锁一个明显的缺点是它只有两种状态：锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足，它常和互斥锁一起使用。使用时，条件变量被用来阻塞一个线程，当条件不满足时，线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量，它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来，条件变量被用来进行线承间的同步。&lt;BR&gt;　　条件变量的结构为pthread_cond_t，函数pthread_cond_init（）被用来初始化一个条件变量。它的原型为：&lt;BR&gt;　　extern int pthread_cond_init __P ((pthread_cond_t *__cond,__const pthread_condattr_t *__cond_attr));&lt;BR&gt;　　其中cond是一个指向结构pthread_cond_t的指针，cond_attr是一个指向结构pthread_condattr_t的指针。结构pthread_condattr_t是条件变量的属性结构，和互斥锁一样我们可以用它来设置条件变量是进程内可用还是进程间可用，默认值是PTHREAD_ PROCESS_PRIVATE，即此条件变量被同一进程内的各个线程使用。注意初始化条件变量只有未被使用时才能重新初始化或被释放。释放一个条件变量的函数为pthread_cond_ destroy（pthread_cond_t cond）。　&lt;BR&gt;　　函数pthread_cond_wait（）使线程阻塞在一个条件变量上。它的函数原型为：&lt;BR&gt;　　extern int pthread_cond_wait __P ((pthread_cond_t *__cond,&lt;BR&gt;　　pthread_mutex_t *__mutex));&lt;BR&gt;　　线程解开mutex指向的锁并被条件变量cond阻塞。线程可以被函数pthread_cond_signal和函数pthread_cond_broadcast唤醒，但是要注意的是，条件变量只是起阻塞和唤醒线程的作用，具体的判断条件还需用户给出，例如一个变量是否为0等等，这一点我们从后面的例子中可以看到。线程被唤醒后，它将重新检查判断条件是否满足，如果还不满足，一般说来线程应该仍阻塞在这里，被等待被下一次唤醒。这个过程一般用while语句实现。&lt;BR&gt;　　另一个用来阻塞线程的函数是pthread_cond_timedwait（），它的原型为：&lt;BR&gt;　　extern int pthread_cond_timedwait __P ((pthread_cond_t *__cond,&lt;BR&gt;　　pthread_mutex_t *__mutex, __const struct timespec *__abstime));&lt;BR&gt;　　它比函数pthread_cond_wait（）多了一个时间参数，经历abstime段时间后，即使条件变量不满足，阻塞也被解除。&lt;BR&gt;　　函数pthread_cond_signal（）的原型为：&lt;BR&gt;　　extern int pthread_cond_signal __P ((pthread_cond_t *__cond));&lt;BR&gt;　　它用来释放被阻塞在条件变量cond上的一个线程。多个线程阻塞在此条件变量上时，哪一个线程被唤醒是由线程的调度策略所决定的。要注意的是，必须用保护条件变量的互斥锁来保护这个函数，否则条件满足信号又可能在测试条件和调用pthread_cond_wait函数之间被发出，从而造成无限制的等待。下面是使用函数pthread_cond_wait（）和函数pthread_cond_signal（）的一个简单的例子。&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;pthread_mutex_t count_lock;&lt;BR&gt;pthread_cond_t count_nonzero;&lt;BR&gt;unsigned count;&lt;BR&gt;decrement_count　() {&lt;BR&gt;pthread_mutex_lock (&amp;amp;count_lock);&lt;BR&gt;while(count==0) &lt;BR&gt;pthread_cond_wait( &amp;amp;count_nonzero, &amp;amp;count_lock);&lt;BR&gt;count=count -1;&lt;BR&gt;pthread_mutex_unlock (&amp;amp;count_lock);&lt;BR&gt;}&lt;BR&gt;&lt;BR&gt;increment_count(){&lt;BR&gt;pthread_mutex_lock(&amp;amp;count_lock);&lt;BR&gt;if(count==0)&lt;BR&gt;pthread_cond_signal(&amp;amp;count_nonzero);&lt;BR&gt;count=count+1;&lt;BR&gt;pthread_mutex_unlock(&amp;amp;count_lock);&lt;BR&gt;}&lt;BR&gt;　　count值为0&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;时，decrement函数在pthread_cond_wait处被阻塞，并打开互斥锁count_lock。此时，当调用到函数increment_count时，pthread_cond_signal（）函数改变条件变量，告知decrement_count（）停止阻塞。读者可以试着让两个线程分别运行这两个函数，看看会出现什么样的结果。&lt;BR&gt;　　函数pthread_cond_broadcast（pthread_cond_t *cond）用来唤醒所有被阻塞在条件变量cond上的线程。这些线程被唤醒后将再次竞争相应的互斥锁，所以必须小心使用这个函数。&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;B&gt;4.4 信号量&lt;BR&gt;&lt;/B&gt;　　信号量本质上是一个非负的整数计数器，它被用来控制对公共资源的访问。当公共资源增加时，调用函数sem_post（）增加信号量。只有当信号量值大于０时，才能使用公共资源，使用后，函数sem_wait（）减少信号量。函数sem_trywait（）和函数pthread_ mutex_trylock（）起同样的作用，它是函数sem_wait（）的非阻塞版本。下面我们逐个介绍和信号量有关的一些函数，它们都在头文件/usr/include/semaphore.h中定义。&lt;BR&gt;　　信号量的数据类型为结构sem_t，它本质上是一个长整型的数。函数sem_init（）用来初始化一个信号量。它的原型为：&lt;BR&gt;　　extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));&lt;BR&gt;　　sem为指向信号量结构的一个指针；pshared不为０时此信号量在进程间共享，否则只能为当前进程的所有线程共享；value给出了信号量的初始值。&lt;BR&gt;　　函数sem_post( sem_t *sem )用来增加信号量的值。当有线程阻塞在这个信号量上时，调用这个函数会使其中的一个线程不在阻塞，选择机制同样是由线程的调度策略决定的。&lt;BR&gt;　　函数sem_wait( sem_t *sem )被用来阻塞当前线程直到信号量sem的值大于0，解除阻塞后将sem的值减一，表明公共资源经使用后减少。函数sem_trywait ( sem_t *sem )是函数sem_wait（）的非阻塞版本，它直接将信号量sem的值减一。&lt;BR&gt;　　函数sem_destroy(sem_t *sem)用来释放信号量sem。&lt;BR&gt;　　下面我们来看一个使用信号量的例子。在这个例子中，一共有4个线程，其中两个线程负责从文件读取数据到公共的缓冲区，另两个线程从缓冲区读取数据作不同的处理（加和乘运算）。&lt;BR&gt;/* File sem.c */&lt;BR&gt;#include &amp;lt;stdio.h&amp;gt;&lt;BR&gt;#include &amp;lt;pthread.h&amp;gt;&lt;BR&gt;#include &amp;lt;semaphore.h&amp;gt;&lt;BR&gt;#define MAXSTACK 100&lt;BR&gt;int stack[MAXSTACK][2];&lt;BR&gt;int size=0;&lt;BR&gt;sem_t sem;&lt;BR&gt;/* 从文件1.dat读取数据，每读一次，信号量加一*/&lt;BR&gt;void ReadData1(void){&lt;BR&gt;FILE *fp=fopen(&quot;1.dat&quot;,&quot;r&quot;);&lt;BR&gt;while(!feof(fp)){&lt;BR&gt;fscanf(fp,&quot;%d %d&quot;,&amp;amp;stack[size][0],&amp;amp;stack[size][1]);&lt;BR&gt;sem_post(&amp;amp;sem);&lt;BR&gt;++size;&lt;BR&gt;}&lt;BR&gt;fclose(fp);&lt;BR&gt;}&lt;BR&gt;/*从文件2.dat读取数据*/&lt;BR&gt;void ReadData2(void){&lt;BR&gt;FILE *fp=fopen(&quot;2.dat&quot;,&quot;r&quot;);&lt;BR&gt;while(!feof(fp)){&lt;BR&gt;fscanf(fp,&quot;%d %d&quot;,&amp;amp;stack[size][0],&amp;amp;stack[size][1]);&lt;BR&gt;sem_post(&amp;amp;sem);&lt;BR&gt;++size;&lt;BR&gt;}&lt;BR&gt;fclose(fp);&lt;BR&gt;}&lt;BR&gt;/*阻塞等待缓冲区有数据，读取数据后，释放空间，继续等待*/&lt;BR&gt;void HandleData1(void){&lt;BR&gt;while(1){&lt;BR&gt;sem_wait(&amp;amp;sem);&lt;BR&gt;printf(&quot;Plus:%d+%d=%d\n&quot;,stack[size][0],stack[size][1],&lt;BR&gt;stack[size][0]+stack[size][1]);&lt;BR&gt;--size;&lt;BR&gt;}&lt;BR&gt;}&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN class=line style=&quot;COLOR: #000000&quot;&gt;&lt;SPAN class=line style=&q