﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-月光记忆-文章分类-c</title><link>http://www.blogjava.net/sunzhong/category/41054.html</link><description>清泉响露</description><language>zh-cn</language><lastBuildDate>Thu, 15 Oct 2009 14:19:45 GMT</lastBuildDate><pubDate>Thu, 15 Oct 2009 14:19:45 GMT</pubDate><ttl>60</ttl><item><title>Linux内核中的进程组及会话</title><link>http://www.blogjava.net/sunzhong/articles/298464.html</link><dc:creator>月光记忆</dc:creator><author>月光记忆</author><pubDate>Thu, 15 Oct 2009 13:39:00 GMT</pubDate><guid>http://www.blogjava.net/sunzhong/articles/298464.html</guid><wfw:comment>http://www.blogjava.net/sunzhong/comments/298464.html</wfw:comment><comments>http://www.blogjava.net/sunzhong/articles/298464.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sunzhong/comments/commentRss/298464.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sunzhong/services/trackbacks/298464.html</trackback:ping><description><![CDATA[<h3 class="" title=""><a href="http://shake863.javaeye.com/blog/187085"><span class="hilite1">Linux</span>内核中的进程组及会话</a></h3>
<div class="blog_content">
<p><span style="font-family: Verdana">将阐述<span class="hilite1">Linux</span>内核中的如下几个概念 <br />
1) 进程组 <br />
2) 会话 <br />
3) 控制终端 <br />
前面的概念来源于前人，我只是站在前人的肩膀上结合内核中的实现加深概念理解。 <br />
<br />
1.概念： <br />
<br />
a)进程组 <br />
Shell 上的一条命令行形成一个进程组 <br />
每个进程属于一个进程组 <br />
每个进程组有一个领头进程 <br />
进程组的生命周期到组中最后一个进程终止, 或加入其他进程组为止 <br />
getpgrp: 获得进程组 id, 即领头进程的 pid <br />
setpgid: 加入进程组和建立新的进程组 <br />
前台进程组和后台进程组 <br />
<br />
=============================================================================== <br />
#include &lt;unistd.h&gt; <br />
<br />
int setpgid (pid_t pid, pid_t pgid); <br />
pid_t getpgid (pid_t pid); <br />
int setpgrp (void); <br />
pid_t getpgrp (void); <br />
------------------------------------------------------------------------------- <br />
进程只能将自身和其子进程设置为进程组 id. <br />
某个子进程调用 exec 函数之后, 就不能再将该子进程的 id 作为进程组 id. <br />
=============================================================================== <br />
<br />
b)会话 <br />
一次登录形成一个会话 <br />
一个会话可包含多个进程组, 但只能有一个前台进程组. <br />
setsid 可建立一个新的会话 <br />
=============================================================================== <br />
#include &lt;unistd.h&gt; <br />
<br />
pid_t setsid(void); <br />
------------------------------------------------------------------------------- <br />
如果调用进程不是进程组的领头进程, 该函数才能建立新的会话. <br />
调用 setsid 之后, 进程成为新会话的领头进程. <br />
进程成为新进程组的领头进程. <br />
进程失去控制终端 <br />
=============================================================================== <br />
<br />
c)控制终端 <br />
会话的领头进程打开一个终端之后, 该终端就成为该会话的控制终端 (SVR4/<span class="hilite1">Linux</span>) <br />
与控制终端建立连接的会话领头进程也称为控制进程 (session leader) <br />
一个会话只能有一个控制终端 <br />
产生在控制终端上的输入和信号将发送给会话的前台进程组中的所有进程 <br />
终端上的连接断开时 (比如网络断开或 Modem 断开), 挂起信号将发送到控制进程(session leader) </span></p>
</div>
<img src ="http://www.blogjava.net/sunzhong/aggbug/298464.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sunzhong/" target="_blank">月光记忆</a> 2009-10-15 21:39 <a href="http://www.blogjava.net/sunzhong/articles/298464.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>autotools使用</title><link>http://www.blogjava.net/sunzhong/articles/297469.html</link><dc:creator>月光记忆</dc:creator><author>月光记忆</author><pubDate>Thu, 08 Oct 2009 12:58:00 GMT</pubDate><guid>http://www.blogjava.net/sunzhong/articles/297469.html</guid><wfw:comment>http://www.blogjava.net/sunzhong/comments/297469.html</wfw:comment><comments>http://www.blogjava.net/sunzhong/articles/297469.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sunzhong/comments/commentRss/297469.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sunzhong/services/trackbacks/297469.html</trackback:ping><description><![CDATA[<span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Makefile固然</span><span style="font-family: 宋体">可以帮助</span><span lang="EN-US">make</span><span style="font-family: 宋体">完成它的使命，但要承认的是，编写</span><span lang="EN-US">Makefile</span><span style="font-family: 宋体">确实不是一件轻松的事，尤其对于一个较大的项目而言更是如此。那么，有没有一种轻松的手段生成</span><span lang="EN-US">Makefile</span><span style="font-family: 宋体">而同时又能让我们享受</span><span lang="EN-US">make</span><span style="font-family: 宋体">的优越性呢？本节要讲</span><span lang="EN-US">autotools</span><span style="font-family: 宋体">系列工具正是为此而设的，它只需用户输入简单的目标文件、依赖文件、文件目录等就可以轻松地生成</span><span lang="EN-US">Makefile</span><span style="font-family: 宋体">了，这无疑是广大用户的所希望的。另外，这些工具还可以完成系统配置信息的收集，从而可以方便地处理各种移植性的问题。也正是基于此，现在</span><span lang="EN-US">Linux</span><span style="font-family: 宋体">上的软件开发一般都用</span><span lang="EN-US">autotools</span><span style="font-family: 宋体">来制作</span><span lang="EN-US">Makefile</span><span style="font-family: 宋体">。<br />
&nbsp;&nbsp; <br />
a<span lang="EN-US">utotools</span><span style="font-family: 方正细黑一简体">使用流程</span><br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp; <span style="font-family: 宋体">正如前面所言，</span><span lang="EN-US">autotools</span><span style="font-family: 宋体">是系列工具，读者首先要确认系统是否装了以下工具（可以用</span><span lang="EN-US">which</span><span style="font-family: 宋体">命令进行查看）。</span>
<p class="MsoNormal"><span lang="EN-US" style="font-family: Symbol">&#183;</span><span lang="EN-US">&nbsp; aclocal</span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-family: Symbol">&#183;</span><span lang="EN-US">&nbsp; autoscan</span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-family: Symbol">&#183;</span><span lang="EN-US">&nbsp; autoconf</span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-family: Symbol">&#183;</span><span lang="EN-US">&nbsp; autoheader</span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-family: Symbol">&#183;</span><span lang="EN-US">&nbsp; automake</span></p>
<p class="MsoNormal"><span style="font-family: 宋体">使用</span><span lang="EN-US">autotools</span><span style="font-family: 宋体">主要就是利用各个工具的脚本文件以生成最后的</span><span lang="EN-US">Makefile</span><span style="font-family: 宋体">。其总体流程是这样的：</span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-family: Symbol">&#183;</span><span lang="EN-US">&nbsp; </span><span style="font-family: 宋体">使用</span><span lang="EN-US">aclocal</span><span style="font-family: 宋体">生成一个&#8220;</span><span lang="EN-US">aclocal.m4<span lang="EN-US" style="font-family: 宋体"><span lang="EN-US">&#8221;</span></span><span lang="EN-US" style="font-family: 宋体">文件，该文件主要处理本地的宏定义；</span></span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-family: Symbol">&#183;</span><span lang="EN-US">&nbsp; </span><span style="font-family: 宋体">改写&#8220;</span><span lang="EN-US">configure.scan</span><span style="font-family: 宋体">&#8221;文件，并将其重命名为&#8220;</span><span lang="EN-US">configure.in</span><span style="font-family: 宋体">&#8221;，并使用</span><span lang="EN-US">autoconf</span><span style="font-family: 宋体">文件生成</span><span lang="EN-US">configure</span><span style="font-family: 宋体">文件。</span></p>
<p class="MsoNormal"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; 接下来，将通过一个简单的</span><span lang="EN-US">hello.c</span><span style="font-family: 宋体">例子来熟悉</span><span lang="EN-US">autotools</span><span style="font-family: 宋体">生成</span><span lang="EN-US">makefile</span><span style="font-family: 宋体">的过程.</span></p>
<h4 style="page-break-before: always"><span lang="EN-US">1</span><span style="font-family: 黑体">．</span><span lang="EN-US">autoscan</span></h4>
<p class="MsoNormal"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp;它会在给定目录及其子目录树中检查源文件，若没有给出目录，就在当前目录及其子目录树中进行检查。它会搜索源文件以寻找一般的移植性问题并创建一个文件&#8220;</span><span lang="EN-US">configure.scan</span><span style="font-family: 宋体">&#8221;，该文件就是接下来</span><span lang="EN-US">autoconf</span><span style="font-family: 宋体">要用到的&#8220;</span><span lang="EN-US">configure.in</span><span style="font-family: 宋体">&#8221;原型。如下所示：</span></p>
<p class="a0" style="line-height: 8pt"><span lang="EN-US">&nbsp;</span></p>
<p class="a8"><span lang="EN-US">[root@localhost automake]# <strong>autoscan</strong></span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600">autom4te: configure.ac: no such file or directory</span></span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600">autoscan: /usr/bin/autom4te failed with exit status: 1</span></span></p>
<p class="a8"><span lang="EN-US">[root@localhost automake]# ls</span></p>
<p class="a8"><span lang="EN-US">autoscan.log&nbsp; <strong>configure.scan</strong>&nbsp; hello.c</span></p>
<p class="a0" style="line-height: 8pt"><span lang="EN-US">&nbsp;</span></p>
<p class="MsoNormal"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #ff6600"><span style="font-family: 宋体">如上所示，</span><span lang="EN-US">autoscan</span><span style="font-family: 宋体">首先会尝试去读入&#8220;</span><span lang="EN-US">configure.ac</span><span style="font-family: 宋体">&#8221;（同</span><span lang="EN-US">configure.in</span><span style="font-family: 宋体">的配置文件）文件，此时还没有创建该配置文件，于是它会自动生成一个&#8220;</span><span lang="EN-US">configure.in</span><span style="font-family: 宋体">&#8221;的原型文件&#8220;</span><span lang="EN-US">configure.scan</span><span style="font-family: 宋体">&#8221;。</span></span></span></p>
<h4><span lang="EN-US">2</span><span style="font-family: 黑体">．</span><span lang="EN-US">autoconf</span></h4>
<p class="MsoNormal"><span lang="EN-US">configure.in</span><span style="font-family: 宋体">是</span><span lang="EN-US">autoconf</span><span style="font-family: 宋体">的脚本配置文件，它的原型文件&#8220;</span><span lang="EN-US">configure.scan</span><span style="font-family: 宋体">&#8221;如下所示：</span></p>
<p class="a0"><span lang="EN-US">&nbsp;</span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600">#&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -*- Autoconf -*-</span></span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600"># Process this file with autoconf to produce a configure script.</span></span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600">AC_PREREQ(2.59)</span></span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600">#The next one is modified by sunq</span></span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600">#AC_INIT(FULL-PACKAGE-NAME,VERSION,BUG-REPORT-ADDRESS)</span></span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600">AC_INIT(hello,1.0)</span></span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600"># The next one is added by sunq</span></span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600">AM_INIT_AUTOMAKE(hello,1.0)</span></span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600">AC_CONFIG_SRCDIR([hello.c])</span></span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600">AC_CONFIG_HEADER([config.h])</span></span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600"># Checks for programs.</span></span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600">AC_PROG_CC</span></span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600"># Checks for libraries.</span></span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600"># Checks for header files.</span></span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600"># Checks for typedefs, structures, and compiler characteristics.</span></span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600"># Checks for library functions.</span></span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600">AC_CONFIG_FILES([Makefile])</span></span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600">AC_OUTPUT</span></span></p>
<p class="a0"><span lang="EN-US">&nbsp;</span></p>
<p class="MsoNormal"><span style="font-family: 宋体">下面对这个脚本文件进行解释：</span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-family: Symbol">&#183;</span><span lang="EN-US">&nbsp; </span><span style="font-family: 宋体">以&#8220;</span><span lang="EN-US">#</span><span style="font-family: 宋体">&#8221;号开始的行为注释。</span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-family: Symbol">&#183;</span><span lang="EN-US">&nbsp; AC_PREREQ</span><span style="font-family: 宋体">宏声明本文件要求的</span><span lang="EN-US">autoconf</span><span style="font-family: 宋体">版本，如本例使用的版本</span><span lang="EN-US">2.59</span><span style="font-family: 宋体">。</span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-family: Symbol; letter-spacing: -0.3pt">&#183;</span><span lang="EN-US" style="letter-spacing: -0.3pt">&nbsp;&nbsp;AC_INIT</span><span style="font-family: 宋体; letter-spacing: -0.3pt">宏用来定义软件的名称和版本等信息，在本例中省略了</span><span lang="EN-US" style="letter-spacing: -0.3pt">BUG-REPORT-ADDRESS</span><span style="font-family: 宋体; letter-spacing: -0.3pt">，</span><span style="font-family: 宋体">一般为作者的</span><span lang="EN-US">e-mail</span><span style="font-family: 宋体">。</span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-family: Symbol">&#183;</span><span lang="EN-US">&nbsp; AM_INIT_AUTOMAKE</span><span style="font-family: 宋体">是笔者另加的，它是</span><span lang="EN-US">automake</span><span style="font-family: 宋体">所必备的宏，也同前面一样，</span><span lang="EN-US">PACKAGE</span><span style="font-family: 宋体">是所要产生软件套件的名称，</span><span lang="EN-US">VERSION</span><span style="font-family: 宋体">是版本编号。</span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-family: Symbol">&#183;</span><span lang="EN-US">&nbsp; AC_CONFIG_SRCDIR</span><span style="font-family: 宋体">宏用来侦测所指定的源码文件是否存在，来确定源码目录的有</span></p>
<p class="MsoNormal"><span style="font-family: 宋体">效性。在此处为当前目录下的</span><span lang="EN-US">hello.c</span><span style="font-family: 宋体">。</span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-family: Symbol">&#183;</span><span lang="EN-US">&nbsp; AC_CONFIG_HEADER</span><span style="font-family: 宋体">宏用于生成</span><span lang="EN-US">config.h</span><span style="font-family: 宋体">文件，以便</span><span lang="EN-US">autoheader</span><span style="font-family: 宋体">使用。</span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-family: Symbol">&#183;</span><span lang="EN-US">&nbsp; AC_CONFIG_FILES</span><span style="font-family: 宋体">宏用于生成相应的</span><span lang="EN-US">Makefile</span><span style="font-family: 宋体">文件。</span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-family: Symbol">&#183;</span><span lang="EN-US">&nbsp; </span><span style="font-family: 宋体">中间的注释间可以添加分别用户测试程序、测试函数库、测试头文件等宏定义。</span></p>
<p class="MsoNormal"><span style="font-family: 宋体">接下来首先运行</span><span lang="EN-US">aclocal</span><span style="font-family: 宋体">，生成一个&#8220;</span><span lang="EN-US">aclocal.m4<span lang="EN-US" style="font-family: 宋体"><span lang="EN-US">&#8221;</span></span><span lang="EN-US" style="font-family: 宋体">文件，该文件主要处理本地的宏定义。如下所示：</span></span></p>
<p class="a0"><span lang="EN-US">&nbsp;</span></p>
<p class="a8"><span lang="EN-US">[root@localhost automake]# <strong>aclocal</strong></span></p>
<p class="a0"><span lang="EN-US">&nbsp;</span></p>
<p class="MsoNormal"><span style="font-family: 宋体">再接着运行</span><span lang="EN-US">autoconf</span><span style="font-family: 宋体">，生成&#8220;</span><span lang="EN-US">configure</span><span style="font-family: 宋体">&#8221;可执行文件。如下所示：</span></p>
<p class="a0"><span lang="EN-US">&nbsp;</span></p>
<p class="a8"><span lang="EN-US">[root@localhost automake]# <strong>autoconf</strong></span></p>
<p class="a8"><span lang="EN-US">[root@localhost automake]# <strong>ls</strong></span></p>
<p class="a8"><span lang="EN-US">aclocal.m4&nbsp; autom4te.cache&nbsp; autoscan.log&nbsp; <strong>configure</strong>&nbsp; configure.in&nbsp; hello.c</span></p>
<h4 style="margin: 7pt 0cm"><span lang="EN-US">3</span><span style="font-family: 黑体">．</span><span lang="EN-US">autoheader</span></h4>
<p class="MsoNormal"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp;接着使用</span><span lang="EN-US">autoheader</span><span style="font-family: 宋体">命令，它负责生成</span><span lang="EN-US">config.h.in</span><span style="font-family: 宋体">文件。该工具通常会从&#8220;</span><span lang="EN-US">acconfig.h</span><span style="font-family: 宋体">&#8221;文件中复制用户附加的符号定义，因此此处没有附加符号定义，所以不需要创建&#8220;</span><span lang="EN-US">acconfig.h</span><span style="font-family: 宋体">&#8221;文件。如下所示：</span></p>
<p class="a0"><span lang="EN-US">&nbsp;</span></p>
<p class="a8"><span lang="EN-US">[root@localhost automake]#<strong> autoheader</strong></span></p>
<h4 style="margin: 7pt 0cm"><span lang="EN-US">4</span><span style="font-family: 黑体">．</span><span lang="EN-US">automake</span></h4>
<p class="MsoNormal"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp;这一步是创建</span><span lang="EN-US">Makefile</span><span style="font-family: 宋体">很重要的一步，</span><span lang="EN-US">automake</span><span style="font-family: 宋体">要用的脚本配置文件是</span><span lang="EN-US">Makefile.am</span><span style="font-family: 宋体">，用户需要自己创建这个文件。之后，</span><span lang="EN-US">automake</span><span style="font-family: 宋体">工具将其转换成</span><span lang="EN-US">Makefile.in</span><span style="font-family: 宋体">。<br />
&nbsp;&nbsp;&nbsp; 在该例中，创建的文件为</span><span lang="EN-US">Makefile.am</span><span style="font-family: 宋体">如下所示：</span></p>
<p class="a0"><span lang="EN-US">&nbsp;</span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600">AUTOMAKE_OPTIONS=foreign</span></span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600">bin_PROGRAMS= hello</span></span></p>
<p class="a8"><span lang="EN-US"><span style="color: #ff6600">hello_SOURCES= hello.c hello.h</span></span></p>
<p class="a0"><span lang="EN-US">&nbsp;</span></p>
<p class="MsoNormal"><span style="font-family: 宋体">下面对该脚本文件的对应项进行解释。</span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-family: Symbol">&#183;</span><span lang="EN-US">&nbsp; </span><span style="font-family: 宋体">其中的</span><span lang="EN-US">AUTOMAKE_OPTIONS</span><span style="font-family: 宋体">为设置</span><span lang="EN-US">automake</span><span style="font-family: 宋体">的选项。由于</span><span lang="EN-US">GNU</span><span style="font-family: 宋体">（在第</span><span lang="EN-US">1</span><span style="font-family: 宋体">章中已经有所介绍）对自己发布的软件有严格的规范，比如必须附带许可证声明文件</span><span lang="EN-US">COPYING</span><span style="font-family: 宋体">等，否则</span><span lang="EN-US">automake</span><span style="font-family: 宋体">执行时会报错。</span><span lang="EN-US">automake</span><span style="font-family: 宋体">提供了三种软件等级：</span><span lang="EN-US">foreign</span><span style="font-family: 宋体">、</span><span lang="EN-US">gnu</span><span style="font-family: 宋体">和</span><span lang="EN-US">gnits</span><span style="font-family: 宋体">，让用户选择采用，默认等级为</span><span lang="EN-US">gnu</span><span style="font-family: 宋体">。在本例使用</span><span lang="EN-US">foreign</span><span style="font-family: 宋体">等级，它只检测必须的文件。</span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-family: Symbol">&#183;</span><span lang="EN-US">&nbsp; bin_PROGRAMS</span><span style="font-family: 宋体">定义要产生的执行文件名。如果要产生多个执行文件，每个文件名用空格隔开。</span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-family: Symbol">&#183;</span><span lang="EN-US">&nbsp; hello_SOURCES</span><span style="font-family: 宋体">定义&#8220;</span><span lang="EN-US">hello</span><span style="font-family: 宋体">&#8221;这个执行程序所需要的原始文件。如果&#8221;</span><span lang="EN-US">hello</span><span style="font-family: 宋体">&#8221;这个程序是由多个原始文件所产生的，则必须把它所用到的所有原始文件都列出来，并用空格隔开。例如：若目标体&#8220;</span><span lang="EN-US">hello</span><span style="font-family: 宋体">&#8221;需要&#8220;</span><span lang="EN-US">hello.c</span><span style="font-family: 宋体">&#8221;、&#8220;</span><span lang="EN-US">sunq.c</span><span style="font-family: 宋体">&#8221;、&#8220;</span><span lang="EN-US">hello.h</span><span style="font-family: 宋体">&#8221;三个依赖文件，则定义</span><span lang="EN-US">hello_SOURCES=hello.c sunq.c hello.h</span><span style="font-family: 宋体">。<br />
</span><span style="font-family: 宋体">&nbsp; &nbsp;&nbsp;接下来可以使用</span><span lang="EN-US">automake</span><span style="font-family: 宋体">对其生成&#8220;</span><span lang="EN-US">configure.in</span><span style="font-family: 宋体">&#8221;文件，在这里使用选项&#8220;</span><span lang="EN-US">—adding-missing</span><span style="font-family: 宋体">&#8221;可以让</span><span lang="EN-US">automake</span><span style="font-family: 宋体">自动添加有一些必需的脚本文件。如下所示：</span></p>
<p class="a0"><span lang="EN-US">&nbsp;</span></p>
<p class="a8"><span lang="EN-US">[root@localhost automake]# <strong>automake --add-missing</strong></span></p>
<p class="a8"><span lang="EN-US">configure.in: installing './install-sh'</span></p>
<p class="a8"><span lang="EN-US">configure.in: installing './missing'</span></p>
<p class="a8"><span lang="EN-US">Makefile.am: installing 'depcomp'</span></p>
<p class="a8"><span lang="EN-US">[root@localhost automake]# <strong>ls</strong></span></p>
<p class="a8"><span lang="EN-US">aclocal.m4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; autoscan.log&nbsp; <strong>configure.in</strong>&nbsp; hello.c&nbsp;&nbsp;&nbsp;&nbsp; Makefile.am&nbsp; missing</span></p>
<p class="a8"><span lang="EN-US" style="letter-spacing: -0.1pt">autom4te.cache&nbsp; configure&nbsp;&nbsp;&nbsp;&nbsp; depcomp&nbsp;&nbsp;&nbsp; install-sh&nbsp; Makefile.in&nbsp; config.h.in</span></p>
<p class="a0"><span lang="EN-US">&nbsp;</span></p>
<p class="MsoNormal"><span style="font-family: 宋体">可以看到，在</span><span lang="EN-US">automake</span><span style="font-family: 宋体">之后就可以生成</span><span lang="EN-US">configure.in</span><span style="font-family: 宋体">文件。</span></p>
<h4 style="margin: 5pt 0cm"><span lang="EN-US">5</span><span style="font-family: 黑体">．运行</span><span lang="EN-US">configure</span></h4>
<p class="MsoNormal"><span style="font-family: 宋体">在这一步中，通过运行自动配置设置文件</span><span lang="EN-US">configure</span><span style="font-family: 宋体">，把</span><span lang="EN-US">Makefile.in</span><span style="font-family: 宋体">变成了最终的</span><span lang="EN-US">Makefile</span><span style="font-family: 宋体">。如下所示：</span></p>
<p class="a0"><span lang="EN-US">&nbsp;</span></p>
<p class="a8"><span lang="EN-US">[root@localhost automake]# <strong>./configure</strong></span></p>
<p class="a8"><span lang="EN-US">checking for a BSD-compatible install... /usr/bin/install -c</span></p>
<p class="a8"><span lang="EN-US">checking whether build enVironment is sane... yes</span></p>
<p class="a8"><span lang="EN-US">checking for gawk... gawk</span></p>
<p class="a8"><span lang="EN-US">checking whether make sets $(MAKE)... yes</span></p>
<p class="a8"><span lang="EN-US">checking for Gcc... Gcc</span></p>
<p class="a8"><span lang="EN-US">checking for C compiler default output file name... a.out</span></p>
<p class="a8"><span lang="EN-US">checking whether the C compiler works... yes</span></p>
<p class="a8"><span lang="EN-US">checking whether we are cross compiling... no</span></p>
<p class="a8"><span lang="EN-US">checking for suffix of executables...</span></p>
<p class="a8"><span lang="EN-US">checking for suffix of object files... o</span></p>
<p class="a8"><span lang="EN-US">checking whether we are using the GNU C compiler... yes</span></p>
<p class="a8"><span lang="EN-US">checking whether Gcc accepts -g... yes</span></p>
<p class="a8"><span lang="EN-US">checking for Gcc option to accept ANSI C... none needed</span></p>
<p class="a8"><span lang="EN-US">checking for style of include used by make... GNU</span></p>
<p class="a8"><span lang="EN-US">checking dependency style of Gcc... Gcc3</span></p>
<p class="a8"><span lang="EN-US">configure: creating ./config.status</span></p>
<p class="a8"><span lang="EN-US">config.status: <strong>creating Makefile</strong></span></p>
<p class="a8"><span lang="EN-US">config.status: executing depfiles commands</span></p>
<p class="MsoNormal"><span style="font-family: 宋体; letter-spacing: 0.1pt">&nbsp;&nbsp;&nbsp;&nbsp;可以看到，在运行</span><span lang="EN-US" style="letter-spacing: 0.1pt">configure</span><span style="font-family: 宋体; letter-spacing: 0.1pt">时收集了系统的信息，用户可以在</span><span lang="EN-US" style="letter-spacing: 0.1pt">configure</span><span style="font-family: 宋体; letter-spacing: 0.1pt">命令中对其进行方便地配置.<br />
在</span><span lang="EN-US" style="letter-spacing: 0.1pt">./configure</span><span style="font-family: 宋体; letter-spacing: 0.1pt">的自定义参数有两种，一种是开关式（</span><span lang="EN-US" style="letter-spacing: 0.1pt">--enable-XXX</span><span style="font-family: 宋体; letter-spacing: 0.1pt">或</span><span lang="EN-US" style="letter-spacing: 0.1pt">--disable-XXX</span><span style="font-family: 宋体; letter-spacing: 0.1pt">），另一种是开放式，即后面要填入一串字符（</span><span lang="EN-US" style="letter-spacing: 0.1pt">--with-XXX=yyyy</span><span style="font-family: 宋体; letter-spacing: 0.1pt">）参数。读者可以自行尝试其使用方法。另外，读者可以查看同一目录下的&#8221;</span><span lang="EN-US" style="letter-spacing: 0.1pt">config.log</span><span style="font-family: 宋体; letter-spacing: 0.1pt">&#8221;文件，以方便调试之用。</span></p>
<p class="MsoNormal"><span style="font-family: 宋体">到此为止，</span><span lang="EN-US">makefile</span><span style="font-family: 宋体">就可以自动生成了。回忆整个步骤，用户不再需要定制不同的规则，而只需要输入简单的文件及目录名即可，这样就大大方便了用户的使用。下面的图</span><span lang="EN-US">3.9</span><span style="font-family: 宋体">总结了上述过程：</span></p>
<p align="center"><span lang="EN-US"><img style="width: 549px; height: 319px" height="319" alt="" src="http://book.csdn.net/BookFiles/132/03/image012.gif" width="549" /><br />
</span></p>
<p align="center"><span style="font-family: 宋体"><br />
<br />
图</span><span lang="EN-US">3.9&nbsp; autotools</span><span style="font-family: 宋体">生成</span><span lang="EN-US">Makefile</span><span style="font-family: 宋体">流程图</span></p>
<p class="MsoNormal"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;autotools</span><span style="font-family: 宋体">生成的</span><span lang="EN-US">Makefile</span><span style="font-family: 宋体">除具有普通的编译功能外，还具有以下主要功能:<br />
</span><span lang="EN-US">1</span><span style="font-family: 黑体">．</span><span lang="EN-US">make</span></p>
<p class="MsoNormal"><span style="font-family: 宋体">键入</span><span lang="EN-US">make</span><span style="font-family: 宋体">默认执行&#8221;</span><span lang="EN-US">make all</span><span style="font-family: 宋体">&#8221;命令，即目标体为</span><span lang="EN-US">all</span><span style="font-family: 宋体">，其执行情况如下所示：</span></p>
<p class="a0"><span lang="EN-US">&nbsp;</span></p>
<p class="a8"><span lang="EN-US">[root@localhost automake]#<strong> make</strong></span></p>
<p class="a8"><span lang="EN-US" style="letter-spacing: -0.3pt">if Gcc -D PACKAGE_NAME=\"\" -DPACKAGE_TARNAME=\"\" -DPACKAGE_VERSION=\"\" -DPACKAGE_STRING=\"\" -DPACKAGE_BUGREPORT=\"\" -DPACKAGE=\"hello\" -DVERSION=\"1.0\"&nbsp; -I. -I.&nbsp;&nbsp;&nbsp;&nbsp; -g -O2 -MT hello.o -MD -MP -MF ".deps/hello.Tpo" -c -o hello.o hello.c; \</span></p>
<p class="a8"><span lang="EN-US">then mv -f ".deps/hello.Tpo" ".deps/hello.Po"; else rm -f ".deps/hello.Tpo"; exit 1; fi</span></p>
<p class="a8"><strong><span lang="EN-US" style="color: #ff6600">Gcc&nbsp; -g -O2&nbsp;&nbsp; -o hello&nbsp; hello.o</span></strong></p>
<p class="MsoNormal"><span style="font-family: 宋体; letter-spacing: 0.2pt">此时在本目录下就生成了可执行文件&#8220;</span><span lang="EN-US" style="letter-spacing: 0.2pt">hello</span><span style="font-family: 宋体; letter-spacing: 0.2pt">&#8221;，运行&#8220;</span><span lang="EN-US" style="letter-spacing: 0.2pt">./hello</span><span style="font-family: 宋体; letter-spacing: 0.2pt">&#8221;能出现正常结果，如下所示：</span></p>
<p class="a0"><span lang="EN-US">&nbsp;</span></p>
<p class="a8"><span lang="EN-US">[root@localhost automake]# <strong>./hello</strong></span></p>
<p class="a8"><span lang="EN-US">Hello everyone!</span></p>
<h4><span lang="EN-US">2</span><span style="font-family: 黑体">．</span><span lang="EN-US">make install</span></h4>
<p class="MsoNormal"><span style="font-family: 宋体">此时，会把该程序安装到系统目录中去，如下所示：</span></p>
<p class="a0"><span lang="EN-US">&nbsp;</span></p>
<p class="a8"><span lang="EN-US">[root@localhost automake]#<strong> make install</strong></span></p>
<p class="a8"><span lang="EN-US" style="letter-spacing: -0.3pt">if Gcc -DPACKAGE_NAME=\"\" -DPACKAGE_TARNAME=\"\" -DPACKAGE_VERSION=\"\" -DPACKAGE_STRING=\"\" -DPACKAGE_BUGREPORT=\"\" -DPACKAGE=\"hello\" -DVERSION=\"1.0\"&nbsp; -I. -I.&nbsp;&nbsp;&nbsp;&nbsp; -g -O2 -MT hello.o -MD -MP -MF ".deps/hello.Tpo" -c -o hello.o hello.c; \</span></p>
<p class="a8"><span lang="EN-US">then mv -f ".deps/hello.Tpo" ".deps/hello.Po"; else rm -f ".deps/hello.Tpo"; exit 1; fi</span></p>
<p class="a8"><strong><span lang="EN-US">Gcc&nbsp; -g -O2&nbsp;&nbsp; -o hello&nbsp; hello.o</span></strong></p>
<p class="a8"><span lang="EN-US">make[1]: Entering directory '/root/workplace/automake'</span></p>
<p class="a8"><strong><span lang="EN-US"><span style="color: #ff6600">test -z "/usr/local/bin" || mkdir -p -- "/usr/local/bin"</span></span></strong></p>
<p class="a8"><strong><span lang="EN-US"><span style="color: #ff6600">&nbsp; /usr/bin/install -c 'hello' '/usr/local/bin/hello'</span></span></strong></p>
<p class="a8"><span lang="EN-US">make[1]: Nothing to be done for 'install-data-am'.</span></p>
<p class="a8"><span lang="EN-US">make[1]: LeaVing directory '/root/workplace/automake'</span></p>
<p class="a0"><span lang="EN-US">&nbsp;</span></p>
<p class="MsoNormal"><span style="font-family: 宋体">此时，若直接运行</span><span lang="EN-US">hello</span><span style="font-family: 宋体">，也能出现正确结果，如下所示：</span></p>
<p class="a0"><span lang="EN-US">&nbsp;</span></p>
<p class="a8"><span lang="EN-US">[root@localhost automake]# <strong>hello</strong></span></p>
<p class="a8"><span lang="EN-US">Hello everyone!</span><br />
<br />
</p>
<h4><span lang="EN-US">3</span><span style="font-family: 黑体">．</span><span lang="EN-US">make clean</span></h4>
<p class="MsoNormal"><span style="font-family: 宋体">此时，</span><span lang="EN-US">make</span><span style="font-family: 宋体">会清除之前所编译的可执行文件及目标文件（</span><span lang="EN-US">object file, *.o</span><span style="font-family: 宋体">），如下所示：</span></p>
<p class="a0"><span lang="EN-US">&nbsp;</span></p>
<p class="a8"><span lang="EN-US">[root@localhost automake]# <strong>make clean</strong></span></p>
<p class="a8"><span lang="EN-US">test -z "hello" || rm -f hello</span></p>
<p class="a8"><strong><span lang="EN-US">rm -f *.o</span></strong></p>
<h4><span lang="EN-US">4</span><span style="font-family: 黑体">．</span><span lang="EN-US">make dist</span></h4>
<p class="MsoNormal"><span style="font-family: 宋体">此时，</span><span lang="EN-US">make</span><span style="font-family: 宋体">将程序和相关的文档打包为一个压缩文档以供发布，如下所示：</span></p>
<p class="a0"><span lang="EN-US">&nbsp;</span></p>
<p class="a8"><span lang="EN-US">[root@localhost automake]# <strong>make dist</strong></span></p>
<p class="a8"><span lang="EN-US">[root@localhost automake]# ls hello-1.0-tar.gz</span></p>
<p class="a8"><span lang="EN-US">hello-1.0-tar.gz</span></p>
<p class="a0"><span lang="EN-US">&nbsp;</span></p>
<p class="MsoNormal"><span style="font-family: 宋体">可见该命令生成了一个</span><span lang="EN-US">hello-1.0-tar.gz</span><span style="font-family: 宋体">的压缩文件。</span></p>
<p class="MsoNormal"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp;由上面的讲述不难看出，</span><span lang="EN-US">autotools</span><span style="font-family: 宋体">确实是软件维护与发布的必备工具，也鉴于此，如今</span><span lang="EN-US">GUN</span><span style="font-family: 宋体">的软件一般都是由</span><span lang="EN-US">automake</span><span style="font-family: 宋体">来制作的。</span></p>
<br />
<!-- page --></span><!-- page -->
<img src ="http://www.blogjava.net/sunzhong/aggbug/297469.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sunzhong/" target="_blank">月光记忆</a> 2009-10-08 20:58 <a href="http://www.blogjava.net/sunzhong/articles/297469.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转载] C语言关键字volatile的使用</title><link>http://www.blogjava.net/sunzhong/articles/295916.html</link><dc:creator>月光记忆</dc:creator><author>月光记忆</author><pubDate>Mon, 21 Sep 2009 14:40:00 GMT</pubDate><guid>http://www.blogjava.net/sunzhong/articles/295916.html</guid><wfw:comment>http://www.blogjava.net/sunzhong/comments/295916.html</wfw:comment><comments>http://www.blogjava.net/sunzhong/articles/295916.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sunzhong/comments/commentRss/295916.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sunzhong/services/trackbacks/295916.html</trackback:ping><description><![CDATA[<p>&nbsp;</p>
<div>
<p><strong class="Apple-style-span" style="font-weight: bold">volatile原理是什么?</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; volatile的语义, 其实是告诉处理器, 不要将我放入工作内存, 请直接在主存操作我.(工作内存详见java内存模型)</p>
<p style="color: #ff9900"><span style="color: #000000">当多核或多线程在访问该变量时, 都将直接 操作 主存, 这从本质上, 做到了变量共享.<br />
<br />
因此volatile可以应用到以下场景:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1) 并行设备的硬件寄存器（如：状态寄存器） <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2) &nbsp;一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3) &nbsp;多线程应用中被几个任务共享的变量 <br />
</span><br />
<span style="color: #000000">由于访问寄存器的速度要快过RAM，所以编译器一般都会作减少存取外部RAM的优化。比如：<br />
</span><br />
<span style="color: #ff00ff">static int i=0; <br />
<br />
int main(void) <br />
{ <br />
&nbsp; ... <br />
&nbsp; while (1) <br />
&nbsp; { <br />
&nbsp;&nbsp;&nbsp; if (i) dosomething(); <br />
&nbsp; } <br />
} <br />
<br />
/* Interrupt service routine. */ <br />
void ISR_2(void) <br />
{ <br />
&nbsp; i=1; <br />
}&nbsp;<br />
</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #000000">程序的本意是希望ISR_2中断产生时，在main当中调用dosomething函数，但是，由于编译器判断在main函数里面没有修改过i，因此可能只执行一次对从i到某寄存器的读操作，然后每次if判断都只使用这个寄存器里面的&#8220;i副本&#8221;，导致dosomething永远也不会被调用。</span><span style="color: #ff0000">如果将将变量i加上volatile修饰，则编译器保证对此变量的读写操作都不会被优化（每次从内存中读取）</span>。<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #000000">再看下面代码,会有什么错误<br />
</span>&nbsp;<br />
<span style="color: #ff00ff">int square(volatile int *ptr) <br />
{ <br />
&nbsp;&nbsp; return *ptr * *ptr; <br />
}<br />
</span><br />
</p>
分析:<br />
这段代码里有个恶作剧。<span style="color: #ff9900">这段代码的目的是用来返指针*ptr指向值的平方</span>，但是，由于*ptr指向一个volatile型参数，编译器将产生类似下面的代码：<br />
<span style="color: #ff00ff">int square(volatile int *ptr) <br />
{<br />
&nbsp; int a,b;<br />
&nbsp; a = *ptr;<br />
&nbsp; b = *ptr;<br />
&nbsp; return a * b;<br />
}<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #ff9900">由</span><span style="color: #ff9900">于*ptr的值可能在赋予a之后,赋予b之前被改变，因此a和b可能是不同的</span>。结果，这段代码可能返回值不是你所期望的平方值！正确的代码如下：<br />
long square(volatile int *ptr)<br />
{<br />
&nbsp; int a;<br />
&nbsp; a = *ptr;<br />
&nbsp; return a * a;<br />
}<br />
<br />
<span style="color: #ff0000">一般说来，volatile用在如下的几个地方：<br />
1、中断服务程序中修改的供其它程序检测的变量需要加volatile；<br />
2、多任务环境下各任务间共享的标志应该加volatile；<br />
3、存储器映射的硬件寄存器通常也要加volatile说明，因为每次对它的读写都可能有不同意义；<br />
<br />
<br />
================================================================================ <br />
<br />
<span style="color: #000000">关键在于两个地方： <br />
<br />
1、编译器的优化 </span>
<p><br />
<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在本线程内，当读取一个变量时，为提高存取速度，编译器优化时有时会先把变量读取到一个寄存器中；以后，再取变量值时，就直接从寄存器中取值；当变量值在本线程里改变时，会同时把变量的新值copy到该寄存器中，以便保持一致；当变量在因别的线程等而改变了值，该寄存器的值不会相应改变，从而造成应用程序读取的值和实际的变量值不一致；当该寄存器在因别的线程等而改变了值，原变量的值不会改变，从而造成应用程序读取的值和实际的变量值不一致。<br />
<br />
<br />
2 .&nbsp;&nbsp;&nbsp; volatile应该解释为&#8220;直接存取原始内存地址&#8221;比较合适，称其&#8220;易变&#8221;是因为外在因素引起的，象多线程，中断等，并不是因为用volatile修饰了的变量就是&#8220;易变&#8221;了，假如没有外因，即使用volatile定义，它也不会变化；<br />
而用volatile定义之后，就能让编译器感知到这些外因造成的变化，可以放心使用了这些变量；<br />
</span></p>
<br />
－－－－－－－－－－－－简明示例如下－－－－－－－－－－－－－－－－－－&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #000000">volatile关键字是一种类型修饰符，用它声明的类型变量表示可以被某些编译器未知的因素更改，比如：操作系统、硬件或者其它线程等。遇到这个关键字声明的变量，编译器对访问该变量的代码就不再进行优化，从而可以提供对特定地址的稳定访问。<br />
使用该关键字的例子如下： <br />
int volatile nVint;<br />
&gt;&gt;&gt;&gt;当要求使用volatile 声明的变量的值的时候，系统总是重新从它所在的内存读取数据，即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。<br />
例如：<br />
volatile int i=10; <br />
int a = i;<br />
...<br />
//其他代码，并未明确告诉编译器对i进行过操作<br />
int b = i; <br />
&gt;&gt;&gt;&gt;volatile 指出i是随时可能发生变化的，每次使用它的时候必须从i的地址中读取，因而编译器生成的汇编代码会重新从i的地址读取数据放在b中。而对非volatile变量优化做法是，由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作，它会自动把上次读的数据放在b中。而不是重新从i里面读。这样以来，如果i是一个寄存器变量或者表示一个端口数据就容易出错，所以说volatile可以保证对特定地址的稳定访问。<br />
</span><br />
<br />
<span style="color: #000000">典型的例子：<br />
1.<br />
for ( int i=0; i&lt;100000; i++);<br />
这个语句用来测试空循环的速度的<br />
但是编译器肯定要把它优化掉，根本就不执行<br />
如果你写成：<br />
for ( volatile int i=0; i&lt;100000; i++);<br />
它就会执行了<br />
<br />
2.<br />
不做常量合并、常量传播等优化，所以像下面的代码：<br />
volatile int i = 1; <br />
if (i &gt; 0) ... <br />
<br />
if的条件不会当作无条件真。<br />
<br />
3、对volatile变量的读写不会被优化掉。如果你对一个变量赋值但后面没用到，编译器常常可以省略那个赋值操作，然而对Memory Mapped IO的处理是不能这样优化的。<br />
</span><br />
</span><br />
<br />
</div>
<img src ="http://www.blogjava.net/sunzhong/aggbug/295916.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sunzhong/" target="_blank">月光记忆</a> 2009-09-21 22:40 <a href="http://www.blogjava.net/sunzhong/articles/295916.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>sizeof()用法汇总</title><link>http://www.blogjava.net/sunzhong/articles/292173.html</link><dc:creator>月光记忆</dc:creator><author>月光记忆</author><pubDate>Fri, 21 Aug 2009 18:43:00 GMT</pubDate><guid>http://www.blogjava.net/sunzhong/articles/292173.html</guid><wfw:comment>http://www.blogjava.net/sunzhong/comments/292173.html</wfw:comment><comments>http://www.blogjava.net/sunzhong/articles/292173.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sunzhong/comments/commentRss/292173.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sunzhong/services/trackbacks/292173.html</trackback:ping><description><![CDATA[sizeof（）功能：计算数据空间的字节数
<p>&nbsp;&nbsp;&nbsp; 1.与strlen（）比较</p>
<p>&nbsp;&nbsp;&nbsp; strlen（）计算字符数组的字符数，以"\0"为结束判断，不计算为'\0'的数组元素。</p>
<p>&nbsp;&nbsp;&nbsp; 而sizeof计算数据（包括数组、变量、类型、结构体等）所占内存空间，用字节数表示。</p>
<p>&nbsp;&nbsp;&nbsp; 2.指针与静态数组(代表数组的变量)的sizeof操作</p>
<p>&nbsp;&nbsp;&nbsp; 指针均可看为变量类型的一种。所有指针变量的sizeof 操作结果均为4.</p>
<p>&nbsp;&nbsp;&nbsp; 注意：int *p； sizeof（p）=4；</p>
<p>&nbsp;&nbsp;&nbsp; 但sizeof（*p）相当于sizeof（int）；</p>
<p>&nbsp;&nbsp;&nbsp; 对于静态数组，sizeof可直接计算数组大小；</p>
<p>&nbsp;&nbsp;&nbsp; 例：int a[10]；char b[]="hello"；</p>
<p>&nbsp;&nbsp;&nbsp; sizeof（a）等于4*10=40；</p>
<p>&nbsp;&nbsp;&nbsp; sizeof（b）等于6；</p>
<p>&nbsp;&nbsp;&nbsp; 注意：数组名做型参时，数组名称只能当作指针使用！！</p>
<p>&nbsp;&nbsp;&nbsp; void&nbsp; fun（char p[]）</p>
<p>&nbsp;&nbsp;&nbsp; {sizeof（p）等于4}</p>
<p>&nbsp;&nbsp;&nbsp; 经典问题：</p>
<p>&nbsp;&nbsp;&nbsp; double* （*a）[3][6]；</p>
<p>&nbsp;&nbsp;&nbsp; cout&lt;&lt;sizeof（a）&lt;&lt;endl； // 4 a为指针</p>
<p>&nbsp;&nbsp;&nbsp; cout&lt;&lt;sizeof（*a）&lt;&lt;endl； // 72 *a为一个有3*6个指针元素的数组</p>
<p>&nbsp;&nbsp;&nbsp; cout&lt;&lt;sizeof（**a）&lt;&lt;endl； // 24 **a为数组一维的6个指针</p>
<p>&nbsp;&nbsp;&nbsp; cout&lt;&lt;sizeof（***a）&lt;&lt;endl； // 4 ***a为一维的第一个指针</p>
<p>&nbsp;&nbsp;&nbsp; cout&lt;&lt;sizeof（****a）&lt;&lt;endl； // 8 ****a为一个double变量</p>
<p>&nbsp;&nbsp;&nbsp; 问题解析：a是一个很奇怪的定义，他表示一个指向维度为[3][6]元素为double * 数组的指针。既然是指针，所以sizeof（a）就是4.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;既然a是执行double*[3][6]类型的指针，*a就表示一个double*[3][6]的多维数组类型，因此sizeof（*a）=3*6*sizeof(double*)=72.同样的，double*[3][6]实际上可以看成一个指针,指向所代表数组的起始元素,由前可知,*a指向一个类型为double* [6]的数据,那么**a就表示一个double*[6]类型的数组，所以sizeof（**a）=6*sizeof&nbsp; （double*）=24.***a就表示其中的一个元素，也就是double*了，所以sizeof（***a）=4.至于****a，就是一个double了，所以sizeof（****a）=sizeof（double）=8.</p>
<p>&nbsp;&nbsp;&nbsp; 3.格式的写法</p>
<p>&nbsp;&nbsp;&nbsp; sizeof操作符，对变量或对象可以不加括号，但若是类型，须加括号。</p>
<p>&nbsp;&nbsp;&nbsp; 4.使用sizeof时string的注意事项</p>
<p>&nbsp;&nbsp;&nbsp; string s="hello"；</p>
<p>&nbsp;&nbsp;&nbsp; sizeof（s）等于string类的大小，sizeof（s.c_str（））得到的是与字符串长度。</p>
<p>&nbsp;&nbsp;&nbsp; 5.union 与struct的空间计算</p>
<p>&nbsp;&nbsp;&nbsp; 总体上遵循两个原则：</p>
<p>&nbsp;&nbsp;&nbsp; （1）整体空间是 占用空间最大的成员（的类型）所占字节数的整倍数</p>
<p>&nbsp;&nbsp;&nbsp; （2）数据对齐原则——内存按结构成员的先后顺序排列，当排到该成员变量时，其距离结构体起始的偏移量必须是该成员类型大小的整倍数，如果不够则补齐，以此向后类推&#8230;&#8230;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 也就是说各成员变量存放的起始地址相对于结构体的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (3)构成的新类型的对齐方式是空间最大的成员的对齐方式</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(4)注意：因为数组按照单个变量一个一个的摆放，所以就不是看成整体。他的对齐方式是她的元素对齐方式<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(5)如果成员中有自定义的类、结构体，也要注意数组问题。</p>
<p>&nbsp;&nbsp;&nbsp; 类型对齐方式（变量存放的起始地址相对于结构的起始地址的偏移量）&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;Char&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;偏移量必须为sizeof(char)即1的倍数&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;偏移量必须为sizeof(int)即4的倍数&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;偏移量必须为sizeof(float)即4的倍数&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;double&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;偏移量必须为sizeof(double)即8的倍数&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;Short&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;偏移量必须为sizeof(short)即2的倍数&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;各成员变量在存放的时候根据在结构中出现的顺序依次申请空间，同时按照上面的对齐方式调整位置，空缺的字节会自动填充。同时为了确保结构的大小为结构的字节边界基数（即该结构中占用最大空间的类型所占用的字节数）的倍数，所以在为最后一个成员变量申请空间后，还会根据需要自动填充空缺的字节。 <br />
</p>
<p>&nbsp;&nbsp;&nbsp; 因为对齐问题使结构体的sizeof变得比较复杂，看下面的例子：（默认对齐方式下）<br />
<table bordercolor="#cccccc" cellspacing="0" cellpadding="3" width="100%" bgcolor="#ffffff" border="2">
    <tbody>
        <tr>
            <td>
            <p>&nbsp;struct s1<br />
            {<br />
            char a;<br />
            double b;<br />
            int c;<br />
            char d;<br />
            };</p>
            <p>struct s2<br />
            {<br />
            char a;<br />
            char b;<br />
            int c;<br />
            double d;<br />
            };</p>
            <p>cout&lt;&lt;sizeof(s1)&lt;&lt;endl; // 24<br />
            cout&lt;&lt;sizeof(s2)&lt;&lt;endl; // 16</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;&nbsp;&nbsp; 同样是两个char类型，一个int类型，一个double类型，但是因为对齐问题，导致他们的大小不同。计算结构体大小可以采用元素摆放法，我举例子说明一下：首先，CPU判断结构体的边界基数，根据上一节的结论，s1和s2的边界基数都取最大的元素类型，也就是double类型的sizeof(double)=8.然后开始摆放每个元素。</p>
<p>&nbsp;&nbsp;&nbsp; 对于s1，因为0是任何类型大小的倍数，所以都从0开始,先放一个sizeof(char)=1的a变量,此时下一个空闲的地址是1，但是下一个元素d是double类型，sizeof(double)=8 ，需要补足7个字节才能使距离结构开始偏移量变为8(满足对齐方式），因此自动填充7个字节，d存放在偏移量为8 的地址上，它占用8个字节。 此时下一个空闲地址变成了16，下一个元素c的大小是4，16可以满足，所以c放在了16，此时下一个空闲地址变成了20，下一个元素d大小是1，所以距离结构任何偏移量都满足，结构体在地址21处结束。由于s1的大小需要是边界基数8的倍数，所以21-23的空间被空白填上，s1的大小变成了24.</p>
<p>&nbsp;&nbsp;&nbsp; 对于s2，首先把a放到0处，此时下一个空闲地址是1，下一个元素的大小也是1，所以b摆放在1，下一个空闲地址变成了2；下一个元素c的大小是4，要取离最近的大于2并且是4倍数的位置来摆放c，就是4了,放下c后下一个空闲地址变成了8，下一个元素d的大小是8，所以d摆放在偏移量为8的地方，所有元素摆放完毕，结构体在16处结束，占用总空间为16，正好是8的倍数。</p>
<p>&nbsp;&nbsp;&nbsp; 这里有个陷阱，对于结构体中的结构体成员，不要认为它的对齐方式就是他的大小，看下面的例子：<br />
<table bordercolor="#cccccc" cellspacing="0" cellpadding="3" width="100%" bgcolor="#ffffff" border="2">
    <tbody>
        <tr>
            <td>
            <p>&nbsp;struct s1<br />
            {<br />
            char a[8];<br />
            };</p>
            <p>struct s2<br />
            {<br />
            double d;<br />
            };</p>
            <p>struct s3<br />
            {<br />
            s1 s;<br />
            char a;<br />
            };</p>
            <p>struct s4<br />
            {<br />
            s2 s;<br />
            char a;<br />
            };<br />
            cout&lt;&lt;sizeof(s1)&lt;&lt;endl; // 8<br />
            cout&lt;&lt;sizeof(s2)&lt;&lt;endl; // 8<br />
            cout&lt;&lt;sizeof(s3)&lt;&lt;endl; // 9<br />
            cout&lt;&lt;sizeof(s4)&lt;&lt;endl; // 16;<br />
            </p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s1和s2大小虽然都是8，但最终导致了在s3和s4中才有占用空间大小的差异。这是因为他们的边界基数不一样所致.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s3中占用空间最大的成员是s1,虽然他的大小为1,但他的对齐方式是1的倍数,这是因为s1是由一个数组构成,数组是他唯一在自然也是他最大的成员,所以数组的对齐方式就是s1的对齐方式,如前所述,数组的对齐方式是元素的对齐方式,所以综上所述,s1的对齐方式居然是数组元素的对齐方式.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s4的边界基数由其占用最大的s2来决定的,s2的对齐方式自然是他唯一成员double所决定.所以s4的边界基数为8,大小就该是16.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 所以，在自己定义结构体的时候，如果空间紧张的话，最好考虑对齐因素来排列结构体里的元素,最好不要让double干扰你的位域.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;总结下,计算结构的大小,要考虑到这几个指标:一个结构的边界基数,二是成员的对齐方式,以及成员的大小:<br />
占用空间最大的成员的对齐方式自然是结构的边界基数;<br />
如果成员也是结构的话,那么对齐方式不是其大小,而是他的最大的成员的对齐方式,<br />
如果成员是数组的话,那么对齐方式是元素的对齐方式.</p>
<p>&nbsp;&nbsp;&nbsp; 在结构体和类中，可以使用位域来规定某个成员所能占用的空间，所以使用位域能在一定程度上节省结构体占用的空间。不过考虑下面的代码：<br />
<table bordercolor="#cccccc" cellspacing="0" cellpadding="3" width="100%" bgcolor="#ffffff" border="2">
    <tbody>
        <tr>
            <td>
            <p>&nbsp;struct s1<br />
            {<br />
            　int i: 8;<br />
            　int j: 4;<br />
            　double b;<br />
            　int a:3;<br />
            };</p>
            <p>struct s2<br />
            {<br />
            　int i;<br />
            　int j;<br />
            　double b;<br />
            　int a;<br />
            };</p>
            <p>struct s3<br />
            {<br />
            　int i;<br />
            　int j;<br />
            　int a;<br />
            　double b;<br />
            };</p>
            <p>struct s4<br />
            {<br />
            　int i: 8;<br />
            　int j: 4;<br />
            　int a:3;<br />
            　double b;<br />
            };</p>
            <p>cout&lt;&lt;sizeof(s1)&lt;&lt;endl; // 24<br />
            cout&lt;&lt;sizeof(s2)&lt;&lt;endl; // 24<br />
            cout&lt;&lt;sizeof(s3)&lt;&lt;endl; // 24<br />
            cout&lt;&lt;sizeof(s4)&lt;&lt;endl; // 16<br />
            </p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;&nbsp;&nbsp; 可以看到，有double存在会干涉到位域（sizeof的算法参考上一节），所以使用位域的的时候，最好把float类型和double类型放在程序的开始或者最后。</p>
<p>&nbsp;&nbsp;&nbsp; 相关常数：</p>
<p>&nbsp;&nbsp;&nbsp; sizeof int：4</p>
<p>&nbsp;&nbsp;&nbsp; sizeof short：2</p>
<p>&nbsp;&nbsp;&nbsp; sizeof long：4</p>
<p>&nbsp;&nbsp;&nbsp; sizeof float：4</p>
<p>&nbsp;&nbsp;&nbsp; sizeof double：8</p>
<p>&nbsp;&nbsp;&nbsp; sizeof char：1</p>
<p>&nbsp;&nbsp;&nbsp; sizeof p：4</p>
<p>&nbsp;&nbsp;&nbsp; sizeof WORD：2</p>
<p>&nbsp;&nbsp;&nbsp; sizeof DWORD：4</p>
<img src ="http://www.blogjava.net/sunzhong/aggbug/292173.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sunzhong/" target="_blank">月光记忆</a> 2009-08-22 02:43 <a href="http://www.blogjava.net/sunzhong/articles/292173.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux动态库(.so)搜索路径</title><link>http://www.blogjava.net/sunzhong/articles/289397.html</link><dc:creator>月光记忆</dc:creator><author>月光记忆</author><pubDate>Sat, 01 Aug 2009 07:39:00 GMT</pubDate><guid>http://www.blogjava.net/sunzhong/articles/289397.html</guid><wfw:comment>http://www.blogjava.net/sunzhong/comments/289397.html</wfw:comment><comments>http://www.blogjava.net/sunzhong/articles/289397.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sunzhong/comments/commentRss/289397.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sunzhong/services/trackbacks/289397.html</trackback:ping><description><![CDATA[<div class="postTitle">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 众所周知，Linux动态库的默认搜索路径是/lib和/usr/lib。动态库被创建后，一般都复制到这两个目录中。当程序执行时需要某动态库，并且该 动 态库还未加载到内存中，则系统会自动到这两个默认搜索路径中去查找相应的动态库文件，然后加载该文件到内存中，这样程序就可以使用该动态库中的函数，以及 该动态库的其它资源了。在Linux 中，动态库的搜索路径除了默认的搜索路径外，还可以通过以下三种方法来指定。 </div>
<p><span style="color: #ff9900">方法一：在配置文件/etc/ld.so.conf中指定动态库搜索路径。</span>
<p>可以通过编辑配置文件/etc/ld.so.conf来指定动态库的搜索路径，该文件中每行为一个动态库搜索路径。每次编辑完该文件后，都必须运行命令ldconfig使修改后的配置生效。我们通过例1来说明该方法。
<p>例1：
<p>我们通过以下命令用源程序pos_conf.c（见程序1）来创建动态库 libpos.so
<p>&nbsp;(1)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #00ff00"><span style="color: #00ccff"># gcc -c pos_conf.c<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # gcc -shared -fPCI -o libpos.so pos_conf.o</span><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #ff0000">程序1: pos_conf.c程序1: pos_conf.c<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #include &lt;stdio.h&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void pos()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("/root/test/conf/lib\n");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<p>&nbsp;(2)&nbsp;&nbsp;&nbsp;&nbsp;接着通过以下命令编译main.c（见程序2）生成目标程序pos。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #00ccff"># gcc -o pos main.c -L. -lpos</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #ff0000">程序2: main.c&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void pos();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int main()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pos();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
<br />
(3)&nbsp; 然后把库文件移动到目录/root/test/conf/lib中。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #00ccff"># mkdir -p /root/test/conf/lib<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # mv libpos.so /root/test/conf/lib</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<p>&nbsp;(4)&nbsp; 最后编辑配置文件/etc/ld.so.conf，在该文件中追加一行"/root/test/conf/lib"。
<p>&nbsp;&nbsp;&nbsp;&nbsp; 运行程序pos试试。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #00ccff"># ./pos<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ./pos: error while loading shared libraries: libpos.so: cannot open shared object file: No such file or directory<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 出错了，系统未找到动态库libpos.so。找找原因，原来在编辑完配置文件/etc/ld.so.conf后，没有运行命令ldconfig，所以刚才的修改还未生效。我们运行ldconfig后再试试。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #00ccff"># ldconfig<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # ./pos<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/root/test/conf/lib&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 程序pos运行成功，并且打印出正确结果。
<p>&nbsp;<span style="color: #ff9900">方法二：通过环境变量LD_LIBRARY_PATH指定动态库搜索路径。</span>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 通过设定环境变量LD_LIBRARY_PATH也可以指定动态库搜索路径。当通过该环境变量指定多个动态库搜索路径时，路径之间用冒号"："分隔。下面通过例2来说明本方法。
<p>例2： 我们通过以下命令用源程序pos_env.c（见程序3）来创建动态库libpos.so。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #00ccff"># gcc -c pos_env.c<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # gcc -shared -fPCI -o libpos.so pos_env.o<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #ff0000">程序3: pos_env.c&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #include &lt;stdio.h&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void pos()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("/root/test/env/lib\n");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 测试用的可执行文件pos可以使用例1中的得到的目标程序pos，不需要再次编译,链接。因为pos_conf.c中的函数pos和pos_env.c中的函数pos 函数原型一致，且动态库名相同，只是运行时搜索路径发生了变化.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这就好比修改动态库pos后重新创建该库一样。这也是使用动态库的优点之一。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 然后把动态库libpos.so移动到目录/root/test/conf/lib中。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #00ccff"># mkdir -p /root/test/env/lib<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # mv libpos.so /root/test/env/lib<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们可以使用export来设置该环境变量，在设置该环境变量后所有的命令中，该环境变量都有效。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 例如： # export LD_LIBRARY_PATH=/root/test/env/lib<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 但本文为了举例方便，使用另一种设置环境变量的方法，既在命令前加环境变量设置，该环境变量只对该命令有效，当该命令执行完成后，该环境变量就无效了。如下述命令：
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #00ccff"># LD_LIBRARY_PATH=/root/test/env/lib&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #./pos<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /root/test/env/lib<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 程序pos运行成功，并且打印的结果是"/root/test/env/lib"，正是程序pos_env.c中的函数pos的运行结果。因此程序pos搜索到的动态库是/root/test/env/lib/libpos.so。
<p><span style="color: #ff9900">&nbsp;方法三：在编译目标代码时指定该程序的动态库搜索路径。</span>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 还可以在编译目标代码时指定程序的动态库搜索路径。这是通过gcc 的参数"-Wl,-rpath,"指定（如例3所示）。当指定多个动态库搜索路径时，路径之间用冒号"："分隔。
<p>例3： 我们通过以下命令用源程序pos.c（见程序4）来创建动态库libpos.so。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #00ccff"># gcc -c pos.c<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # gcc -shared -fPCI -o libpos.so pos.o<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #ff0000">程序4: pos.c&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #include &lt;stdio.h&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void pos()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("./\n");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 因为我们需要<span style="color: #ff00ff">在编译目标代码时指定可执行文件的动态库搜索路径</span>，所以需要用gcc命令重新编译源程序main.c(见程序2)来生成可执行文件pos。
<p>&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #00ccff"># gcc -o pos main.c -L. -lpos -Wl,-rpath,./<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #ff00ff">注意了哈,-L -l 指定的链接路径,-Wl,-rpath,./是运行时被导入库的搜索路径,对于使用动态链接来说,搜索路径作用远大于链接路径.一般来说,链接用来指定只有函数声明的头文件,用来检查语法错误,搜索则是指定了运行时要使用的实现.</span>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 再运行程序pos试试。
<p>&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #00ccff"># ./pos<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;./
<p>&nbsp;&nbsp;&nbsp; 程序pos运行成功，输出的结果正是pos.c中的函数pos的运行结果。说明程序pos搜索到的动态库是./libpos.so。
<p>&nbsp;&nbsp;&nbsp; 以上介绍了三种指定动态库搜索路径的方法，加上默认的动态库搜索路径/lib和/usr/lib，共五种动态库的搜索路径，那么它们搜索的先后顺序是什么呢？
<p>&nbsp;&nbsp;&nbsp;&nbsp; 在介绍上述三种方法时，分别创建了动态库./libpos.so、 /root/test/env/lib/libpos.so和/root/test/conf/lib/libpos.so。我们再用源程序 pos_lib.c（见程序5）来创建动态库/lib/libpos.so，用源程序pos_usrlib.c（见程序6）来创建动态库 /usr/lib/libpos.so。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #ff0000">程序5: pos_lib.c<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #include &lt;stdio.h&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void pos()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("/lib\n");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #ff0000">程序6: pos_usrlib.c<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #include &lt;stdio.h&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void pos()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("/usr/lib\n");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这样我们得到五个动态库libpos.so，这些动态库的名字相同，且都包含相同函数原型的公用函数pos。<span style="color: #ff00ff">但存储的位置不同和公用函数pos 打印的结果不同。</span><span style="color: #ff00ff">每个动态库中的公用函数pos都输出该动态库所存放的位置。</span>这样我们可以通过执行例3中的可执行文件pos得到的结果不同获知其搜索到了 哪个动态库，从而获得第1个动态库搜索顺序，然后删除该动态库，再执行程序pos，获得第2个动态库搜索路径，再删除第2个被搜索到的动态库，如此往复， 将可得到Linux搜索动态库的先后顺序。程序pos执行的输出结果和搜索到的动态库的对应关系如表1所示：
<p>程序pos输出结果<br />
使用的动态库<br />
对应的动态库搜索路径指定方式
<p><span style="color: #ff00ff">./<br />
./libpos.so<br />
编译目标代码时指定的动态库搜索路径 </span>
<p><span style="color: #ff00ff">/root/test/env/lib<br />
/root/test/env/lib/libpos.so<br />
环境变量LD_LIBRARY_PATH指定的动态库搜索路径 </span>
<p><span style="color: #ff00ff">/root/test/conf/lib<br />
/root/test/conf/lib/libpos.so<br />
配置文件/etc/ld.so.conf中指定的动态库搜索路径 </span>
<p><span style="color: #ff00ff">/lib<br />
/lib/libpos.so<br />
默认的动态库搜索路径/lib </span>
<p><span style="color: #ff00ff">/usr/lib<br />
/usr/lib/libpos.so<br />
默认的动态库搜索路径/usr/lib</span>
<p><span style="color: #ff00ff">表1: 程序pos输出结果和动态库的对应关系</span>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 创建各个动态库，并放置在相应的目录中。测试环境就准备好了。执行程序pos，并在该命令行中设置环境变量LD_LIBRARY_PATH。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #00ccff">&nbsp;# LD_LIBRARY_PATH=/root/test/env/lib&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #00ccff"># gcc -o pos main.c -L. -lpos -Wl,-rpath,./</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #./pos<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ./<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #ff00ff">根据程序pos的输出结果可知，最先搜索的是编译目标代码时指定的动态库搜索路径。<br />
</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 然后我们把动态库./libpos.so删除了，再运行上述命令试试。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #00ccff">&nbsp;# rm libpos.so<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rm: remove regular file `libpos.so'? y<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # LD_LIBRARY_PATH=/root/test/env/lib&nbsp;</span><span style="color: #00ffff"><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #00ccff"># gcc -o pos main.c -L. -lpos -Wl,-rpath,./</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #00ffff">#./pos<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /root/test/env/lib<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #ff00ff">根据程序pos的输出结果可知，第2个动态库搜索的路径是环境变量LD_LIBRARY_PATH指定的。<br />
</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们再把/root/test/env/lib/libpos.so删除，运行上述命令。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #00ccff"># rm /root/test/env/lib/libpos.so<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rm: remove regular file `/root/test/env/lib/libpos.so'? y<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # LD_LIBRARY_PATH=/root/test/env/lib&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #00ccff"># gcc -o pos main.c -L. -lpos -Wl,-rpath,./</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #./pos<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /root/test/conf/lib<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #ff00ff">根据程序pos的输出结果可知<font color="#000000">,</font></span><span style="color: #ff00ff">第3个动态库的搜索路径是配置文件/etc/ld.so.conf指定的路径</span>。<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 删除动态库/root/test/conf/lib/libpos.so后再运行上述命令。
<p style="color: #ff00ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #00ccff"># rm /root/test/conf/lib/libpos.so<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rm: remove regular file `/root/test/conf/lib/libpos.so'? y<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # LD_LIBRARY_PATH=/root/test/env/lib<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #00ccff"># gcc -o pos main.c -L. -lpos -Wl,-rpath,./</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #./pos<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /lib<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #ff00ff">根据程序pos的输出结果可知<font color="#000000">,</font></span><span style="color: #ff00ff">第4个动态库的搜索路径是默认搜索路径/lib。</span><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们再删除动态库/lib/libpos.so，运行上述命令。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #00ccff">&nbsp;# rm /lib/libpos.so<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rm: remove regular file `/lib/libpos.so'? y<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # LD_LIBRARY_PATH=/root/test/env/lib&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # gcc -o pos main.c -L. -lpos -Wl,-rpath,./<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #./pos<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /usr/lib
<p style="color: #ff00ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #ff00ff">最后的动态库搜索路径是默认搜索路径/usr/lib。</span>
<p><span style="color: #ff6600">综合以上结果可知，动态库的搜索路径搜索的先后顺序是： </span>
<p><span style="color: #ff6600">1.编译目标代码时指定的动态库搜索路径； </span>
<p><span style="color: #ff6600">2.环境变量LD_LIBRARY_PATH指定的动态库搜索路径； </span>
<p><span style="color: #ff6600">3.配置文件/etc/ld.so.conf中指定的动态库搜索路径； </span>
<p><span style="color: #ff6600">4.默认的动态库搜索路径/lib;</span>
<p><span style="color: #ff6600">5.默认的动态库搜索路径/usr/lib。</span>
<p style="color: #ff00ff">在上述1、2、3指定动态库搜索路径时，都可指定多个动态库搜索路径，其搜索的先后顺序是按指定路径的先后顺序搜索的。 </p>
<img src ="http://www.blogjava.net/sunzhong/aggbug/289397.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sunzhong/" target="_blank">月光记忆</a> 2009-08-01 15:39 <a href="http://www.blogjava.net/sunzhong/articles/289397.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于Linux静态库和动态库的分析</title><link>http://www.blogjava.net/sunzhong/articles/289377.html</link><dc:creator>月光记忆</dc:creator><author>月光记忆</author><pubDate>Sat, 01 Aug 2009 04:10:00 GMT</pubDate><guid>http://www.blogjava.net/sunzhong/articles/289377.html</guid><wfw:comment>http://www.blogjava.net/sunzhong/comments/289377.html</wfw:comment><comments>http://www.blogjava.net/sunzhong/articles/289377.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sunzhong/comments/commentRss/289377.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sunzhong/services/trackbacks/289377.html</trackback:ping><description><![CDATA[<strong><font color="#0000ff">1.什么是库</font></strong><br />
在windows平台和linux平台下都大量存在着库。<br />
<font color="#ff0000">本质上来说库是一种可执行代码的二进制形式，可以被操作系统载入内存执行。</font><br />
由于windows和linux的本质不同，因此二者库的二进制是不兼容的。<br />
本文仅限于介绍linux下的库。<br />
<strong><font color="#0000ff">2.库的种类<br />
</font></strong>linux下的库有两种：静态库和共享库（动态库）。<br />
二者的不同点在于代码被载入的时刻不同。<br />
<font color="#ff0000">静态库的代码在编译过程中已经被载入可执行程序，因此体积较大。<br />
共享库的代码是在可执行程序运行时才载入内存的，在编译过程中仅简单的引用，因此代码体积较小。</font><br />
<strong><font color="#0000ff">3.库存在的意义</font></strong><br />
库是别人写好的现有的，成熟的，可以复用的代码，你可以使用但要记得遵守许可协议。<br />
现实中每个程序都要依赖很多基础的底层库，不可能每个人的代码都从零开始，因此库的存在意义非同寻常。<br />
共享库的好处是，不同的应用程序如果调用相同的库，那么在内存里只需要有一份该共享库的实例。<br />
<font color="#0000ff"><strong>4.库文件是如何产生的</strong></font><strong><font color="#0000ff">在linux下</font></strong><br />
<font color="#ff0000">静态库的后缀是.a，它的产生分两步<br />
Step 1.由源文件编译生成一堆.o，<u>每个.o里都包含这个编译单元的符号表<br />
</u>Step 2.ar命令将很多.o转换成.a，成文静态库<br />
动态库的后缀是.so，它由gcc加特定参数编译产生。<br />
</font>例如:<br />
$ gcc -fPIC -c *.c $ gcc -shared -Wl,-soname, libfoo.so.1 -o libfoo.so.1.0 *.<br />
<strong><font color="#0000ff">5.库文件是如何命名的，有没有什么规范<br />
</font></strong>在linux下，库文件一般放在/usr/lib /lib下，<br />
<font color="#ff0000">静态库的名字一般为libxxxx.a，其中xxxx是该lib的名称<br />
动态库的名字一般为libxxxx.so.major.minor，xxxx是该lib的名称，major是主版本号， minor是副版本号</font><br />
<strong><font color="#0000ff">6.如何知道一个可执行程序依赖哪些库</font></strong><br />
ldd命令可以查看一个可执行程序依赖的共享库，<br />
例如# ldd /bin/lnlibc.so.6<br />
=&gt; /lib/libc.so.6 (0&#215;40021000)/lib/ld-linux.so.2<br />
=&gt; /lib/ld- linux.so.2 (0&#215;40000000)<br />
可以看到ln命令依赖于libc库和ld-linux库<br />
<strong><font color="#0000ff">7.可执行程序在执行的时候如何定位共享库文件</font></strong><br />
当系统加载可执行代码时候，能够知道其所依赖的库的名字，但是还需要知道绝对路径<br />
此时就需要系统动态载入器(dynamic linker/loader)<br />
<font color="#ff0000">对于elf格式的可执行程序，是由ld-linux.so*来完成的，它先后搜索elf文件的</font><font color="#ff0000"> DT_RPATH段—环境变量LD_LIBRARY_PATH—/etc/ld.so.cache文件列表—/lib/,/usr/lib目录找到库文件后将其载入内存</font><br />
<strong><font color="#0000ff">8.在新安装一个库之后如何让系统能够找到他<br />
</font></strong>如果安装在/lib或者/usr/lib下，那么ld默认能够找到，无需其他操作。<br />
<font color="#ff0000">如果安装在其他目录，需要将其添加到/etc/ld.so.cache文件中，步骤如下<br />
1.编辑/etc/ld.so.conf文件，加入库文件所在目录的路径<br />
2.运行ldconfig，该命令会重建/etc/ld.so.cache文件</font><br />
<font color="#ff0000"><br />
</font><font color="#ff0000">我们通常把一些公用函数制作成函数库，供其它程序使用。函数库分为静态库和动态库两种。静态库在程序编译时会被连接到目标代码中，程序运行时将不再需要该静态库。动态库在程序编译时并不会被连接到目标代码中，而是在程序运行是才被载入，因此在程序运行时还需要动态库存在。本文主要通过举例来说明在Linux中如何创建静态库和动态库，以及使用它们。在创建函数库前，我们先来准备举例用的源程序，并将函数库的源程序编译成.o文件。</font> <br />
<font color="#0000ff"><strong>第1步：编辑得到举例的程序--hello.h、hello.c和main.c；</strong> </font><br />
hello.h(见程序1)为该函数库的头文件。<br />
hello.c(见程序2)是函数库的源程序，其中包含公用函数hello，该函数将在屏幕上输出"Hello XXX!"。<br />
main.c(见程序3)为测试库文件的主程序，在主程序中调用了公用函数hello。 <br />
程序1: hello.h<br />
#ifndef HELLO_H<br />
#define HELLO_H<br />
<br />
void hello(const char *name);<br />
<br />
#endif //HELLO_H<br />
程序2: hello.c<br />
#include <br />
<br />
void hello(const char *name)<br />
{<br />
&nbsp;&nbsp;printf("Hello %s!\n", name);<br />
}<br />
&nbsp;&nbsp;程序3: main.c<br />
#include "hello.h"<br />
<br />
int main()<br />
{<br />
&nbsp;&nbsp;hello("everyone");<br />
&nbsp;&nbsp;return 0;<br />
}<br />
<font color="#0000ff"><strong>第2步：将hello.c编译成.o文件； </strong></font><br />
无论静态库，还是动态库，都是由.o文件创建的。因此，我们必须将源程序hello.c通过gcc先编译成.o文件。 <br />
在系统提示符下键入以下命令得到hello.o文件。 <br />
# gcc -c hello.c <br />
# <br />
(注1：本文不介绍各命令使用和其参数功能，若希望详细了解它们，请参考其他文档。) <br />
(注2：首字符"#"是系统提示符，不需要键入，下文相同。) <br />
我们运行ls命令看看是否生存了hello.o文件。 <br />
# ls <br />
hello.c hello.h hello.o main.c <br />
# <br />
(注3：首字符不是"#"为系统运行结果，下文相同。) <br />
在ls命令结果中，我们看到了hello.o文件，本步操作完成。 <br />
下面我们先来看看如何创建静态库，以及使用它。 <br />
<strong><font color="#0000ff">第3步：由.o文件创建静态库；</font></strong> <br />
静态库文件名的命名规范是以lib为前缀，紧接着跟静态库名，扩展名为.a。例如：我们将创建的静态库名为myhello，则静态库文件名就是libmyhello.a。在创建和使用静态库时，需要注意这点。创建静态库用ar命令。 <br />
在系统提示符下键入以下命令将创建静态库文件libmyhello.a。 <br />
# ar cr libmyhello.a hello.o <br />
# <br />
我们同样运行ls命令查看结果： <br />
# ls <br />
hello.c hello.h hello.o libmyhello.a main.c <br />
# <br />
ls命令结果中有libmyhello.a。 <br />
<font color="#0000ff"><strong>第4步：在程序中使用静态库；</strong></font> <br />
静态库制作完了，如何使用它内部的函数呢？只需要在使用到这些公用函数的源程序中包含这些公用函数的原型声明，然后在用gcc命令生成目标文件时指明静态库名，gcc将会从静态库中将公用函数连接到目标文件中。<font color="#ff0000">注意，gcc会在静态库名前加上前缀lib，然后追加扩展名.a得到的静态库文件名来查找静态库文件。 </font><br />
在程序3:main.c中，我们包含了静态库的头文件hello.h，然后在主程序main中直接调用公用函数hello。下面先生成目标程序hello，然后运行hello程序看看结果如何。 <br />
# gcc -o hello main.c <font color="#ff0000">-L. -lmyhello </font><br />
# ./hello <br />
Hello everyone! <br />
# <br />
我们删除静态库文件试试公用函数hello是否真的连接到目标文件 hello中了。 <br />
# rm libmyhello.a <br />
rm: remove regular file `libmyhello.a'? y <br />
# ./hello <br />
Hello everyone! <br />
# <br />
程序照常运行，静态库中的公用函数已经连接到目标文件中了。 <br />
我们继续看看如何在Linux中创建动态库。我们还是从.o文件开始。 <br />
<strong><font color="#0000ff">第5步：由.o文件创建动态库文件；</font></strong> <br />
动态库文件名命名规范和静态库文件名命名规范类似，也是在动态库名增加前缀lib，但其文件扩展名为.so。例如：我们将创建的动态库名为myhello，则动态库文件名就是libmyhello.so。用gcc来创建动态库。 <br />
在系统提示符下键入以下命令得到动态库文件libmyhello.so。 <br />
# gcc -shared -fPCI -o libmyhello.so hello.o <br />
# <br />
我们照样使用ls命令看看动态库文件是否生成。 <br />
# ls <br />
hello.c hello.h hello.o libmyhello.so main.c <br />
# <br />
<strong><font color="#0000ff">第6步：在程序中使用动态库；</font></strong> <br />
在程序中使用动态库和使用静态库完全一样，也是在使用到这些公用函数的源程序中包含这些公用函数的原型声明，然后在用gcc命令生成目标文件时指明动态库名进行编译。我们先运行gcc命令生成目标文件，再运行它看看结果。 <br />
# gcc -o hello main.c <font color="#ff0000">-L. -lmyhello </font><br />
# ./hello <br />
./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory <br />
# <br />
哦！出错了。快看看错误提示，原来是找不到动态库文件libmyhello.so。程序在运行时，会在/usr/lib和/lib等目录中查找需要的动态库文件。若找到，则载入动态库，否则将提示类似上述错误而终止程序运行。我们将文件libmyhello.so复制到目录/usr/lib中，再试试。 <br />
# mv libmyhello.so /usr/lib <br />
# ./hello <br />
./hello: error while loading shared libraries: /usr/lib/libhello.so: cannot restore segment prot after reloc: Permission denied<br />
由于SELinux引起，<br />
# chcon -t texrel_shlib_t /usr/lib/libhello.so<br />
# ./hello<br />
Hello everyone! <br />
# <br />
成功了。这也进一步说明了动态库在程序运行时是需要的。 <br />
我们回过头看看，发现使用静态库和使用动态库编译成目标程序使用的gcc命令完全一样，那当静态库和动态库同名时，gcc命令会使用哪个库文件呢？抱着对问题必究到底的心情，来试试看。 <br />
先删除 除.c和.h外的 所有文件，恢复成我们刚刚编辑完举例程序状态。 <br />
# rm -f hello hello.o /usr/lib/libmyhello.so <br />
# ls <br />
hello.c hello.h main.c <br />
# <br />
在来创建静态库文件libmyhello.a和动态库文件libmyhello.so。 <br />
# gcc -c hello.c <br />
# ar cr libmyhello.a hello.o <br />
# gcc -shared -fPCI -o libmyhello.so hello.o <br />
# ls <br />
hello.c hello.h hello.o libmyhello.a libmyhello.so main.c <br />
# <br />
通过上述最后一条ls命令，可以发现静态库文件libmyhello.a和动态库文件libmyhello.so都已经生成，并都在当前目录中。然后，我们运行gcc命令来使用函数库myhello生成目标文件hello，并运行程序 hello。 <br />
# gcc -o hello main.c -L. -lmyhello <br />
# ./hello <br />
./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory <br />
# <br />
<font color="#ff0000">从程序hello运行的结果中很容易知道，当静态库和动态库同名时， gcc命令将优先使用动态库。 </font><br />
<strong><font color="#0000ff">基本概念</font></strong> <br />
<font color="#ff0000">库有动态与静态两种，动态通常用.so为后缀，静态用.a为后缀。</font> <br />
例如：libhello.so libhello.a 为了在同一系统中使用不同版本的库，可以在库文件名后加上版本号为后缀,例如： libhello.so.1.0,由于程序连接默认以.so为文件后缀名。所以为了使用这些库，通常使用建立符号连接的方式。 <br />
ln -s libhello.so.1.0 libhello.so.1 <br />
ln -s libhello.so.1 libhello.so <strong><br />
1、使用库</strong><font color="#000000"> </font><br />
当要使用静态的程序库时，连接器会找出程序所需的函数，然后将它们拷贝到执行文件，由于这种拷贝是完整的，所以一旦连接成功，静态程序库也就不再需要了。然 而，对动态库而言，就不是这样。动态库会在执行程序内留下一个标记指明当程序执行时，首先必须载入这个库。由于动态库节省空间，linux下进行连接的缺省操作是首先连接动态库，也就是说，如果同时存在静态和动态库，不特别指定的话，将与动态库相连接。 现在假设有一个叫hello的程序开发包，它提供一个静态库libhello.a 一个动态库libhello.so,一个头文件hello.h,头文件中提供sayhello()这个函数 /* hello.h */ void sayhello(); 另外还有一些说明文档。 <br />
这一个典型的程序开发包结构 与动态库连接 linux默认的就是与动态库连接，下面这段程序testlib.c使用hello库中的sayhello()函数 <br />
/*testlib.c*/ <br />
#include&nbsp;&nbsp;<br />
#include&nbsp;&nbsp;<br />
int main() <br />
{ <br />
&nbsp; &nbsp;sayhello(); <br />
&nbsp; &nbsp;return 0; <br />
} <br />
使用如下命令进行编译 $gcc -c testlib.c -o testlib.o <br />
用如下命令连接： $gcc testlib.o -lhello -o testlib <br />
连接时要注意，假设libhello.o 和libhello.a都在缺省的库搜索路径下/usr/lib下，如果在其它位置要加上-L参数 与与静态库连接麻烦一些，主要是参数问题。还是上面的例子： <br />
$gcc testlib.o -o testlib -WI,-Bstatic -lhello <br />
注：<font color="#ff0000">这个特别的"-WI，-Bstatic"参数，实际上是传给了连接器ld。指示它与静态库连接，如果系统中只有静态库当然就不需要这个参数了。</font> 如果要和多个库相连接，而每个库的连接方式不一样，比如上面的程序既要和libhello进行静态连接，又要和libbye进行动态连接，其命令应为： <br />
$gcc testlib.o -o testlib <font color="#0000ff">-WI,-Bstatic</font> -lhello <font color="#0000ff">-WI,-Bdynamic</font> -lbye <br />
&nbsp;&nbsp;<br />
<strong><font color="#0000ff">2、动态库的路径问题 为了让执行程序顺利找到动态库，有三种方法：</font></strong> <br />
(1)把库拷贝到/usr/lib和/lib目录下。 <br />
<font color="#ff0000">(2)</font>在LD_LIBRARY_PATH环境变量中加上库所在路径。 <br />
<font color="#ff0000">例如动态库libhello.so在/home/ting/lib目录下，以bash为例，使用命令： </font><br />
<font color="#ff0000">$export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ting/lib </font><br />
(3) <font color="#ff0000">修改/etc/ld.so.conf文件，把库所在的路径加到文件末尾，并执行ldconfig刷新。这样，加入的目录下的所有库文件都可见。 </font><br />
<strong><font color="#0000ff">3、查看库中的符号</font></strong> <br />
有时候可能需要查看一个库中到底有哪些函数，nm命令可以打印出库中的涉及到的所有符号。库既可以是静态的也可以是动态的。nm列出的符号有很多，常见的有三种： <br />
一种是在库中被调用，但并没有在库中定义(表明需要其他库支持)，用U表示； <br />
一种是库中定义的函数，用T表示，这是最常见的； <br />
另外一种是所谓的&#8220;弱 态&#8221;符号，它们虽然在库中被定义，但是可能被其他库中的同名符号覆盖，用W表示。 <br />
例如，假设开发者希望知道上文提到的hello库中是否定义了 printf(): <br />
$nm libhello.so |grep printf U <br />
其中printf U表示符号printf被引用，但是并没有在函数内定义，由此可以推断，要正常使用hello库，必须有其它库支持，再使用ldd命令查看hello依赖于哪些库： <br />
$ldd hello libc.so.6=&gt;/lib/libc.so.6(0x400la000) /lib/ld-linux.so.2=&gt;/lib/ld-linux.so.2 (0x40000000) <br />
从上面的结果可以继续查看printf最终在哪里被定义，有兴趣可以go on <br />
<strong><font color="#0000ff">4、生成库 </font></strong><br />
<font color="#ff0000">第一步要把源代码编绎成目标代码。</font> <br />
以下面的代码为例，生成上面用到的hello库： <br />
/* hello.c */ <br />
#include&nbsp; &nbsp;<br />
void sayhello() <br />
{ <br />
&nbsp;&nbsp;printf("hello,world "); <br />
} <br />
用gcc编绎该文件，在编绎时可以使用任何全法的编绎参数，例如-g加入调试代码等： gcc -c hello.c -o hello.o <br />
(1)连接成静态库 连接成静态库使用ar命令，其实ar是archive的意思 <br />
$ar cqs libhello.a hello.o <br />
(2)连接成动态库 生成动态库用gcc来完成，由于可能存在多个版本，因此通常指定版本号： <br />
$gcc -shared -Wl,-soname,libhello.so.1 -o libhello.so.1.0 hello.o <br />
另外再建立两个符号连接： <br />
$ln -s libhello.so.1.0 libhello.so.1 <br />
$ln -s libhello.so.1 libhello.so <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这样一个libhello的动态连接库就生成了。<font color="#ff0000">最重要的是传gcc -shared 参数使其生成是动态库而不是普通执行程序。选项</font><font color="#ff0000"> <br />
-Wl 表示后面的参数也就是-soname,libhello.so.1是准备留给连接器ld(link drive)进行处理。</font><font color="#ff0000">实际上，每一个库都有一个soname(如果没有用-Wl,-soname,xxxxx.so.x指定的话,就是生成库的文件名,soname&lt;=libname)，当连接器发现它正在查找的程序库中有这样一个soname(不管是手动还是缺省的)，连接器便会将其提取出来嵌入连结中的二进制文件内，而不是它正在运行的实际文件名，在程序执行期间，程序就会以soname名字去查找库文件，而不是库的文件名，为了保证通过soname能定位到库文件,我们还要建立soname和库文件的链接关系。同样的,为了保证编译的时候可以通过-l选项定位动态库, 那么我们还要创建一个 这样形式的链接 libxxxxx.so 去关联库文件．所以才有上面两个ln命令的使用.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为了保证运行时,不会出现库导入错误,可以运行 ldd 命令 来测试 链接进目标文件的 soname 是否可以导入相关的库文件.如果出现了none的地方,就要看看库所在目录是否添加到可以搜索到的地方了呢.<br />
　　把soname作为库的区分标志, 这样做的目的主要是允许系统中多个版本的库文件共存，也就是说,soname是库的区分标志,习惯上在命名库文件的时候通常与soname相同 libxxxx.so.major.minor 其中，xxxx是库的名字，major是主版本号，minor 是次版本号</font> <br />
<img src ="http://www.blogjava.net/sunzhong/aggbug/289377.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sunzhong/" target="_blank">月光记忆</a> 2009-08-01 12:10 <a href="http://www.blogjava.net/sunzhong/articles/289377.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux下进程的各种状态</title><link>http://www.blogjava.net/sunzhong/articles/288945.html</link><dc:creator>月光记忆</dc:creator><author>月光记忆</author><pubDate>Wed, 29 Jul 2009 11:23:00 GMT</pubDate><guid>http://www.blogjava.net/sunzhong/articles/288945.html</guid><wfw:comment>http://www.blogjava.net/sunzhong/comments/288945.html</wfw:comment><comments>http://www.blogjava.net/sunzhong/articles/288945.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sunzhong/comments/commentRss/288945.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sunzhong/services/trackbacks/288945.html</trackback:ping><description><![CDATA[D Uninterruptible sleep (usually IO)<br />
不可中断的深度睡眠，一般由IO引起，同步IO在做读或写操作时，此进程不能做其它事情，只能等待，这时进程处于这种状态，如果程序采用异步IO，这种状态应该就很少见到了<br />
&nbsp;<br />
R Running or runnable (on run queue)&nbsp; <br />
进程处于运行或就绪状态<br />
&nbsp;<br />
S Interruptible sleep (waiting for an event to complete) <br />
可接收信号的睡眠状态，sleep函数可演示这种状态<br />
&nbsp;<br />
T Stopped, either by a job control signal or because it is being traced.<br />
被ctrl+z中断或被trace<br />
&nbsp;<br />
W paging (not valid since the 2.6.xx kernel) <br />
&nbsp;<br />
X dead (should never be seen) <br />
进程已经完全死掉，不可能看见这种状态的<br />
&nbsp;<br />
Z Defunct ("zombie") process, terminated but not reaped by its parent.<br />
进程已经终止，但是其父进程没有来及处理它，多进程写不好的话，这种状态是常见的<br />
&nbsp;<br />
For BSD formats and when the stat keyword is used, additional characters may <br />
be displayed: <br />
&lt; high-priority (not nice to other users) <br />
&nbsp;<br />
N low-priority (nice to other users) <br />
&nbsp;<br />
L has pages locked into memory (for real-time and custom IO) <br />
&nbsp;<br />
s is a session leader<br />
&nbsp;<br />
l is multi-threaded (using CLONE_THREAD, like NPTL pthreads do) <br />
&nbsp;<br />
+ is in the foreground process group<br />
&nbsp;<br />
关于D和Z一段有趣的解释：<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; 有一类垃圾却并非这么容易打扫，那就是我们常见的状态为 D (Uninterruptible sleep) ，以及状态为 Z (Zombie) 的垃圾进程。这些垃圾进程要么是求而不得，像怨妇一般等待资源(D)，要么是僵而不死，像冤魂一样等待超度(Z)，它们在 CPU run_queue 里滞留不去，把 Load Average 弄的老高老高，没看过我前一篇blog的国际友人还以为这儿民怨沸腾又出了什么大事呢。怎么办？开枪！kill -9！看你们走是不走。但这两种垃圾进程偏偏是刀枪不入的，不管换哪种枪法都杀不掉它们。无奈，只好reboot，像剿灭禽流感那样不分青红皂白地一律扑杀！<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; 怨妇 D，往往是由于 I/O 资源得不到满足，而引发等待，在内核源码 fs/proc/array.c 里，其文字定义为&#8220; "D (disk sleep)", /* 2 */ &#8221;（由此可知 D 原是Disk的打头字母），对应着 include/linux/sched.h 里的&#8220; #define TASK_UNINTERRUPTIBLE 2 &#8221;。举个例子，当 NFS 服务端关闭之时，若未事先 umount 相关目录，在 NFS 客户端执行 df 就会挂住整个登录会话，按 Ctrl+C 、Ctrl+Z 都无济于事。断开连接再登录，执行 ps axf 则看到刚才的 df 进程状态位已变成了 D ，kill -9 无法杀灭。正确的处理方式，是马上恢复 NFS 服务端，再度提供服务，刚才挂起的 df 进程发现了其苦苦等待的资源，便完成任务，自动消亡。若 NFS 服务端无法恢复服务，在 reboot 之前也应将 /etc/mtab 里的相关 NFS mount 项删除，以免 reboot 过程例行调用 netfs stop 时再次发生等待资源，导致系统重启过程挂起。
<p>　　冤魂 Z 之所以杀不死，是因为它已经死了，否则怎么叫 Zombie（僵尸）呢？冤魂不散，自然是生前有结未解之故。在UNIX/Linux中，每个进程都有一个父进程，进程号叫PID（Process ID），相应地，父进程号就叫PPID（Parent PID）。当进程死亡时，它会自动关闭已打开的文件，舍弃已占用的内存、交换空间等等系统资源，然后向其父进程返回一个退出状态值，报告死讯。如果程序有 bug，就会在这最后一步出问题。儿子说我死了，老子却没听见，没有及时收棺入殓，儿子便成了僵尸。在UNIX/Linux中消灭僵尸的手段比较残忍，执行 ps axjf 找出僵尸进程的父进程号（PPID，第一列），先杀其父，然后再由进程天子 init（其PID为1，PPID为0）来一起收拾父子僵尸，超度亡魂，往生极乐。注意，子进程变成僵尸只是碍眼而已，并不碍事，如果僵尸的父进程当前有要务在身，则千万不可贸然杀之。</p>
 <img src ="http://www.blogjava.net/sunzhong/aggbug/288945.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sunzhong/" target="_blank">月光记忆</a> 2009-07-29 19:23 <a href="http://www.blogjava.net/sunzhong/articles/288945.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>什么是僵死进程 </title><link>http://www.blogjava.net/sunzhong/articles/288900.html</link><dc:creator>月光记忆</dc:creator><author>月光记忆</author><pubDate>Wed, 29 Jul 2009 08:19:00 GMT</pubDate><guid>http://www.blogjava.net/sunzhong/articles/288900.html</guid><wfw:comment>http://www.blogjava.net/sunzhong/comments/288900.html</wfw:comment><comments>http://www.blogjava.net/sunzhong/articles/288900.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sunzhong/comments/commentRss/288900.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sunzhong/services/trackbacks/288900.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 进程在它的生命周期有几种状态：睡眠，可运行，停止，正在运行和僵死状态。所谓僵死进程，指的是一个进程已经退出，它的内存和相关的资源已经被内核释放掉，但是在进程表中这个进程项（entry）还保留着，以便它的父进程得到它的退出状态。一个进程退出时，它的父进程会收到一个SIGCHLD信号。一般情况下，这个信号的句柄通常执行wait系统调用，这样处于僵死状态的进程会被删除。如果父进程没有这么做，结果是什么呢？毫无疑问，进程会处于僵死状态。实际上，僵死进程不会对系统有太大的伤害，最多就是它的进程号(PID)和进程表中的进程项系统不能使用。
  <img src ="http://www.blogjava.net/sunzhong/aggbug/288900.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sunzhong/" target="_blank">月光记忆</a> 2009-07-29 16:19 <a href="http://www.blogjava.net/sunzhong/articles/288900.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>vim 对gtk API 函数自动补全</title><link>http://www.blogjava.net/sunzhong/articles/288829.html</link><dc:creator>月光记忆</dc:creator><author>月光记忆</author><pubDate>Tue, 28 Jul 2009 17:48:00 GMT</pubDate><guid>http://www.blogjava.net/sunzhong/articles/288829.html</guid><wfw:comment>http://www.blogjava.net/sunzhong/comments/288829.html</wfw:comment><comments>http://www.blogjava.net/sunzhong/articles/288829.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sunzhong/comments/commentRss/288829.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sunzhong/services/trackbacks/288829.html</trackback:ping><description><![CDATA[Gtk的API有很多，如何在使用时让VIM自动补全呢？<br />
其实VIM已经有此功能了，这就是VIM的自动补全功能。包括ctrl-N,ctrl-P以及Ommi 补全功能。<br />
当然了，还有对包含的头文件的自动搜索。但是编写gtk程序有一点不方便的是，它包的是gtk.h<br />
,而gtk.h中是一大堆的*.h文件，显然是不可能在gtk.h中找到什么有用的东西的。<br />
是不是有别的什么办法呢？<br />
<br />
linux的使用在于小巧组合，vim配合ctags即可完成此任务。步骤如下：<br />
1.首先进入/usr/include/gtk-2.0/gtk目录，下面有很多头文件，我们要在此目录下生成一个tags文件供使用。<br />
2.执行ctags -R<br />
3.将生成的tags文件copy到你的工作目录，即你写程序的地方。<br />
4.再执行ctags -a&nbsp; //将你写的程序的函数appended 到此文件上。<br />
<br />
OK，大功告成。这下你写程序时，即可ctrl-N/P来自动补全gtk的API了。<br />
不过使用时列表中的函数是有点多了，选择起来反而有点麻烦，但总比没有要好。<br />
<img src ="http://www.blogjava.net/sunzhong/aggbug/288829.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sunzhong/" target="_blank">月光记忆</a> 2009-07-29 01:48 <a href="http://www.blogjava.net/sunzhong/articles/288829.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux进程控制－wait()</title><link>http://www.blogjava.net/sunzhong/articles/288659.html</link><dc:creator>月光记忆</dc:creator><author>月光记忆</author><pubDate>Mon, 27 Jul 2009 17:30:00 GMT</pubDate><guid>http://www.blogjava.net/sunzhong/articles/288659.html</guid><wfw:comment>http://www.blogjava.net/sunzhong/comments/288659.html</wfw:comment><comments>http://www.blogjava.net/sunzhong/articles/288659.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sunzhong/comments/commentRss/288659.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sunzhong/services/trackbacks/288659.html</trackback:ping><description><![CDATA[<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode"><font size="3">#include &lt;sys/types.h&gt; /* 提供类型pid_t的定义 */<br />
            #include &lt;sys/wait.h&gt;<br />
            pid_t wait(int *status)<br />
            </font></pre>
            </td>
        </tr>
    </tbody>
</table>
<font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 进程一旦调用了wait，就立即阻塞自己，由wait自动分析是否当前进程的某个子进程已经退出，如果让它找到了这样一个已经变成僵尸的子进程，wait就会收集这个子进程的信息，并把它彻底销毁后返回；如果没有找到这样一个子进程，wait就会一直阻塞在这里，直到有一个出现为止,当然 如果在调用wait（）时子进程已经结束，则wait（）就会立即返回子进程结束状态值。</font><font size="3"><br />
&nbsp;&nbsp;&nbsp; 参数status用来保存被收集进程退出时的一些状态，它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉的毫不在意，只想把这个僵尸进程消灭掉，（事实上绝大多数情况下，我们都会这样想），我们就可以设定这个参数为NULL，就象下面这样：</font>
<table style="width: 585px; height: 16px" cellspacing="0" cellpadding="0" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode"><font size="3">		pid = wait(NULL);<br />
            </font></pre>
            </td>
        </tr>
    </tbody>
</table>
<font size="3">如果成功，wait会返回被收集的子进程的进程ID，如果调用进程没有子进程，调用就会失败，此时wait返回-1，同时errno被置为ECHILD。</font>
<p><font size="3"><a name="N1009C"></a><br />
下面就让我们用一个例子来实战应用一下wait调用：</font></p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode"><font size="3">             /* wait1.c */<br />
            &nbsp;#include &lt;sys/types.h&gt;<br />
            &nbsp;#include &lt;sys/wait.h&gt;<br />
            &nbsp;#include &lt;unistd.h&gt;<br />
            &nbsp;#include &lt;stdlib.h&gt;<br />
            &nbsp; main()<br />
            &nbsp; {<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pid_t pc,pr;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pc=fork();<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(pc&lt;0) &nbsp;&nbsp;/* 如果出错 */<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("error ocurred!\n");<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if(pc==0){&nbsp;&nbsp;/* 如果是子进程 */ <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("This is child process with pid of %d\n",getpid());<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(10);&nbsp;/* 睡眠10秒钟 */<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else{&nbsp;&nbsp;&nbsp;/* 如果是父进程 */<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pr=wait(NULL);&nbsp;/* 在这里等待 */<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("I catched a child process with pid of %d\n"),pr);<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(0);<br />
            &nbsp; }</font></pre>
            </td>
        </tr>
    </tbody>
</table>
<font size="3"><br />
</font>
<p><font size="3">编译并运行:</font></p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode"><font size="3">$ cc wait1.c -o wait1<br />
            $ ./wait1<br />
            This is child process with pid of 1508<br />
            I catched a child process with pid of 1508<br />
            </font></pre>
            </td>
        </tr>
    </tbody>
</table>
<font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可以明显注意到，在第2行结果打印出来前有10 秒钟的等待时间，这就是我们设定的让子进程睡眠的时间，只有子进程从睡眠中苏醒过来，它才能正常退出，这样就能清晰的看到只有父进程捕捉的过程。其实这里我们不管设定子进程睡眠的时间有多长，父进程都会一直等待下去.</font>
<p style="font-weight: bold"><font size="3"><a name="N100B3"><span class="smalltitle">参数status：</span></a></font></p>
<p><font size="3">如果参数status的值不是NULL，wait就会把子进程退出时的状态取出并存入其中，这是一个整数值（int），指出了子进程是正常退出还是被非正常结束的（一个进程也可以被其他进程用信号结束，我们将在以后的文章中介绍），以及正常结束时的返回值，或被哪一个信号结束的等信息。由于这些信息被存放在一个整数的不同二进制位中，所以用常规的方法读取会非常麻烦，人们就设计了一套专门的宏（macro）来完成这项工作，下面我们来学习一下其中最常用的两个：</font></p>
<p><font size="3">1，WIFEXITED(status) 这个宏用来指出子进程是否为正常退出的，如果是，它会返回一个非零值。</font></p>
<p><font size="3">（请注意，虽然名字一样，这里的参数status并不同于wait唯一的参数--指向整数的指针status，而是那个指针所指向的整数，切记不要搞混了。）</font></p>
<p><font size="3">2， WEXITSTATUS(status) 当WIFEXITED返回非零值时，我们可以用这个宏来提取子进程的返回值，如果子进程调用exit(5)退出，WEXITSTATUS(status) 就会返回5；如果子进程调用exit(7)，WEXITSTATUS(status)就会返回7。请注意，如果进程不是正常退出的，也就是说， WIFEXITED返回0，这个值就毫无意义。</font></p>
<p><font size="3">下面通过例子来实战一下我们刚刚学到的内容：</font></p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode"><font size="3">            /* wait2.c */<br />
            #include &lt;sys/types.h&gt;<br />
            #include &lt;sys/wait.h&gt;<br />
            #include &lt;unistd.h&gt;<br />
            main()<br />
            {<br />
            &nbsp;int status;<br />
            &nbsp;pid_t pc,pr;<br />
            &nbsp;pc=fork();<br />
            &nbsp; if(pc&lt;0)&nbsp;/* 如果出错 */<br />
            &nbsp;&nbsp;&nbsp;&nbsp; printf("error ocurred!\n");<br />
            &nbsp;else if(pc==0){&nbsp;/* 子进程 */<br />
            &nbsp;&nbsp;printf("This is child process with pid of %d.\n",getpid());<br />
            &nbsp;&nbsp;exit(3);&nbsp;/* 子进程返回3 */<br />
            &nbsp;}<br />
            &nbsp;else{&nbsp;&nbsp;/* 父进程 */<br />
            &nbsp;&nbsp;pr=wait(&amp;status);<br />
            &nbsp;&nbsp;&nbsp; if(WIFEXITED(status)){&nbsp;/* 如果WIFEXITED返回非零值 */<br />
            &nbsp;&nbsp;printf("the child process %d exit normally.\n",pr);<br />
            &nbsp;&nbsp;printf("the return code is %d.\n",WEXITSTATUS(status));<br />
            &nbsp;&nbsp;}else&nbsp;&nbsp;&nbsp;/* 如果WIFEXITED返回零 */<br />
            &nbsp;&nbsp;printf("the child process %d exit abnormally.\n",pr);<br />
            &nbsp; }<br />
            }
            </font></pre>
            </td>
        </tr>
    </tbody>
</table>
<font size="3">编译并运行:</font>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode"><font size="3">$ cc wait2.c -o wait2<br />
            $ ./wait2<br />
            This is child process with pid of 1538.<br />
            the child process 1538 exit normally.<br />
            the return code is 3.          </font></pre>
            </td>
        </tr>
    </tbody>
</table>
<font size="3">父进程准确捕捉到了子进程的返回值3，并把它打印了出来。</font>
<p><font size="3">当然，处理进程退出状态的宏并不止这两个，但它们当中的绝大部分在平时的编程中很少用到，就也不在这里浪费篇幅介绍了，有兴趣的读者可以自己参阅Linux man pages去了解它们的用法。</font></p>
<p style="font-weight: bold"><font size="3"><a name="N100D9"><span class="smalltitle">进程同步：</span></a></font></p>
<p><font size="3">有时候，父进程要求子进程的运算结果进行下一步的运算，或者子进程的功能是为父进程提供了下一步执行的先决条件（如：子进程建立文件，而父进程写入数据），此时父进程就必须在某一个位置停下来，等待子进程运行结束，而如果父进程不等待而直接执行下去的话，可以想见，会出现极大的混乱。这种情况称为进程之间的同步，更准确地说，这是进程同步的一种特例。进程同步就是要协调好2个以上的进程，使之以安排好地次序依次执行。解决进程同步问题有更通用的方法，我们将在以后介绍，但对于我们假设的这种情况，则完全可以用wait系统调用简单的予以解决。请看下面这段程序：</font></p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode"><font size="3">            #include &lt;sys/types.h&gt;<br />
            #include &lt;sys/wait.h&gt;<br />
            main()<br />
            {<br />
            &nbsp;&nbsp;pid_t pc, pr;<br />
            &nbsp;&nbsp;int status;<br />
            &nbsp;&nbsp;<br />
            &nbsp;&nbsp;pc=fork();<br />
            &nbsp;&nbsp;<br />
            &nbsp;&nbsp;if(pc&lt;0)<br />
            &nbsp;&nbsp;&nbsp;&nbsp; printf("Error occured on forking.\n");<br />
            &nbsp;&nbsp;else if(pc==0){<br />
            &nbsp;&nbsp;&nbsp;&nbsp;/* 子进程的工作 */<br />
            &nbsp;&nbsp;&nbsp;&nbsp;exit(0);<br />
            &nbsp;&nbsp;}else{<br />
            &nbsp;&nbsp;&nbsp;/* 父进程的工作 */<br />
            &nbsp;&nbsp;&nbsp;pr=wait(&amp;status);<br />
            &nbsp;&nbsp;&nbsp;/* 利用子进程的结果 */<br />
            &nbsp;&nbsp;}<br />
            }<br />
            <br />
            </font></pre>
            </td>
        </tr>
    </tbody>
</table>
<font size="3">这段程序只是个例子，不能真正拿来执行，但它却说明了一些问题，首先，当fork调用成功后，父子进程各做各的事情，但当父进程的工作告一段落，需要用到子进程的结果时，它就停下来调用wait，一直等到子进程运行结束，然后利用子进程的结果继续执行，这样就圆满地解决了我们提出的进程同步问题。</font> <font size="3"><br />
</font>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><font size="3"><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></font></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><font size="3"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            </font>
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><font size="3"><br />
                        </font></td>
                        <td valign="top" align="right"><br />
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<font size="3"><br />
</font><font size="3">waitpid系统调用在Linux函数库中的原型是：</font>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode"><font size="3">#include &lt;sys/types.h&gt; /* 提供类型pid_t的定义 */<br />
            #include &lt;sys/wait.h&gt;<br />
            pid_t waitpid(pid_t pid,int *status,int options)<br />
            </font></pre>
            </td>
        </tr>
    </tbody>
</table>
<font size="3">从本质上讲，系统调用waitpid和wait的作用是完全相同的，但waitpid多出了两个可由用户控制的参数pid和options，从而为我们编程提供了另一种更灵活的方式。下面我们就来详细介绍一下这两个参数：</font>
<p><font size="3"><a name="N100FF"><span class="smalltitle">pid</span></a></font></p>
<p><font size="3">从参数的名字pid和类型pid_t中就可以看出，这里需要的是一个进程ID。但当pid取不同的值时，在这里有不同的意义。</font></p>
<ol>
    <li><font size="3">pid&gt;0时，只等待进程ID等于pid的子进程，不管其它已经有多少子进程运行结束退出了，只要指定的子进程还没有结束，waitpid就会一直等下去。</font>
    <li><font size="3">pid=-1时，等待任何一个子进程退出，没有任何限制，此时waitpid和wait的作用一模一样。</font>
    <li><font size="3">pid=0时，等待同一个进程组中的任何子进程，如果子进程已经加入了别的进程组，waitpid不会对它做任何理睬。</font>
    <li><font size="3">pid&lt;-1时，等待一个指定进程组中的任何子进程，这个进程组的ID等于pid的绝对值。</font> </li>
</ol>
<p><font size="3"><a name="N10117"><span class="smalltitle">options</span></a></font></p>
<p><font size="3">options提供了一些额外的选项来控制waitpid，目前在Linux中只支持WNOHANG和WUNTRACED两个选项，这是两个常数，可以用"|"运算符把它们连接起来使用，比如：</font></p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode"><font size="3">ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);<br />
            </font></pre>
            </td>
        </tr>
    </tbody>
</table>
<font size="3">如果我们不想使用它们，也可以把options设为0，如：</font>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode"><font size="3">ret=waitpid(-1,NULL,0);<br />
            </font></pre>
            </td>
        </tr>
    </tbody>
</table>
<font size="3">如果使用了WNOHANG参数调用waitpid，即使没有子进程退出，它也会立即返回，不会像wait那样永远等下去。</font>
<p><font size="3">而WUNTRACED参数，由于涉及到一些跟踪调试方面的知识，加之极少用到，这里就不多费笔墨了，有兴趣的读者可以自行查阅相关材料。</font></p>
<p><font size="3">&nbsp;&nbsp;&nbsp; wait不就是经过包装的waitpid吗？没错，察看&lt;内核源码目录&gt;/include/unistd.h文件349-352行就会发现以下程序段：</font></p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode"><font size="3">static inline pid_t wait(int * wait_stat)<br />
            {<br />
            return waitpid(-1,wait_stat,0);<br />
            }            </font></pre>
            </td>
        </tr>
    </tbody>
</table>
<font size="3"><br />
</font>
<p><font size="3"><a name="N10138"><span class="smalltitle">1.9.2 返回值和错误</span></a></font></p>
<p><font size="3">waitpid的返回值比wait稍微复杂一些，一共有3种情况：</font></p>
<ol>
    <li><font size="3">当正常返回的时候，waitpid返回收集到的子进程的进程ID；</font>
    <li><font size="3">如果设置了选项WNOHANG，而调用中waitpid发现没有已退出的子进程可收集，则返回0；</font>
    <li><font size="3">如果调用中出错，则返回-1，这时errno会被设置成相应的值以指示错误所在；</font> </li>
</ol>
<p><font size="3">当pid所指示的子进程不存在，或此进程存在，但不是调用进程的子进程，waitpid就会出错返回，这时errno被设置为ECHILD；</font></p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode"><font size="3">            #include &lt;sys/types.h&gt;<br />
            #include &lt;sys/wait.h&gt;<br />
            #include &lt;unistd.h&gt;<br />
            main()<br />
            {<br />
            &nbsp;&nbsp;&nbsp; pid_t pc, pr;<br />
            &nbsp;&nbsp;&nbsp; <br />
            &nbsp;&nbsp;&nbsp; pc=fork();<br />
            &nbsp;&nbsp;&nbsp; if(pc&lt;0)&nbsp;&nbsp;/* 如果fork出错 */<br />
            &nbsp;&nbsp;&nbsp; printf("Error occured on forking.\n");<br />
            &nbsp;&nbsp;&nbsp; else if(pc==0)<br />
            &nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;/* 如果是子进程 */<br />
            &nbsp;&nbsp;&nbsp; sleep(4);&nbsp;/* 睡眠4秒 */<br />
            &nbsp;&nbsp;&nbsp; exit(0);<br />
            &nbsp;&nbsp;&nbsp; }<br />
            &nbsp;&nbsp;&nbsp; /* 如果是父进程 */<br />
            &nbsp;&nbsp;&nbsp; do<br />
            &nbsp;&nbsp;&nbsp; {<br />
            &nbsp;&nbsp;&nbsp; pr=waitpid(pc, NULL, WNOHANG);&nbsp;/* 使用了WNOHANG参数，waitpid不会在这里等待 */<br />
            &nbsp;&nbsp;&nbsp;&nbsp; if(pr==0)<br />
            &nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;/* 如果没有收集到子进程 */<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("No child exited\n");<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(1);<br />
            &nbsp;&nbsp;&nbsp;&nbsp; }<br />
            &nbsp;&nbsp;&nbsp; }while(pr==0);&nbsp;&nbsp;&nbsp;&nbsp;/* 没有收集到子进程，就回去继续尝试 */<br />
            &nbsp;&nbsp;&nbsp; if(pr==pc)<br />
            &nbsp;&nbsp;&nbsp;&nbsp; printf("successfully release child %d\n", pr);<br />
            &nbsp;&nbsp;&nbsp; else<br />
            &nbsp;&nbsp;&nbsp;&nbsp; printf("some error occured\n");<br />
            }</font></pre>
            </td>
        </tr>
    </tbody>
</table>
<font size="3"><br />
</font>
<p><font size="3">编译并运行：</font></p>
<table style="float: left" cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode"><font size="3">$ cc waitpid.c -o waitpid<br />
            $ ./waitpid<br />
            No child exited<br />
            No child exited<br />
            No child exited<br />
            No child exited<br />
            successfully&nbsp;release child 1526<br />
            </font></pre>
            </td>
        </tr>
    </tbody>
</table>
<font size="3"><br />
</font>
<p><font size="3">父进程经过4次失败的尝试之后，终于收集到了退出的子进程。</font></p>
<p><font size="3">因为这只是一个例子程序，不便写得太复杂，所以我们就让父进程和子进程分别睡眠了4秒钟和1秒钟，代表它们分别作了4秒钟和1秒钟的工作。父子进程都有工作要做，父进程利用工作的简短间歇察看子进程的是否退出，如退出就收集它.这样的话,既不影响父进程的工作,也可以消除僵尸进程.<br />
<br />
最后 不管是 wait 还是waitpid函数都有个参数来反映子进程的结束状态,底下有几个宏可判别结束情况,参数当然是指针指向的那个：<br />
WIFEXITED（status）如果子进程正常结束则为非0 值。<br />
WEXITSTATUS（status）取得子进程exit（）返回的结束代码，一<br />
般会先用WIFEXITED 来判断是否正常结束才能使用此宏。<br />
WIFSIGNALED（status）如果子进程是因为信号而结束则此宏值为<br />
真<br />
WTERMSIG（status） 取得子进程因信号而中止的信号代码，一般<br />
会先用WIFSIGNALED 来判断后才使用此宏。<br />
WIFSTOPPED（status） 如果子进程处于暂停执行情况则此宏值为<br />
真。一般只有使用WUNTRACED 时才会有此情况。<br />
WSTOPSIG（status） 取得引发子进程暂停的信号代码，一般会先<br />
用WIFSTOPPED 来判断后才使用此宏。<br />
返回值<br />
如果执行成功则返回子进程识别码（PID），如果有错误发生则返回<br />
-1。失败原因存于errno 中。<br />
<br />
&nbsp;</font></p>
<img src ="http://www.blogjava.net/sunzhong/aggbug/288659.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sunzhong/" target="_blank">月光记忆</a> 2009-07-28 01:30 <a href="http://www.blogjava.net/sunzhong/articles/288659.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Const 用法小结(转)</title><link>http://www.blogjava.net/sunzhong/articles/288376.html</link><dc:creator>月光记忆</dc:creator><author>月光记忆</author><pubDate>Sat, 25 Jul 2009 11:50:00 GMT</pubDate><guid>http://www.blogjava.net/sunzhong/articles/288376.html</guid><wfw:comment>http://www.blogjava.net/sunzhong/comments/288376.html</wfw:comment><comments>http://www.blogjava.net/sunzhong/articles/288376.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sunzhong/comments/commentRss/288376.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sunzhong/services/trackbacks/288376.html</trackback:ping><description><![CDATA[<p align="left">1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const常量，如const int max = 100;&nbsp;<br />
优点：const常量有数据类型，而宏常量没有数据类型。编译器可以对前者进行类型安全检查，而对后者只进行字符替换，没有类型安全检查，并且在字符替换时可能会产生意料不到的错误（边际效应）</p>
<p align="left">2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const 修饰类的数据成员。如：<br />
class A</p>
<p align="left">{</p>
<p align="left">&nbsp;&nbsp;&nbsp; const int size;</p>
<p align="left">&nbsp;&nbsp;&nbsp; &#8230; </p>
<p align="left">}</p>
<p align="left">const数据成员只在某个对象生存期内是常量，而对于整个类而言却是可变的。因为类可以创建多个对象，不同的对象其const数据成员的值可以不同。所以不能在类声明中初始化const数据成员，因为类的对象未被创建时，编译器不知道const 数据成员的值是什么。如</p>
<p align="left">class A</p>
<p align="left">{</p>
<p align="left">&nbsp;const int size = 100;&nbsp;&nbsp;&nbsp; //错误</p>
<p align="left">&nbsp;int array[size];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //错误，未知的size</p>
<p align="left">}</p>
<p align="left">const数据成员的初始化只能在类的构造函数的初始化表中进行。要想建立在整个类中都恒定的常量，应该用类中的枚举常量来实现。如</p>
<p align="left">class A</p>
<p align="left">{&#8230;</p>
<p align="left">&nbsp;enum {size1=100, size2 = 200 };</p>
<p align="left">int array1[size1];</p>
<p align="left">int array2[size2]; </p>
<p align="left">}</p>
<p align="left">枚举常量不会占用对象的存储空间，他们在编译时被全部求值。但是枚举常量的隐含数据类型是整数，其最大值有限，且不能表示浮点数。</p>
<p align="left">3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const修饰指针的情况，见下式：</p>
<p align="left">int b = 500; <br />
const int* a = &amp;b&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[1] <br />
int const *a = &amp;b&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[2] <br />
int* const a = &amp;b &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[3] <br />
const int* const a = &amp;b &nbsp;&nbsp;&nbsp;&nbsp;[4]&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp; 我们可以参考《Effective c++》Item21上的做法，如果const位于星号的左侧，则const就是用来修饰指针所指向的变量，即指针指向为常量；如果const位于星号的右侧，const就是修饰指针本身，即指针本身是常量。因此，[1]和[2]的情况相同，都是指针所指向的内容为常量（const放在变量声明符的位置无关），这种情况下不允许对内容进行更改操作，如不能*a = 3 ；[3]为指针本身是常量，而指针所指向的内容不是常量，这种情况下不能对指针本身进行更改操作，如a++是错误的；[4]为指针本身和指向的内容均为常量。</p>
<p align="left">4.&nbsp;&nbsp;&nbsp;&nbsp; const的初始化<br />
<br />
先看一下const变量初始化的情况<br />
1) 非指针const常量初始化的情况：A b; <br />
const A a = b; <br />
<br />
2) 指针const常量初始化的情况：</p>
<p align="left">A* d = new A(); <br />
const A* c = d; <br />
或者：const A* c = new A(); <br />
3）引用const常量初始化的情况：<br />
A f; <br />
const A&amp; e = f; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 这样作e只能访问声明为const的函数，而不能访问一&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p align="left">般的成员函数；<br />
<br />
&nbsp;&nbsp;&nbsp; [思考1]：以下的这种赋值方法正确吗？<br />
&nbsp;&nbsp;&nbsp; const A* c=new A(); <br />
&nbsp;&nbsp;&nbsp; A* e = c; <br />
&nbsp;&nbsp;&nbsp; [思考2]：以下的这种赋值方法正确吗？<br />
&nbsp;&nbsp;&nbsp; A* const c = new A(); <br />
&nbsp;&nbsp;&nbsp; A* b = c;</p>
<p align="left">5.&nbsp;&nbsp;&nbsp;&nbsp; 另外const 的一些强大的功能在于它在函数声明中的应用。在一个函数声明中，const 可以修饰函数的返回值，或某个参数；对于成员函数，还可以修饰是整个函数。有如下几种情况，以下会逐渐的说明用法：<br />
A&amp; operator=(const A&amp; a); <br />
void fun0(const A* a ); <br />
void fun1( ) const; // fun1( ) 为类成员函数<br />
const A fun2( );<br />
1）修饰参数的const，如 void fun0(const A* a ); void fun1(const A&amp; a); <br />
调用函数的时候，用相应的变量初始化const常量，则在函数体中，按照const所修饰的部分进行常量化，如形参为const A* a，则不能对传递进来的指针的内容进行改变，保护了原指针所指向的内容；如形参为const A&amp; a，则不能对传递进来的引用对象进行改变，保护了原对象的属性。<br />
[注意]：参数const通常用于参数为指针或引用的情况，且只能修饰输入参数;若输入参数采用&#8220;值传递&#8221;方式，由于函数将自动产生临时变量用于复制该参数，该参数本就不需要保护，所以没有必要用const修饰。<br />
[总结]对于非内部数据类型的输入参数，因该将&#8220;值传递&#8221;的方式改为&#8220;const引用传递&#8221;，目的是为了提高效率。例如，将void Func(A a)改为void Func(const A &amp;a)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对于内部数据类型的输入参数，不要将&#8220;值传递&#8221;的方式改为&#8220;const引用传递&#8221;。否则既达不到提高效率的目的，又降低了函数的可理解性。例如void Func(int x)不应该改为void Func(const int &amp;x)<br />
2）&nbsp;修饰返回值的const，如const A fun2( ); const A* fun3( ); <br />
这样声明了返回值后，const按照"修饰原则"进行修饰，起到相应的保护作用。<br />
const Rational operator*(const Rational&amp; lhs, const Rational&amp; rhs) <br />
{ <br />
return Rational(lhs.numerator() * rhs.numerator(), <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lhs.denominator() * rhs.denominator()); <br />
} <br />
<br />
返回值用const修饰可以防止允许这样的操作发生:Rational a,b; <br />
Radional c; <br />
(a*b) = c; <br />
<br />
一般用const修饰返回值为对象本身（非引用和指针）的情况多用于二目操作符重载函数并产生新对象的时候。<br />
[总结]<br />
1.&nbsp;&nbsp;&nbsp;&nbsp; 一般情况下，函数的返回值为某个对象时，如果将其声明为const时，多用于操作符的重载。通常，不建议用const修饰函数的返回值类型为某个对象或对某个对象引用的情况。原因如下：如果返回值为某个对象为const（const A test = A 实例）或某个对象的引用为const（const A&amp; test = A实例），则返回值具有const属性，则返回实例只能被访问到类A中的公有（保护）数据成员和const成员函数，并且不允许对其进行赋值操作，这在一般情况下很少用到。<br />
2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果给采用&#8220;指针传递&#8221;方式的函数返回值加const修饰，那么函数返回值（即指针）的内容不能被修改，该返回值只能被赋给加const 修饰的同类型指针。如：</p>
<p align="left">const char * GetString(void);</p>
<p align="left">如下语句将出现编译错误：</p>
<p align="left">char *str=GetString();</p>
<p align="left">正确的用法是：<br />
const char *str=GetString();</p>
<p align="left">6.&nbsp;&nbsp;&nbsp;&nbsp; 类成员函数中const的使用<br />
一般放在函数体后，形如：void fun() const; <br />
任何不会修改数据成员的函数都因该声明为const类型。如果在编写const成员函数时，不慎修改了数据成员，或者调用了其他非const成员函数，编译器将报错，这大大提高了程序的健壮性。如：</p>
<p align="left">class Stack</p>
<p align="left">{</p>
<p align="left">&nbsp;public:</p>
<p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;void Push(int elem);</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int Pop(void);</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int GetCount(void) const;&nbsp;&nbsp; //const 成员函数</p>
<p align="left">&nbsp;private: </p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int m_num;</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int m_data[100];</p>
<p align="left">};</p>
<p align="left">int Stack::GetCount(void) const</p>
<p align="left">{</p>
<p align="left">&nbsp;++m_num;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //编译错误，企图修改数据成员m_num</p>
<p align="left">&nbsp;Pop();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //编译错误，企图调用非const函数</p>
<p align="left">&nbsp;Return m_num;</p>
<p align="left">}</p>
<p align="left">7.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 使用const的一些建议<br />
<br />
1 要大胆的使用const，这将给你带来无尽的益处，但前提是你必须搞清楚原委；<br />
2 要避免最一般的赋值操作错误，如将const变量赋值，具体可见思考题；<br />
3 在参数中使用const应该使用引用或指针，而不是一般的对象实例，原因同上；<br />
4 const在成员函数中的三种用法（参数、返回值、函数）要很好的使用；<br />
5 不要轻易的将函数的返回值类型定为const; <br />
6除了重载操作符外一般不要将返回值类型定为对某个对象的const引用; <br />
<br />
[思考题答案] <br />
1 这种方法不正确，因为声明指针的目的是为了对其指向的内容进行改变，而声明的指针e指向的是一个常量，所以不正确；<br />
2 这种方法正确，因为声明指针所指向的内容可变；<br />
</p>
<div id="div_digg" align="left"></div>
 <img src ="http://www.blogjava.net/sunzhong/aggbug/288376.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sunzhong/" target="_blank">月光记忆</a> 2009-07-25 19:50 <a href="http://www.blogjava.net/sunzhong/articles/288376.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>static C语言</title><link>http://www.blogjava.net/sunzhong/articles/288364.html</link><dc:creator>月光记忆</dc:creator><author>月光记忆</author><pubDate>Sat, 25 Jul 2009 10:37:00 GMT</pubDate><guid>http://www.blogjava.net/sunzhong/articles/288364.html</guid><wfw:comment>http://www.blogjava.net/sunzhong/comments/288364.html</wfw:comment><comments>http://www.blogjava.net/sunzhong/articles/288364.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sunzhong/comments/commentRss/288364.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sunzhong/services/trackbacks/288364.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C语言程序可以看成由一系列外部对象构成，这些外部对象可能是变量或函数。而内部变量是指定义在函数内部的函数参数及变量。外部变量定义在函数之外，因此可以在许多函数中使用。由于C语言不允许在一个函数中定义其它函数，因此函数本身只能是&#8220;外部的&#8221;。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;由于C语言代码是以文件为单位来组织的，在一个源程序所有源文件中，一个外部变量或函数只能在某个文件中定义一次，而其它文件可以通过extern声明来访问它（定义外部变量或函数的源文件中也可以包含对该外部变量的extern声明）。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;而static则可以限定变量或函数为静态存储。如果用static限定外部变量与函数，则可以将该对象的作用域限定为被编译源文件的剩余部分。通过static限定外部对象，可以达到隐藏外部对象的目的。因而，static限定的变量或函数不会和同一程序中其它文件中同名的相冲突。如果用static限定内部变量，则该变量从程序一开始就拥有内存，不会随其所在函数的调用和退出而分配和消失。<br />
&nbsp;&nbsp;&nbsp;<strong>C语言中使用静态函数的好处</strong>：
<ol>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;静态函数会被自动分配在一个一直使用的存储区，直到退出应用程序实例，避免了调用函数时压栈出栈，速度快很多。
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;关键字&#8220;static&#8221;，译成中文就是&#8220;静态的&#8221;，所以内部函数又称静态函数。但此处&#8220;static&#8221;的含义不是指存储方式，而是指对函数的作用域仅局限于本文件。 使用内部函数的好处是：不同的人编写不同的函数时，不用担心自己定义的函数，是否会与其它文件中的函数同名，因为同名也没有关系。 </li>
</ol>
<blockquote dir="ltr" style="margin-right: 0px">
<p><strong>c语言中static的语义<br />
</strong>1.static变量:<br />
&nbsp; 1).局部<br />
&nbsp;&nbsp;&nbsp;&nbsp; a.静态局部变量在函数内定义,生存期为整个源程序，但作用域与自动变量相同，只能在定义该变量的函数内使用。退出该函数后， 尽管该变量还继续存在，但不能使用它。<br />
&nbsp;&nbsp;&nbsp;&nbsp; b.对基本类型的静态局部变量若在说明时未赋以初值，则系统自动赋予0值。而对自动变量不赋初值，则其值是不定的。<br />
&nbsp; 2).全局<br />
&nbsp;&nbsp;&nbsp; 全局变量本身就是静态存储方式， 静态全局变量当然也是静态存储方式。但是他们的作用域，非静态全局 变量的作用域是整个源程序（多个源文件可以共同使用）； 而静态全局变量则限制了其作用域， 即只在定义该变量的源文件内有效， 在同一源程序的其它源文件中不能使用它。<br />
2.static函数（也叫内部函数）<br />
&nbsp;&nbsp; 只能被本文件中的函数调用，而不能被同一程序其它文件中的函数调用。区别于一般的非静态函数（外部函数） <br />
&nbsp;&nbsp;&nbsp;&nbsp;static在c里面可以用来修饰变量，也可以用来修饰函数。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 先看用来修饰变量的时候。变量在c里面可分为存在全局数据区、栈和堆里。其实我们平时所说的堆栈是栈而不包含对，不要弄混。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int a ;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; main()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int b ; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int c* = (int *)malloc(sizeof(int));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a是全局变量，b是栈变量，c是堆变量。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static对全局变量的修饰，可以认为是限制了只能是本文件引用此变量。有的程序是由好多.c文件构成。彼此可以互相引用变量，但加入static修饰之后，只能被本文件中函数引用此变量。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static对栈变量的修饰，可以认为栈变量的生命周期延长到程序执行结束时。一般来说，栈变量的生命周期由OS管理，在退栈的过程中，栈变量的生命也就结束了。但加入static修饰之后，变量已经不在存储在栈中，而是和全局变量一起存储。同时，离开定义它的函数后不能使用，但如再次调用定义它的函数时，它又可继续使用， 而且保存了前次被调用后留下的值。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static对函数的修饰与对全局变量的修饰相似，只能被本文件中的函数调用，而不能被同一程序其它文件中的函数调用。&nbsp;<br />
&nbsp;&nbsp; &nbsp;&nbsp; static 声明的变量在C语言中有两方面的特征：<br />
　　1)、变量会被放在程序的全局存储区中，这样可以在下一次调用的时候还可以保持原来的赋值。这一点是它与堆栈变量和堆变量的区别。&nbsp;<br />
　　2)、变量用static告知编译器，自己仅仅在变量的作用范围内可见。这一点是它与全局变量的区别。</p>
</blockquote>
<p>　　问题：Static的理解</p>
<p>　　关于static变量，请选择下面所有说法正确的内容：</p>
<p>　　A、若全局变量仅在单个C文件中访问，则可以将这个变量修改为静态全局变量，以降低模块间的耦合度；</p>
<p>　　B、若全局变量仅由单个函数访问，则可以将这个变量改为该函数的静态局部变量，以降低模块间的耦合度；</p>
<p>　　C、设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时，需要考虑重入问题；</p>
<p>　　D、静态全局变量过大，可那会导致堆栈溢出。 </p>
<p>　　答案与分析：</p>
<p>　　对于A，B：根据本篇概述部分的说明b)，我们知道，A,B都是正确的。</p>
<p>　　对于C：根据本篇概述部分的说明a)，我们知道，C是正确的（所谓的函数重入问题，下面会详细阐述）。</p>
<p>　　对于D：静态变量放在程序的全局数据区，而不是在堆栈中分配，所以不可能导致堆栈溢出，D是错误的。</p>
<p>　　因此，答案是A、B、C。</p>
<p>　　前面有个问题是何为不可重入函数,下面有个这样的问题</p>
<p>　　曾经设计过如下一个函数，在代码检视的时候被提醒有bug，因为这个函数是不可重入的，为什么？</p>
<blockquote dir="ltr" style="margin-right: 0px">
<p>unsigned int sum_int( unsigned int base )<br />
{<br />
　unsigned int index;<br />
　static unsigned int sum = 0; // 注意，是static类型的。 <br />
　for (index = 1; index &lt;= base; index++)<br />
　{<br />
　　sum += index;<br />
　}<br />
　return sum;<br />
} </p>
</blockquote>
<p>　　答案与分析：</p>
<p>　　所谓的函数是可重入的（也可以说是可预测的），即：只要输入数据相同就应产生相同的输出。<br />
　　这个函数之所以是不可预测的，就是因为函数中使用了static变量，因为static变量的特征，这样的函数被称为：带&#8220;内部存储器&#8221;功能的的函数。因此如果我们需要一个可重入的函数，那么，我们一定要避免函数中使用static变量，这种函数中的static变量，使用原则是，能不用尽量不用。<br />
　　将上面的函数修改为可重入的函数很简单，只要将声明sum变量中的static关键字去掉，变量sum即变为一个auto 类型的变量，函数即变为一个可重入的函数。<br />
　　当然，有些时候，在函数中是必须要使用static变量的，比如当某函数的返回值为指针类型时，则必须是static的局部变量的地址作为返回值，若为auto类型，创建的就会保存在栈里面,函数调用一结束,就马上回收,则返回指针就是野指针了。</p>
<img src ="http://www.blogjava.net/sunzhong/aggbug/288364.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sunzhong/" target="_blank">月光记忆</a> 2009-07-25 18:37 <a href="http://www.blogjava.net/sunzhong/articles/288364.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>