﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-网络服务器软件开发/中间件开发，关注ACE/ICE/boost-随笔分类-linux</title><link>http://www.cppblog.com/true/category/3902.html</link><description /><language>zh-cn</language><lastBuildDate>Mon, 09 Jul 2012 22:26:49 GMT</lastBuildDate><pubDate>Mon, 09 Jul 2012 22:26:49 GMT</pubDate><ttl>60</ttl><item><title>linux常用命令搜集</title><link>http://www.cppblog.com/true/archive/2012/07/09/182263.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Sun, 08 Jul 2012 18:07:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2012/07/09/182263.html</guid><wfw:comment>http://www.cppblog.com/true/comments/182263.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2012/07/09/182263.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/182263.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/182263.html</trackback:ping><description><![CDATA[用于备忘<br />1.awk使用<br /><div>http://sunting.blog.51cto.com/1244382/281472<br /><div>http://brantc.blog.51cto.com/410705/199901<br /><div>http://www.cnblogs.com/zhanglanyun/archive/2011/10/02/2198015.html<br /><br />2.sort<br /><div>http://www.cnblogs.com/kumuspring/archive/2011/07/17/2149237.html</div></div></div></div><img src ="http://www.cppblog.com/true/aggbug/182263.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2012-07-09 02:07 <a href="http://www.cppblog.com/true/archive/2012/07/09/182263.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>给ZThread提供一个patch</title><link>http://www.cppblog.com/true/archive/2011/05/29/147632.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Sun, 29 May 2011 09:44:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2011/05/29/147632.html</guid><wfw:comment>http://www.cppblog.com/true/comments/147632.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2011/05/29/147632.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/147632.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/147632.html</trackback:ping><description><![CDATA[&nbsp;<a href="http://zthread.sourceforge.net/">http://zthread.sourceforge.net/</a>&nbsp;&nbsp;<br />&nbsp;这个库设计的很小巧，优雅，符合我个人的审美观，linux下通过编译时会出现错误，主要是由于gcc的老语法所致，由于库的作者2005年停止了更新，所以这一块一直没改，这里提供一个patch，其实很简单，就是替换了下几个语句，内容为：<span class="Apple-style-span" style="font-size: 13px; ">&nbsp; &nbsp; &nbsp;</span><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><span style="color: #000000; ">#(</span><span style="color: #000000; ">0</span><span style="color: #000000; ">)&nbsp;put&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">&nbsp;file&nbsp;under&nbsp;top&nbsp;ZThread&nbsp;directory,such&nbsp;</span><span style="color: #0000FF; ">as</span><span style="color: #000000; ">&nbsp;ZThread</span><span style="color: #000000; ">-</span><span style="color: #000000; ">2.3</span><span style="color: #000000; ">.</span><span style="color: #000000; ">2</span><span style="color: #000000; ">/</span><span style="color: #000000; ">,and<br />#(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">)&nbsp;cd&nbsp;ZThread</span><span style="color: #000000; ">-</span><span style="color: #000000; ">2.3</span><span style="color: #000000; ">.</span><span style="color: #000000; ">2</span><span style="color: #000000; "><br />#(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">)&nbsp;.</span><span style="color: #000000; ">/</span><span style="color: #000000; ">zthread</span><span style="color: #000000; ">-</span><span style="color: #000000; ">patch.sh&nbsp;&nbsp;(chmod&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">x&nbsp;zthread</span><span style="color: #000000; ">-</span><span style="color: #000000; ">patch.sh&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;necessary)<br />#(</span><span style="color: #000000; ">3</span><span style="color: #000000; ">)&nbsp;.</span><span style="color: #000000; ">/</span><span style="color: #000000; ">configure<br />#(</span><span style="color: #000000; ">4</span><span style="color: #000000; ">)&nbsp;make<br /><br />sed&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">i&nbsp;</span><span style="color: #000000; ">'</span><span style="color: #000000; ">/if(!isDisabled())/s/^.*/&nbsp;&nbsp;&nbsp;&nbsp;if(!this-&gt;isDisabled())/</span><span style="color: #000000; ">'</span><span style="color: #000000; ">&nbsp;.</span><span style="color: #000000; ">/</span><span style="color: #000000; ">include</span><span style="color: #000000; ">/</span><span style="color: #000000; ">zthread</span><span style="color: #000000; ">/</span><span style="color: #000000; ">Guard.h<br />sed&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">i&nbsp;</span><span style="color: #000000; ">'</span><span style="color: #000000; ">/waiterArrived(self)/s/^.*/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this-&gt;waiterArrived(self);/</span><span style="color: #000000; ">'</span><span style="color: #000000; ">&nbsp;.</span><span style="color: #000000; ">/</span><span style="color: #000000; ">src</span><span style="color: #000000; ">/</span><span style="color: #000000; ">MutexImpl.h<br />sed&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">i&nbsp;</span><span style="color: #000000; ">'</span><span style="color: #000000; ">/waiterDeparted(self)/s/^.*/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this-&gt;waiterDeparted(self);/</span><span style="color: #000000; ">'</span><span style="color: #000000; ">&nbsp;.</span><span style="color: #000000; ">/</span><span style="color: #000000; ">src</span><span style="color: #000000; ">/</span><span style="color: #000000; ">MutexImpl.h<br />sed&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">i&nbsp;</span><span style="color: #000000; ">'</span><span style="color: #000000; ">/ownerAcquired(self)/s/^.*/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this-&gt;ownerAcquired(self);/</span><span style="color: #000000; ">'</span><span style="color: #000000; ">&nbsp;.</span><span style="color: #000000; ">/</span><span style="color: #000000; ">src</span><span style="color: #000000; ">/</span><span style="color: #000000; ">MutexImpl.h<br />sed&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">i&nbsp;</span><span style="color: #000000; ">'</span><span style="color: #000000; ">/ownerReleased(impl)/s/^.*/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this-&gt;ownerReleased(impl);/</span><span style="color: #000000; ">'</span><span style="color: #000000; ">&nbsp;.</span><span style="color: #000000; ">/</span><span style="color: #000000; ">src</span><span style="color: #000000; ">/</span><span style="color: #000000; ">MutexImpl.h</span></div><div></div><img src ="http://www.cppblog.com/true/aggbug/147632.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2011-05-29 17:44 <a href="http://www.cppblog.com/true/archive/2011/05/29/147632.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>tcpdump的常用方式</title><link>http://www.cppblog.com/true/archive/2011/05/25/147104.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Wed, 25 May 2011 10:53:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2011/05/25/147104.html</guid><wfw:comment>http://www.cppblog.com/true/comments/147104.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2011/05/25/147104.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/147104.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/147104.html</trackback:ping><description><![CDATA[<p>1.sudo /usr/sbin/tcpdump -i eth1 -n port 7999</p>
<p>2.sudo /usr/sbin/tcpdump -i any -X -n -s0 port 7999</p>
<p>3.sudo /usr/sbin/tcpdump -i any -X -n -s0 dst port&nbsp; 7999 and ip[39]==0 and ip[40]==2<br />最后的&nbsp;ip[39]==0 and ip[40]==0是过滤条件，含义为，从ip头开始算起的第39个字节为十进制0，第40个字节为十进制2</p><img src ="http://www.cppblog.com/true/aggbug/147104.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2011-05-25 18:53 <a href="http://www.cppblog.com/true/archive/2011/05/25/147104.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++实现的MD5算法</title><link>http://www.cppblog.com/true/archive/2010/09/04/125888.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Sat, 04 Sep 2010 12:03:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2010/09/04/125888.html</guid><wfw:comment>http://www.cppblog.com/true/comments/125888.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2010/09/04/125888.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/125888.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/125888.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;首先说明这份代码的原创作者不是我，我只是做了一点简单的包装，在这里下载：<a href="http://www.cppblog.com/Files/true/md5lib.rar">http://www.cppblog.com/Files/true/md5lib.rar</a><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;使用说明：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;（1）源代码主要提供了MD5类，在类之外提供了两个函数：<br>&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;std::string getMD5(std::ifstream&amp; stream);//这个是文件接口，计算文件内容的MD5<br>&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;std::string getMD5(const char* str,unsigned int str_len);//这个就不用说了<br>想研究代码的话，参考MD5类的实现，如果只是用的话，只需会用上面的接口即可<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;（2）std::string getMD5(const char* str,unsigned int str_len)的使用：<br>&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;string sName = "MD5";<br>&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;string sMD5&nbsp; = getMD5(sName.c_str(),sName.length());<br>此时sMD5的值为：7f138a09169b250e9dcb378140907378<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;（3）std::string getMD5(std::ifstream&amp; stream)的使用：创建md5.txt，内容为：MD5<br>&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;ifstream file("md5.txt");<br>&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;string sMD5File = getMD5(file);<br>sMD5File 的也是：7f138a09169b250e9dcb378140907378<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;（4）linux下提供md5sum,将上面的md5.txt传到linux下，然后：md5sum md5.txt，结果和上面一样。注意，一定要确认md5.txt内容为3个字节，如果直接登录上linux，用vi创建md5.txt，然后输入&#8220;MD5&#8221;，最后保存，这种方式linux下会追加一个空行，导致MD5值不一致。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;（5）安全意识要树立，这个网站<a href="http://www.cmd5.com/">http://www.cmd5.com/</a>以网页形式提供md5解密，将上面的7f138a09169b250e9dcb378140907378的输入，几秒时间，得到MD5
<img src ="http://www.cppblog.com/true/aggbug/125888.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2010-09-04 20:03 <a href="http://www.cppblog.com/true/archive/2010/09/04/125888.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OCCI在linux上的编译警告</title><link>http://www.cppblog.com/true/archive/2009/03/19/77181.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Thu, 19 Mar 2009 06:06:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2009/03/19/77181.html</guid><wfw:comment>http://www.cppblog.com/true/comments/77181.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2009/03/19/77181.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/77181.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/77181.html</trackback:ping><description><![CDATA[<br>&nbsp;&nbsp;&nbsp;makefile的部分输出：<br>gcc -o ./testocci&nbsp;test.o -lpthread -lstdc++ -ldl -L/export/home/oracle/instantclient_10_2 -locci -lclntsh<br><span style="FONT-FAMILY: Arial">/usr/bin/ld: warning: libstdc++.so.5, needed by /export/home/oracle/instantclient_10_2/libocci.so, may conflict with libstdc++.so.6<br><br>&nbsp; 将makefile改为：<br>gcc -o ./testocci&nbsp;test.o -lpthread -lstdc++ -ldl -L/export/home/oracle/instantclient_10_2 -locci -lclntsh&nbsp; /usr/lib/libstdc++.so.5<br>警告消失<br><br></span>
<img src ="http://www.cppblog.com/true/aggbug/77181.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2009-03-19 14:06 <a href="http://www.cppblog.com/true/archive/2009/03/19/77181.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>修改内核的部分参数</title><link>http://www.cppblog.com/true/archive/2009/02/04/72951.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Wed, 04 Feb 2009 01:30:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2009/02/04/72951.html</guid><wfw:comment>http://www.cppblog.com/true/comments/72951.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2009/02/04/72951.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/72951.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/72951.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在/proc/sys/kernel目录下，可以修改消息队列的参数msgmax,msgmnb,msgmni,还有如终端显示的hostname等,在os重启动时，所有的参数就会恢复到默认值，如果想在系统重启后，修改依然有效，则可以修改/etc/sysctl.conf文件，以修改hostname为例：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo "kernel.hostname=VMware"&nbsp; &gt;&gt;/etc/sysctl.conf<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/sbin/sysctl -p&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<img src ="http://www.cppblog.com/true/aggbug/72951.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2009-02-04 09:30 <a href="http://www.cppblog.com/true/archive/2009/02/04/72951.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基础库的错误信息应该如何处理</title><link>http://www.cppblog.com/true/archive/2009/02/02/72811.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Mon, 02 Feb 2009 08:42:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2009/02/02/72811.html</guid><wfw:comment>http://www.cppblog.com/true/comments/72811.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2009/02/02/72811.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/72811.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/72811.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 用"线程专有存储"实现基础库的错误信息处理&nbsp;&nbsp;<a href='http://www.cppblog.com/true/archive/2009/02/02/72811.html'>阅读全文</a><img src ="http://www.cppblog.com/true/aggbug/72811.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2009-02-02 16:42 <a href="http://www.cppblog.com/true/archive/2009/02/02/72811.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>对基本类型的再包装，方便了移植</title><link>http://www.cppblog.com/true/archive/2009/02/02/72782.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Mon, 02 Feb 2009 02:51:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2009/02/02/72782.html</guid><wfw:comment>http://www.cppblog.com/true/comments/72782.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2009/02/02/72782.html#Feedback</comments><slash:comments>12</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/72782.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/72782.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 对基本类型的再包装&nbsp;&nbsp;<a href='http://www.cppblog.com/true/archive/2009/02/02/72782.html'>阅读全文</a><img src ="http://www.cppblog.com/true/aggbug/72782.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2009-02-02 10:51 <a href="http://www.cppblog.com/true/archive/2009/02/02/72782.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>自己造的一个线程类</title><link>http://www.cppblog.com/true/archive/2009/01/19/72306.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Mon, 19 Jan 2009 04:54:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2009/01/19/72306.html</guid><wfw:comment>http://www.cppblog.com/true/comments/72306.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2009/01/19/72306.html#Feedback</comments><slash:comments>12</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/72306.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/72306.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 自己用的一个线程类&nbsp;&nbsp;<a href='http://www.cppblog.com/true/archive/2009/01/19/72306.html'>阅读全文</a><img src ="http://www.cppblog.com/true/aggbug/72306.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2009-01-19 12:54 <a href="http://www.cppblog.com/true/archive/2009/01/19/72306.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux下信号处理函数实现的一点理解</title><link>http://www.cppblog.com/true/archive/2009/01/15/72072.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Thu, 15 Jan 2009 03:39:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2009/01/15/72072.html</guid><wfw:comment>http://www.cppblog.com/true/comments/72072.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2009/01/15/72072.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/72072.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/72072.html</trackback:ping><description><![CDATA[<p>代码操作步骤如下：<br><br>[tqg@localhost test]$ vi test.cpp </p>
<p><br>#include &lt;pthread.h&gt;<br>#include &lt;signal.h&gt;<br>#include &lt;stdio.h&gt;<br>#include &lt;unistd.h&gt;</p>
<p>void signal_handler(int sig)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("catch signal: %d,thread id = %u\n",sig,pthread_self());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pthread_exit(0);<br>}</p>
<p>void* thread_handler(void*arg)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; signal(SIGQUIT,signal_handler);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("thread arg = %s\n",(char*)arg);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(10);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("in thread\n");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (void*)0;<br>} </p>
<p>int main()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char* pArg = "hello";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pthread_t tid;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pthread_create(&amp;tid,NULL,thread_handler,pArg);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("main thread id = %u\n",pthread_self());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(2);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("killing now\n");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pthread_kill(tid,SIGQUIT);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(20);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("exit main now\n");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br>}<br>~<br>~<br>~<br>~<br>"test.cpp" 42L, 648C written<br>[tqg@localhost test]$ g++ -o test test.cpp -lpthread<br>[tqg@localhost test]$ ./test <br>main thread id = 3086875296<br>thread arg = hello<br>killing now<br>catch signal: 3,thread id = 3086871472<br>exit main now<br>[tqg@localhost test]$ <br><br>可以看出，信号处理函数的执行是在要捕获信号的子线程thread_handler的上下文中执行的。</p>
<img src ="http://www.cppblog.com/true/aggbug/72072.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2009-01-15 11:39 <a href="http://www.cppblog.com/true/archive/2009/01/15/72072.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用GDB调试程序---转贴，非常全面的调试技巧</title><link>http://www.cppblog.com/true/archive/2009/01/11/71749.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Sun, 11 Jan 2009 12:21:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2009/01/11/71749.html</guid><wfw:comment>http://www.cppblog.com/true/comments/71749.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2009/01/11/71749.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/71749.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/71749.html</trackback:ping><description><![CDATA[用GDB调试程序 <br>GDB是一个强大的命令行调试工具。大家知道命令行的强大就是在于，其可以形成执行序<br>列，形成脚本。UNIX下的软件全是命令行的，这给程序开发提代供了极大的便利，命令行<br>软件的优势在于，它们可以非常容易的集成在一起，使用几个简单的已有工具的命令，就可<br>以做出一个非常强大的功能。 <br>于是UNIX下的软件比Windows下的软件更能有机地结合，各自发挥各自的长处，组合成<br>更为强劲的功能。而Windows下的图形软件基本上是各自为营，互相不能调用，很不利于<br>各种软件的相互集成。在这里并不是要和Windows做个什么比较，所谓&#8220;寸有所长，尺有<br>所短&#8221;，图形化工具还是有不如命令行的地方。 <br>用GDB调试程序 <br>GDB概述 <br>———— <br>GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具。或许，各位比较喜欢那<br>种图形界面方式的，像VC、BCB等IDE的调试，但如果你是在UNIX平台下做软件，你<br>会发现GDB这个调试工具有比VC、BCB的图形化调试器更强大的功能。所谓&#8220;寸有所长，<br>尺有所短&#8221;就是这个道理。 <br>一般来说，GDB主要帮忙你完成下面四个方面的功能： <br>1、启动你的程序，可以按照你的自定义的要求随心所欲的运行程序。 <br>2、可让被调试的程序在你所指定的调置的断点处停住。（断点可以是条件表达式） <br>3、当程序被停住时，可以检查此时你的程序中所发生的事。 <br>4、动态的改变你程序的执行环境。 <br>从上面看来，GDB和一般的调试工具没有什么两样，基本上也是完成这些功能，不过在细<br>节上，你会发现GDB这个调试工具的强大，大家可能比较习惯了图形化的调试工具，但有<br>时候，命令行的调试工具却有着图形化工具所不能完成的功能。让我们一一看来。 <br>一个调试示例 <br>—————— <br>源程序：tst.c <br>1 #include <br>2 <br>3 int func(int n) <br>4 { <br>5 int sum=0,i; <br>6 for(i=0; i&lt;7; i++) {
<p>8 sum+=i; <br>9 } <br>10 return sum; <br>11 } <br>12 <br>13 <br>14 main() <br>15 { <br>16 int i; <br>17 long result = 0; <br>18 for(i=1; i&lt;=100; i++) <br>19 { <br>20 result += i; <br>21 } <br>22 <br>23 printf("result[1-100] = %d \n", result ); <br>24 printf("result[1-250] = %d \n", func(250) ); <br>25 } <br>编译生成执行文件：（Linux下） <br>hchen/test&gt; cc -g tst.c -o tst <br>使用GDB调试： <br>hchen/test&gt; gdb tst &lt;---------- 启动GDB <br>GNU gdb 5.1.1 <br>Copyright 2002 Free Software Foundation, Inc. <br>GDB is free software, covered by the GNU General Public License, and you are <br>welcome to change it and/or distribute copies of it under certain conditions. <br>Type "show copying" to see the conditions. <br>There is absolutely no warranty for GDB. Type "show warranty" for details. <br>This GDB was configured as "i386-suse-linux"... <br>(gdb) l &lt;-------------------- l命令相当于list，从第一行开始例出原码。 <br>1 #include <br>2 <br>3 int func(int n) <br>4 { <br>5 int sum=0,i; <br>6 for(i=0; i 7 { <br>8 sum+=i; <br>9 } <br>10 return sum; <br>(gdb) &lt;-------------------- 直接回车表示，重复上一次命令 <br>11 } </p>
<p>12 <br>13 <br>14 main() <br>15 { <br>16 int i; <br>17 long result = 0; <br>18 for(i=1; i&lt;=100; i++) <br>19 { <br>20 result += i; <br>(gdb) break 16 &lt;-------------------- 设置断点，在源程序第16行处。 <br>Breakpoint 1 at 0x8048496: file tst.c, line 16. <br>(gdb) break func &lt;-------------------- 设置断点，在函数func()入口处。 <br>Breakpoint 2 at 0x8048456: file tst.c, line 5. <br>(gdb) info break &lt;-------------------- 查看断点信息。 <br>Num Type Disp Enb Address What <br>1 breakpoint keep y 0x08048496 in main at tst.c:16 <br>2 breakpoint keep y 0x08048456 in func at tst.c:5 <br>(gdb) r &lt;--------------------- 运行程序，run命令简写 <br>Starting program: /home/hchen/test/tst <br>Breakpoint 1, main () at tst.c:17 &lt;---------- 在断点处停住。 <br>17 long result = 0; <br>(gdb) n &lt;--------------------- 单条语句执行，next命令简写。 <br>18 for(i=1; i&lt;=100; i++) <br>(gdb) n <br>20 result += i; <br>(gdb) n <br>18 for(i=1; i&lt;=100; i++) <br>(gdb) n <br>20 result += i; <br>(gdb) c &lt;--------------------- 继续运行程序，continue命令简写。 <br>Continuing. <br>result[1-100] = 5050 &lt;----------程序输出。 <br>Breakpoint 2, func (n=250) at tst.c:5 <br>5 int sum=0,i; <br>(gdb) n <br>6 for(i=1; i&lt;=n; i++) <br>(gdb) p i &lt;--------------------- 打印变量i的值，print命令简写。 <br>$1 = 134513808 <br>(gdb) n <br>8 sum+=i; <br>(gdb) n <br>6 for(i=1; i&lt;=n; i++) </p>
<p>(gdb) p sum <br>$2 = 1 <br>(gdb) n <br>8 sum+=i; <br>(gdb) p i <br>$3 = 2 <br>(gdb) n <br>6 for(i=1; i&lt;=n; i++) <br>(gdb) p sum <br>$4 = 3 <br>(gdb) bt &lt;--------------------- 查看函数堆栈。 <br>#0 func (n=250) at tst.c:5 <br>#1 0x080484e4 in main () at tst.c:24 <br>#2 0x400409ed in __libc_start_main () from /lib/libc.so.6 <br>(gdb) finish &lt;--------------------- 退出函数。 <br>Run till exit from #0 func (n=250) at tst.c:5 <br>0x080484e4 in main () at tst.c:24 <br>24 printf("result[1-250] = %d \n", func(250) ); <br>Value returned is $6 = 31375 <br>(gdb) c &lt;--------------------- 继续运行。 <br>Continuing. <br>result[1-250] = 31375 &lt;----------程序输出。 <br>Program exited with code 027. &lt;--------程序退出，调试结束。 <br>(gdb) q &lt;--------------------- 退出gdb。 <br>hchen/test&gt; <br>好了，有了以上的感性认识，还是让我们来系统地认识一下gdb吧。 <br>使用GDB <br>———— <br>一般来说GDB主要调试的是C/C++的程序。要调试C/C++的程序，首先在编译时，我们必<br>须要把调试信息加到可执行文件中。使用编译器（cc/gcc/g++）的 -g 参数可以做到这一点。<br>如： <br>&gt; cc -g hello.c -o hello <br>&gt; g++ -g hello.cpp -o hello <br>如果没有-g，你将看不见程序的函数名、变量名，所代替的全是运行时的内存地址。当你用<br>-g把调试信息加入之后，并成功编译目标代码以后，让我们来看看如何用gdb来调试他。 <br>启动GDB的方法有以下几种： </p>
<p>1、gdb <br>program也就是你的执行文件，一般在当然目录下。 <br>2、gdb core <br>用gdb同时调试一个运行程序和core文件，core是程序非法执行后core dump后产生的文件。 <br>3、gdb <br>如果你的程序是一个服务程序，那么你可以指定这个服务程序运行时的进程ID。gdb会自<br>动attach上去，并调试他。program应该在PATH环境变量中搜索得到。 <br>GDB启动时，可以加上一些GDB的启动开关，详细的开关可以用gdb -help查看。我在下<br>面只例举一些比较常用的参数： <br>-symbols <br>-s <br>从指定文件中读取符号表。 <br>-se file <br>从指定文件中读取符号表信息，并把他用在可执行文件中。 <br>-core <br>-c <br>调试时core dump的core文件。 <br>-directory <br>-d <br>加入一个源文件的搜索路径。默认搜索路径是环境变量中PATH所定义的路径。 <br>GDB的命令概貌 <br>——————— <br>启动gdb后，就你被带入gdb的调试环境中，就可以使用gdb的命令开始调试程序了，gdb<br>的命令可以使用help命令来查看，如下所示： <br>/home/hchen&gt; gdb <br>GNU gdb 5.1.1 <br>Copyright 2002 Free Software Foundation, Inc. <br>GDB is free software, covered by the GNU General Public License, and you are <br>welcome to change it and/or distribute copies of it under certain conditions. <br>Type "show copying" to see the conditions. <br>There is absolutely no warranty for GDB. Type "show warranty" for details. <br>This GDB was configured as "i386-suse-linux". <br>(gdb) help </p>
<p>List of classes of commands: <br>aliases -- Aliases of other commands <br>breakpoints -- Making program stop at certain points <br>data -- Examining data <br>files -- Specifying and examining files <br>internals -- Maintenance commands <br>obscure -- Obscure features <br>running -- Running the program <br>stack -- Examining the stack <br>status -- Status inquiries <br>support -- Support facilities <br>tracepoints -- Tracing of program execution without stopping the program <br>user-defined -- User-defined commands <br>Type "help" followed by a class name for a list of commands in that class. <br>Type "help" followed by command name for full documentation. <br>Command name abbreviations are allowed if unambiguous. <br>(gdb) <br>gdb的命令很多，gdb把之分成许多个种类。help命令只是例出gdb的命令种类，如果要看<br>种类中的命令，可以使用help 命令，如：help breakpoints，查看设置断点的所有命令。也<br>可以直接help 来查看命令的帮助。 <br>gdb中，输入命令时，可以不用打全命令，只用打命令的前几个字符就可以了，当然，命令<br>的前几个字符应该要标志着一个唯一的命令，在Linux下，你可以敲击两次TAB键来补齐<br>命令的全称，如果有重复的，那么gdb会把其例出来。 <br>示例一：在进入函数func时，设置一个断点。可以敲入break func，或是直接就是b func <br>(gdb) b func <br>Breakpoint 1 at 0x8048458: file hello.c, line 10. <br>示例二：敲入b按两次TAB键，你会看到所有b打头的命令： <br>(gdb) b <br>backtrace break bt <br>(gdb) <br>示例三：只记得函数的前缀，可以这样： <br>(gdb) b make_ &lt;按TAB键&gt; <br>（再按下一次TAB键，你会看到:） <br>make_a_section_from_file make_environ <br>make_abs_section make_function_type <br>make_blockvector make_pointer_type </p>
<p>make_cleanup make_reference_type <br>make_command make_symbol_completion_list <br>(gdb) b make_ <br>GDB把所有make开头的函数全部例出来给你查看。 <br>示例四：调试C++的程序时，有可以函数名一样。如： <br>(gdb) b 'bubble( M-? <br>bubble(double,double) bubble(int,int) <br>(gdb) b 'bubble( <br>你可以查看到C++中的所有的重载函数及参数。（注：M-?和&#8220;按两次TAB键&#8221;是一个意思） <br>要退出gdb时，只用发quit或命令简称q就行了。 <br>GDB中运行UNIX的shell程序 <br>———————————— <br>在gdb环境中，你可以执行UNIX的shell的命令，使用gdb的shell命令来完成： <br>shell <br>调用UNIX的shell来执行，环境变量SHELL中定义的UNIX的shell将会被用来执行，如<br>果SHELL没有定义，那就使用UNIX的标准shell：/bin/sh。（在Windows中使用Command.com<br>或cmd.exe） <br>还有一个gdb命令是make： <br>make <br>可以在gdb中执行make命令来重新build自己的程序。这个命令等价于&#8220;shell make &#8221;。 <br>在GDB中运行程序 <br>———————— <br>当以gdb 方式启动gdb后，gdb会在PATH路径和当前目录中搜索的源文件。如要确认gdb<br>是否读到源文件，可使用l或list命令，看看gdb是否能列出源代码。 <br>在gdb中，运行程序使用r或是run命令。程序的运行，你有可能需要设置下面四方面的事。 <br>1、程序运行参数。 <br>set args 可指定运行时参数。（如：set args 10 20 30 40 50） <br>show args 命令可以查看设置好的运行参数。 <br>2、运行环境。 <br>path <br>可设定程序的运行路径。 <br>show paths 查看程序的运行路径。 </p>
<p>set environment varname [=value] 设置环境变量。如：set env USER=hchen <br>show environment [varname] 查看环境变量。 <br>3、工作目录。 <br>cd <br>相当于shell的cd命令。 <br>pwd 显示当前的所在目录。 <br>4、程序的输入输出。 <br>info terminal 显示你程序用到的终端的模式。 <br>使用重定向控制程序输出。如：run &gt; outfile <br>tty命令可以指写输入输出的终端设备。如：tty /dev/ttyb <br>调试已运行的程序 <br>———————— <br>两种方法： <br>1、在UNIX下用ps查看正在运行的程序的PID（进程ID），然后用gdb PID格式挂接正在<br>运行的程序。 <br>2、先用gdb 关联上源代码，并进行gdb，在gdb中用attach命令来挂接进程的PID。并用<br>detach来取消挂接的进程。 <br>暂停 / 恢复程序运行 <br>————————— <br>调试程序中，暂停程序运行是必须的，GDB可以方便地暂停程序的运行。你可以设置程序<br>的在哪行停住，在什么条件下停住，在收到什么信号时停往等等。以便于你查看运行时的变<br>量，以及运行时的流程。 <br>当进程被gdb停住时，你可以使用info program 来查看程序的是否在运行，进程号，被暂停<br>的原因。 <br>在gdb中，我们可以有以下几种暂停方式：断点（BreakPoint）、观察点（WatchPoint）、捕<br>捉点（CatchPoint）、信号（Signals）、线程停止（Thread Stops）。如果要恢复程序运行，可<br>以使用c或是continue命令。 <br>一、设置断点（BreakPoint） <br>我们用break命令来设置断点。正面有几点设置断点的方法： <br>break <br>在进入指定函数时停住。C++中可以使用class::function或function(type,type)格式来指定函</p>
<p>数名。 <br>break <br>在指定行号停住。 <br>break +offset <br>break -offset <br>在当前行号的前面或后面的offset行停住。offiset为自然数。 <br>break filename:linenum <br>在源文件filename的linenum行处停住。 <br>break filename:function <br>在源文件filename的function函数的入口处停住。 <br>break *address <br>在程序运行的内存地址处停住。 <br>break <br>break命令没有参数时，表示在下一条指令处停住。 <br>break ... if <br>...可以是上述的参数，condition表示条件，在条件成立时停住。比如在循环境体中，可以设<br>置break if i=100，表示当i为100时停住程序。 <br>查看断点时，可使用info命令，如下所示：（注：n表示断点号） <br>info breakpoints [n] <br>info break [n] <br>二、设置观察点（WatchPoint） <br>观察点一般来观察某个表达式（变量也是一种表达式）的值是否有变化了，如果有变化，马<br>上停住程序。我们有下面的几种方法来设置观察点： <br>watch <br>为表达式（变量）expr设置一个观察点。一量表达式值有变化时，马上停住程序。 <br>rwatch <br>当表达式（变量）expr被读时，停住程序。 <br>awatch <br>当表达式（变量）的值被读或被写时，停住程序。 </p>
<p>info watchpoints <br>列出当前所设置了的所有观察点。 <br>三、设置捕捉点（CatchPoint） <br>你可设置捕捉点来补捉程序运行时的一些事件。如：载入共享库（动态链接库）或是C++<br>的异常。设置捕捉点的格式为： <br>catch <br>当event发生时，停住程序。event可以是下面的内容： <br>1、throw 一个C++抛出的异常。（throw为关键字） <br>2、catch 一个C++捕捉到的异常。（catch为关键字） <br>3、exec 调用系统调用exec时。（exec为关键字，目前此功能只在HP-UX下有用） <br>4、fork 调用系统调用fork时。（fork为关键字，目前此功能只在HP-UX下有用） <br>5、vfork 调用系统调用vfork时。（vfork为关键字，目前此功能只在HP-UX下有用） <br>6、load 或 load 载入共享库（动态链接库）时。（load为关键字，目前此功能只在HP-UX<br>下有用） <br>7、unload 或 unload 卸载共享库（动态链接库）时。（unload为关键字，目前此功能只在<br>HP-UX下有用） <br>tcatch <br>只设置一次捕捉点，当程序停住以后，应点被自动删除。 <br>四、维护停止点 <br>上面说了如何设置程序的停止点，GDB中的停止点也就是上述的三类。在GDB中，如果你<br>觉得已定义好的停止点没有用了，你可以使用delete、clear、disable、enable这几个命令来<br>进行维护。 <br>clear <br>清除所有的已定义的停止点。 <br>clear <br>clear <br>清除所有设置在函数上的停止点。 <br>clear <br>clear <br>清除所有设置在指定行上的停止点。 <br>delete [breakpoints] [range...] <br>删除指定的断点，breakpoints为断点号。如果不指定断点号，则表示删除所有的断点。range <br>表示断点号的范围（如：3-7）。其简写命令为d。 </p>
<p>比删除更好的一种方法是disable停止点，disable了的停止点，GDB不会删除，当你还需要<br>时，enable即可，就好像回收站一样。 <br>disable [breakpoints] [range...] <br>disable所指定的停止点，breakpoints为停止点号。如果什么都不指定，表示disable所有的<br>停止点。简写命令是dis. <br>enable [breakpoints] [range...] <br>enable所指定的停止点，breakpoints为停止点号。 <br>enable [breakpoints] once range... <br>enable所指定的停止点一次，当程序停止后，该停止点马上被GDB自动disable。 <br>enable [breakpoints] delete range... <br>enable所指定的停止点一次，当程序停止后，该停止点马上被GDB自动删除。 <br>五、停止条件维护 <br>前面在说到设置断点时，我们提到过可以设置一个条件，当条件成立时，程序自动停止，这<br>是一个非常强大的功能，这里，我想专门说说这个条件的相关维护命令。一般来说，为断点<br>设置一个条件，我们使用if关键词，后面跟其断点条件。并且，条件设置好后，我们可以<br>用condition命令来修改断点的条件。（只有break和watch命令支持if，catch目前暂不支持<br>if） <br>condition <br>修改断点号为bnum的停止条件为expression。 <br>condition <br>清除断点号为bnum的停止条件。 <br>还有一个比较特殊的维护命令ignore，你可以指定程序运行时，忽略停止条件几次。 <br>ignore <br>表示忽略断点号为bnum的停止条件count次。 <br>六、为停止点设定运行命令 <br>我们可以使用GDB提供的command命令来设置停止点的运行命令。也就是说，当运行的<br>程序在被停止住时，我们可以让其自动运行一些别的命令，这很有利行自动化调试。对基于<br>GDB的自动化调试是一个强大的支持。 <br>commands [bnum] </p>
<p>... command-list ... <br>end <br>为断点号bnum指写一个命令列表。当程序被该断点停住时，gdb会依次运行命令列表中的<br>命令。 <br>例如： <br>break foo if x&gt;0 <br>commands <br>printf "x is %d\n",x <br>continue <br>end <br>断点设置在函数foo中，断点条件是x&gt;0，如果程序被断住后，也就是，一旦x的值在foo<br>函数中大于0，GDB会自动打印出x的值，并继续运行程序。 <br>如果你要清除断点上的命令序列，那么只要简单的执行一下commands命令，并直接在打个<br>end就行了。 <br>七、断点菜单 <br>在C++中，可能会重复出现同一个名字的函数若干次（函数重载），在这种情况下，break 不<br>能告诉GDB要停在哪个函数的入口。当然，你可以使用break 也就是把函数的参数类型告<br>诉GDB，以指定一个函数。否则的话，GDB会给你列出一个断点菜单供你选择你所需要的<br>断点。你只要输入你菜单列表中的编号就可以了。如： <br>(gdb) b String::after <br>[0] cancel <br>[1] all <br>[2] file:String.cc; line number:867 <br>[3] file:String.cc; line number:860 <br>[4] file:String.cc; line number:875 <br>[5] file:String.cc; line number:853 <br>[6] file:String.cc; line number:846 <br>[7] file:String.cc; line number:735 <br>&gt; 2 4 6 <br>Breakpoint 1 at 0xb26c: file String.cc, line 867. <br>Breakpoint 2 at 0xb344: file String.cc, line 875. <br>Breakpoint 3 at 0xafcc: file String.cc, line 846. <br>Multiple breakpoints were set. <br>Use the "delete" command to delete unwanted <br>breakpoints. <br>(gdb) </p>
<p>可见，GDB列出了所有after的重载函数，你可以选一下列表编号就行了。0表示放弃设置<br>断点，1表示所有函数都设置断点。 <br>八、恢复程序运行和单步调试 <br>当程序被停住了，你可以用continue命令恢复程序的运行直到程序结束，或下一个断点到来。<br>也可以使用step或next命令单步跟踪程序。 <br>continue [ignore-count] <br>c [ignore-count] <br>fg [ignore-count] <br>恢复程序运行，直到程序结束，或是下一个断点到来。ignore-count表示忽略其后的断点次<br>数。continue，c，fg三个命令都是一样的意思。 <br>step <br>单步跟踪，如果有函数调用，他会进入该函数。进入函数的前提是，此函数被编译有debug<br>信息。很像VC等工具中的step in。后面可以加count也可以不加，不加表示一条条地执行，<br>加表示执行后面的count条指令，然后再停住。 <br>next <br>同样单步跟踪，如果有函数调用，他不会进入该函数。很像VC等工具中的step over。后面<br>可以加count也可以不加，不加表示一条条地执行，加表示执行后面的count条指令，然后<br>再停住。 <br>set step-mode <br>set step-mode on <br>打开step-mode模式，于是，在进行单步跟踪时，程序不会因为没有debug信息而不停住。<br>这个参数有很利于查看机器码。 <br>set step-mod off <br>关闭step-mode模式。 <br>finish <br>运行程序，直到当前函数完成返回。并打印函数返回时的堆栈地址和返回值及参数值等信息。 <br>until 或 u <br>当你厌倦了在一个循环体内单步跟踪时，这个命令可以运行程序直到退出循环体。 <br>stepi 或 si <br>nexti 或 ni <br>单步跟踪一条机器指令！一条程序代码有可能由数条机器指令完成，stepi和nexti可以单步<br>执行机器指令。与之一样有相同功能的命令是&#8220;display/i $pc&#8221; ，当运行完这个命令后，单<br>步跟踪会在打出程序代码的同时打出机器指令（也就是汇编代码） </p>
<p>九、信号（Signals） <br>信号是一种软中断，是一种处理异步事件的方法。一般来说，操作系统都支持许多信号。尤<br>其是UNIX，比较重要应用程序一般都会处理信号。UNIX定义了许多信号，比如SIGINT<br>表示中断字符信号，也就是Ctrl+C的信号，SIGBUS表示硬件故障的信号；SIGCHLD表示<br>子进程状态改变信号；SIGKILL表示终止程序运行的信号，等等。信号量编程是UNIX下<br>非常重要的一种技术。 <br>GDB有能力在你调试程序的时候处理任何一种信号，你可以告诉GDB需要处理哪一种信<br>号。你可以要求GDB收到你所指定的信号时，马上停住正在运行的程序，以供你进行调试。<br>你可以用GDB的handle命令来完成这一功能。 <br>handle <br>在GDB中定义一个信号处理。信号可以以SIG开头或不以SIG开头，可以用定义一个要处<br>理信号的范围（如：SIGIO-SIGKILL，表示处理从SIGIO信号到SIGKILL的信号，其中包<br>括SIGIO，SIGIOT，SIGKILL三个信号），也可以使用关键字all来标明要处理所有的信号。<br>一旦被调试的程序接收到信号，运行程序马上会被GDB停住，以供调试。其可以是以下几<br>种关键字的一个或多个。 <br>nostop <br>当被调试的程序收到信号时，GDB不会停住程序的运行，但会打出消息告诉你收到这种信<br>号。 <br>stop <br>当被调试的程序收到信号时，GDB会停住你的程序。 <br>print <br>当被调试的程序收到信号时，GDB会显示出一条信息。 <br>noprint <br>当被调试的程序收到信号时，GDB不会告诉你收到信号的信息。 <br>pass <br>noignore <br>当被调试的程序收到信号时，GDB不处理信号。这表示，GDB会把这个信号交给被调试程<br>序会处理。 <br>nopass <br>ignore <br>当被调试的程序收到信号时，GDB不会让被调试程序来处理这个信号。 <br>info signals <br>info handle <br>查看有哪些信号在被GDB检测中。 <br>十、线程（Thread Stops） </p>
<p>如果你程序是多线程的话，你可以定义你的断点是否在所有的线程上，或是在某个特定的线<br>程。GDB很容易帮你完成这一工作。 <br>break thread <br>break thread if ... <br>linespec指定了断点设置在的源程序的行号。threadno指定了线程的ID，注意，这个ID是<br>GDB分配的，你可以通过&#8220;info threads&#8221;命令来查看正在运行程序中的线程信息。如果你<br>不指定thread 则表示你的断点设在所有线程上面。你还可以为某线程指定断点条件。如： <br>(gdb) break frik.c:13 thread 28 if bartab &gt; lim <br>当你的程序被GDB停住时，所有的运行线程都会被停住。这方便你你查看运行程序的总体<br>情况。而在你恢复程序运行时，所有的线程也会被恢复运行。那怕是主进程在被单步调试时。 <br>查看栈信息 <br>————— <br>当程序被停住了，你需要做的第一件事就是查看程序是在哪里停住的。当你的程序调用了一<br>个函数，函数的地址，函数参数，函数内的局部变量都会被压入&#8220;栈&#8221;（Stack）中。你可以<br>用GDB命令来查看当前的栈中的信息。 <br>下面是一些查看函数调用栈信息的GDB命令： <br>backtrace <br>bt <br>打印当前的函数调用栈的所有信息。如： <br>(gdb) bt <br>#0 func (n=250) at tst.c:6 <br>#1 0x08048524 in main (argc=1, argv=0xbffff674) at tst.c:30 <br>#2 0x400409ed in __libc_start_main () from /lib/libc.so.6 <br>从上可以看出函数的调用栈信息：__libc_start_main --&gt; main() --&gt; func() <br>backtrace <br>bt <br>n是一个正整数，表示只打印栈顶上n层的栈信息。 <br>backtrace &lt;-n&gt; <br>bt &lt;-n&gt; <br>-n表一个负整数，表示只打印栈底下n层的栈信息。 <br>如果你要查看某一层的信息，你需要在切换当前的栈，一般来说，程序停止时，最顶层的栈</p>
<p>就是当前栈，如果你要查看栈下面层的详细信息，首先要做的是切换当前栈。 <br>frame <br>f <br>n是一个从0开始的整数，是栈中的层编号。比如：frame 0，表示栈顶，frame 1，表示栈的<br>第二层。 <br>up <br>表示向栈的上面移动n层，可以不打n，表示向上移动一层。 <br>down <br>表示向栈的下面移动n层，可以不打n，表示向下移动一层。 <br>上面的命令，都会打印出移动到的栈层的信息。如果你不想让其打出信息。你可以使用这三<br>个命令： <br>select-frame 对应于 frame 命令。 <br>up-silently 对应于 up 命令。 <br>down-silently 对应于 down 命令。 <br>查看当前栈层的信息，你可以用以下GDB命令： <br>frame 或 f <br>会打印出这些信息：栈的层编号，当前的函数名，函数参数值，函数所在文件及行号，函数<br>执行到的语句。 <br>info frame <br>info f <br>这个命令会打印出更为详细的当前栈层的信息，只不过，大多数都是运行时的内内地址。比<br>如：函数地址，调用函数的地址，被调用函数的地址，目前的函数是由什么样的程序语言写<br>成的、函数参数地址及值、局部变量的地址等等。如： <br>(gdb) info f <br>Stack level 0, frame at 0xbffff5d4: <br>eip = 0x804845d in func (tst.c:6); saved eip 0x8048524 <br>called by frame at 0xbffff60c <br>source language c. <br>Arglist at 0xbffff5d4, args: n=250 <br>Locals at 0xbffff5d4, Previous frame's sp is 0x0 <br>Saved registers: <br>ebp at 0xbffff5d4, eip at 0xbffff5d8 <br>info args </p>
<p>打印出当前函数的参数名及其值。 <br>info locals <br>打印出当前函数中所有局部变量及其值。 <br>info catch <br>打印出当前的函数中的异常处理信息。 <br>查看源程序 <br>————— <br>一、显示源代码 <br>GDB 可以打印出所调试程序的源代码，当然，在程序编译时一定要加上-g的参数，把源程<br>序信息编译到执行文件中。不然就看不到源程序了。当程序停下来以后，GDB会报告程序<br>停在了那个文件的第几行上。你可以用list命令来打印程序的源代码。还是来看一看查看源<br>代码的GDB命令吧。 <br>list <br>显示程序第linenum行的周围的源程序。 <br>list <br>显示函数名为function的函数的源程序。 <br>list <br>显示当前行后面的源程序。 <br>list - <br>显示当前行前面的源程序。 <br>一般是打印当前行的上5行和下5行，如果显示函数是是上2行下8行，默认是10行，当<br>然，你也可以定制显示的范围，使用下面命令可以设置一次显示源程序的行数。 <br>set listsize <br>设置一次显示源代码的行数。 <br>show listsize <br>查看当前listsize的设置。 <br>list命令还有下面的用法： <br>list , </p>
<p>显示从first行到last行之间的源代码。 <br>list , <br>显示从当前行到last行之间的源代码。 <br>list + <br>往后显示源代码。 <br>一般来说在list后面可以跟以下这们的参数： <br>行号。 <br>&lt;+offset&gt; 当前行号的正偏移量。 <br>&lt;-offset&gt; 当前行号的负偏移量。 <br>哪个文件的哪一行。 <br>函数名。 <br>哪个文件中的哪个函数。 <br>&lt;*address&gt; 程序运行时的语句在内存中的地址。 <br>二、搜索源代码 <br>不仅如此，GDB还提供了源代码搜索的命令： <br>forward-search <br>search <br>向前面搜索。 <br>reverse-search <br>全部搜索。 <br>其中，就是正则表达式，也主一个字符串的匹配模式，关于正则表达式，我就不在这里讲了，<br>还请各位查看相关资料。 <br>三、指定源文件的路径 <br>某些时候，用-g编译过后的执行程序中只是包括了源文件的名字，没有路径名。GDB提供<br>了可以让你指定源文件的路径的命令，以便GDB进行搜索。 <br>directory <br>dir <br>加一个源文件路径到当前路径的前面。如果你要指定多个路径，UNIX下你可以使用&#8220;:&#8221;，<br>Windows下你可以使用&#8220;;&#8221;。 </p>
<p>directory <br>清除所有的自定义的源文件搜索路径信息。 <br>show directories <br>显示定义了的源文件搜索路径。 <br>四、源代码的内存 <br>你可以使用info line命令来查看源代码在内存中的地址。info line后面可以跟&#8220;行号&#8221;，&#8220;函<br>数名&#8221;，&#8220;文件名:行号&#8221;，&#8220;文件名:函数名&#8221;，这个命令会打印出所指定的源码在运行时的内<br>存地址，如： <br>(gdb) info line tst.c:func <br>Line 5 of "tst.c" starts at address 0x8048456 and ends at 0x804845d . <br>还有一个命令（disassemble）你可以查看源程序的当前执行时的机器码，这个命令会把目前<br>内存中的指令dump出来。如下面的示例表示查看函数func的汇编代码。 <br>(gdb) disassemble func <br>Dump of assembler code for function func: <br>0x8048450 : push %ebp <br>0x8048451 : mov %esp,%ebp <br>0x8048453 : sub $0x18,%esp <br>0x8048456 : movl $0x0,0xfffffffc(%ebp) <br>0x804845d : movl $0x1,0xfffffff8(%ebp) <br>0x8048464 : mov 0xfffffff8(%ebp),%eax <br>0x8048467 : cmp 0x8(%ebp),%eax <br>0x804846a : jle 0x8048470 <br>0x804846c : jmp 0x8048480 <br>0x804846e : mov %esi,%esi <br>0x8048470 : mov 0xfffffff8(%ebp),%eax <br>0x8048473 : add %eax,0xfffffffc(%ebp) <br>0x8048476 : incl 0xfffffff8(%ebp) <br>0x8048479 : jmp 0x8048464 <br>0x804847b : nop <br>0x804847c : lea 0x0(%esi,1),%esi <br>0x8048480 : mov 0xfffffffc(%ebp),%edx <br>0x8048483 : mov %edx,%eax <br>0x8048485 : jmp 0x8048487 <br>0x8048487 : mov %ebp,%esp <br>0x8048489 : pop %ebp <br>0x804848a : ret <br>End of assembler dump. </p>
<p>查看运行时数据 <br>——————— <br>在你调试程序时，当程序被停住时，你可以使用print命令（简写命令为p），或是同义命令<br>inspect来查看当前程序的运行数据。print命令的格式是： <br>print <br>print / <br>是表达式，是你所调试的程序的语言的表达式（GDB可以调试多种编程语言），是输出的格<br>式，比如，如果要把表达式按16进制的格式输出，那么就是/x。 <br>一、表达式 <br>print和许多GDB的命令一样，可以接受一个表达式，GDB会根据当前的程序运行的数据<br>来计算这个表达式，既然是表达式，那么就可以是当前程序运行中的const常量、变量、函<br>数等内容。可惜的是GDB不能使用你在程序中所定义的宏。 <br>表达式的语法应该是当前所调试的语言的语法，由于C/C++是一种大众型的语言，所以，本<br>文中的例子都是关于C/C++的。（而关于用GDB调试其它语言的章节，我将在后面介绍） <br>在表达式中，有几种GDB所支持的操作符，它们可以用在任何一种语言中。 <br>@ <br>是一个和数组有关的操作符，在后面会有更详细的说明。 <br>:: <br>指定一个在文件或是一个函数中的变量。 <br>{} <br>表示一个指向内存地址的类型为type的一个对象。 <br>二、程序变量 <br>在GDB中，你可以随时查看以下三种变量的值： <br>1、全局变量（所有文件可见的） <br>2、静态全局变量（当前文件可见的） <br>3、局部变量（当前Scope可见的） <br>如果你的局部变量和全局变量发生冲突（也就是重名），一般情况下是局部变量会隐藏全局<br>变量，也就是说，如果一个全局变量和一个函数中的局部变量同名时，如果当前停止点在函</p>
<p>数中，用print显示出的变量的值会是函数中的局部变量的值。如果此时你想查看全局变量<br>的值时，你可以使用&#8220;::&#8221;操作符： <br>file::variable <br>function::variable <br>可以通过这种形式指定你所想查看的变量，是哪个文件中的或是哪个函数中的。例如，查看<br>文件f2.c中的全局变量x的值： <br>gdb) p 'f2.c'::x <br>当然，&#8220;::&#8221;操作符会和C++中的发生冲突，GDB能自动识别&#8220;::&#8221; 是否C++的操作符，所<br>以你不必担心在调试C++程序时会出现异常。 <br>另外，需要注意的是，如果你的程序编译时开启了优化选项，那么在用GDB调试被优化过<br>的程序时，可能会发生某些变量不能访问，或是取值错误码的情况。这个是很正常的，因为<br>优化程序会删改你的程序，整理你程序的语句顺序，剔除一些无意义的变量等，所以在GDB<br>调试这种程序时，运行时的指令和你所编写指令就有不一样，也就会出现你所想象不到的结<br>果。对付这种情况时，需要在编译程序时关闭编译优化。一般来说，几乎所有的编译器都支<br>持编译优化的开关，例如，GNU的C/C++编译器GCC，你可以使用&#8220;-gstabs&#8221;选项来解决<br>这个问题。关于编译器的参数，还请查看编译器的使用说明文档。 <br>三、数组 <br>有时候，你需要查看一段连续的内存空间的值。比如数组的一段，或是动态分配的数据的大<br>小。你可以使用GDB的&#8220;@&#8221;操作符，&#8220;@&#8221;的左边是第一个内存的地址的值，&#8220;@&#8221;的右<br>边则你你想查看内存的长度。例如，你的程序中有这样的语句： <br>int *array = (int *) malloc (len * sizeof (int)); <br>于是，在GDB调试过程中，你可以以如下命令显示出这个动态数组的取值： <br>p <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#42;&#97;&#114;&#114;&#97;&#121;&#64;&#108;&#101;&#110;"><font color=#336699>*array@len</font></a> <br>@的左边是数组的首地址的值，也就是变量array所指向的内容，右边则是数据的长度，其<br>保存在变量len中，其输出结果，大约是下面这个样子的： <br>(gdb) p <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#42;&#97;&#114;&#114;&#97;&#121;&#64;&#108;&#101;&#110;"><font color=#336699>*array@len</font></a> <br>$1 = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40} <br>如果是静态数组的话，可以直接用print数组名，就可以显示数组中所有数据的内容了。 <br>四、输出格式 </p>
<p>一般来说，GDB会根据变量的类型输出变量的值。但你也可以自定义GDB的输出的格式。<br>例如，你想输出一个整数的十六进制，或是二进制来查看这个整型变量的中的位的情况。要<br>做到这样，你可以使用GDB的数据显示格式： <br>x 按十六进制格式显示变量。 <br>d 按十进制格式显示变量。 <br>u 按十六进制格式显示无符号整型。 <br>o 按八进制格式显示变量。 <br>t 按二进制格式显示变量。 <br>a 按十六进制格式显示变量。 <br>c 按字符格式显示变量。 <br>f 按浮点数格式显示变量。 <br>(gdb) p i <br>$21 = 101 <br>(gdb) p/a i <br>$22 = 0x65 <br>(gdb) p/c i <br>$23 = 101 'e' <br>(gdb) p/f i <br>$24 = 1.41531145e-43 <br>(gdb) p/x i <br>$25 = 0x65 <br>(gdb) p/t i <br>$26 = 1100101 <br>五、查看内存 <br>你可以使用examine命令（简写是x）来查看内存地址中的值。x命令的语法如下所示： <br>x/ <br>n、f、u是可选的参数。 <br>n 是一个正整数，表示显示内存的长度，也就是说从当前地址向后显示几个地址的内容。 <br>f 表示显示的格式，参见上面。如果地址所指的是字符串，那么格式可以是s，如果地十是<br>指令地址，那么格式可以是i。 </p>
<p>u 表示从当前地址往后请求的字节数，如果不指定的话，GDB默认是4个bytes。u参数可<br>以用下面的字符来代替，b表示单字节，h表示双字节，w表示四字节，g表示八字节。当<br>我们指定了字节长度后，GDB会从指内存定的内存地址开始，读写指定字节，并把其当作<br>一个值取出来。 <br>表示一个内存地址。 <br>n/f/u三个参数可以一起使用。例如： <br>命令：x/3uh 0x54320 表示，从内存地址0x54320读取内容，h表示以双字节为一个单位，3<br>表示三个单位，u表示按十六进制显示。 <br>六、自动显示 <br>你可以设置一些自动显示的变量，当程序停住时，或是在你单步跟踪时，这些变量会自动显<br>示。相关的GDB命令是display。 <br>display <br>display/ <br>display/ <br>expr是一个表达式，fmt表示显示的格式，addr表示内存地址，当你用display设定好了一<br>个或多个表达式后，只要你的程序被停下来，GDB会自动显示你所设置的这些表达式的值。 <br>格式i和s同样被display支持，一个非常有用的命令是： <br>display/i $pc <br>$pc是GDB的环境变量，表示着指令的地址，/i则表示输出格式为机器指令码，也就是汇<br>编。于是当程序停下后，就会出现源代码和机器指令码相对应的情形，这是一个很有意思的<br>功能。 <br>下面是一些和display相关的GDB命令： <br>undisplay <br>delete display <br>删除自动显示，dnums意为所设置好了的自动显式的编号。如果要同时删除几个，编号可以<br>用空格分隔，如果要删除一个范围内的编号，可以用减号表示（如：2-5） <br>disable display <br>enable display <br>disable和enalbe不删除自动显示的设置，而只是让其失效和恢复。 </p>
<p>info display <br>查看display设置的自动显示的信息。GDB会打出一张表格，向你报告当然调试中设置了多<br>少个自动显示设置，其中包括，设置的编号，表达式，是否enable。 <br>七、设置显示选项 <br>GDB中关于显示的选项比较多，这里我只例举大多数常用的选项。 <br>set print address <br>set print address on <br>打开地址输出，当程序显示函数信息时，GDB会显出函数的参数地址。系统默认为打开的，<br>如： <br>(gdb) f <br>#0 set_quotes (lq=0x34c78 "&lt;&lt;", rq=0x34c88 "&gt;&gt;") <br>at input.c:530 <br>530 if (lquote != def_lquote) <br>set print address off <br>关闭函数的参数地址显示，如： <br>(gdb) set print addr off <br>(gdb) f <br>#0 set_quotes (lq="&lt;&lt;", rq="&gt;&gt;") at input.c:530 <br>530 if (lquote != def_lquote) <br>show print address <br>查看当前地址显示选项是否打开。 <br>set print array <br>set print array on <br>打开数组显示，打开后当数组显示时，每个元素占一行，如果不打开的话，每个元素则以逗<br>号分隔。这个选项默认是关闭的。与之相关的两个命令如下，我就不再多说了。 <br>set print array off <br>show print array <br>set print elements <br>这个选项主要是设置数组的，如果你的数组太大了，那么就可以指定一个来指定数据显示的<br>最大长度，当到达这个长度时，GDB就不再往下显示了。如果设置为0，则表示不限制。 <br>show print elements </p>
<p>查看print elements的选项信息。 <br>set print null-stop <br>如果打开了这个选项，那么当显示字符串时，遇到结束符则停止显示。这个选项默认为off。 <br>set print pretty on <br>如果打开printf pretty这个选项，那么当GDB显示结构体时会比较漂亮。如： <br>$1 = { <br>next = 0x0, <br>flags = { <br>sweet = 1, <br>sour = 1 <br>}, <br>meat = 0x54 "Pork" <br>} <br>set print pretty off <br>关闭printf pretty这个选项，GDB显示结构体时会如下显示： <br>$1 = {next = 0x0, flags = {sweet = 1, sour = 1}, meat = 0x54 "Pork"} <br>show print pretty <br>查看GDB是如何显示结构体的。 <br>set print sevenbit-strings <br>设置字符显示，是否按&#8220;\nnn&#8221;的格式显示，如果打开，则字符串或字符数据按\nnn显示，<br>如&#8220;\065&#8221;。 <br>show print sevenbit-strings <br>查看字符显示开关是否打开。 <br>set print union <br>设置显示结构体时，是否显式其内的联合体数据。例如有以下数据结构： <br>typedef enum {Tree, Bug} Species; <br>typedef enum {Big_tree, Acorn, Seedling} Tree_forms; <br>typedef enum {Caterpillar, Cocoon, Butterfly} <br>Bug_forms; <br>struct thing { <br>Species it; <br>union { </p>
<p>Tree_forms tree; <br>Bug_forms bug; <br>} form; <br>}; <br>struct thing foo = {Tree, {Acorn}}; <br>当打开这个开关时，执行 p foo 命令后，会如下显示： <br>$1 = {it = Tree, form = {tree = Acorn, bug = Cocoon}} <br>当关闭这个开关时，执行 p foo 命令后，会如下显示： <br>$1 = {it = Tree, form = {...}} <br>show print union <br>查看联合体数据的显示方式 <br>set print object <br>在C++中，如果一个对象指针指向其派生类，如果打开这个选项，GDB会自动按照虚方法<br>调用的规则显示输出，如果关闭这个选项的话，GDB就不管虚函数表了。这个选项默认是<br>off。 <br>show print object <br>查看对象选项的设置。 <br>set print static-members <br>这个选项表示，当显示一个C++对象中的内容是，是否显示其中的静态数据成员。默认是<br>on。 <br>show print static-members <br>查看静态数据成员选项设置。 <br>set print vtbl <br>当此选项打开时，GDB将用比较规整的格式来显示虚函数表时。其默认是关闭的。 <br>show print vtbl <br>查看虚函数显示格式的选项。 <br>八、历史记录 <br>当你用GDB的print查看程序运行时的数据时，你每一个print都会被GDB记录下来。GDB<br>会以$1, $2, $3 .....这样的方式为你每一个print命令编上号。于是，你可以使用这个编号访问<br>以前的表达式，如$1。这个功能所带来的好处是，如果你先前输入了一个比较长的表达式，<br>如果你还想查看这个表达式的值，你可以使用历史记录来访问，省去了重复输入。 </p>
<p>九、GDB环境变量 <br>你可以在GDB的调试环境中定义自己的变量，用来保存一些调试程序中的运行数据。要定<br>义一个GDB的变量很简单只需。使用GDB的set命令。GDB的环境变量和UNIX一样，<br>也是以$起头。如： <br>set $foo = *object_ptr <br>使用环境变量时，GDB会在你第一次使用时创建这个变量，而在以后的使用中，则直接对<br>其賦值。环境变量没有类型，你可以给环境变量定义任一的类型。包括结构体和数组。 <br>show convenience <br>该命令查看当前所设置的所有的环境变量。 <br>这是一个比较强大的功能，环境变量和程序变量的交互使用，将使得程序调试更为灵活便捷。<br>例如： <br>set $i = 0 <br>print bar[$i++]-&gt;contents <br>于是，当你就不必，print bar[0]-&gt;contents, print bar[1]-&gt;contents地输入命令了。输入这样的<br>命令后，只用敲回车，重复执行上一条语句，环境变量会自动累加，从而完成逐个输出的功<br>能。 <br>十、查看寄存器 <br>要查看寄存器的值，很简单，可以使用如下命令： <br>info registers <br>查看寄存器的情况。（除了浮点寄存器） <br>info all-registers <br>查看所有寄存器的情况。（包括浮点寄存器） <br>info registers <br>查看所指定的寄存器的情况。 <br>寄存器中放置了程序运行时的数据，比如程序当前运行的指令地址（ip），程序的当前堆栈<br>地址（sp）等等。你同样可以使用print命令来访问寄存器的情况，只需要在寄存器名字前<br>加一个$符号就可以了。如：p $eip。 </p>
<p>改变程序的执行 <br>——————— <br>一旦使用GDB挂上被调试程序，当程序运行起来后，你可以根据自己的调试思路来动态地<br>在GDB中更改当前被调试程序的运行线路或是其变量的值，这个强大的功能能够让你更好<br>的调试你的程序，比如，你可以在程序的一次运行中走遍程序的所有分支。 <br>一、修改变量值 <br>修改被调试程序运行时的变量值，在GDB中很容易实现，使用GDB的print命令即可完成。<br>如： <br>(gdb) print x=4 <br>x=4这个表达式是C/C++的语法，意为把变量x的值修改为4，如果你当前调试的语言是<br>Pascal，那么你可以使用Pascal的语法：x:=4。 <br>在某些时候，很有可能你的变量和GDB中的参数冲突，如： <br>(gdb) whatis width <br>type = double <br>(gdb) p width <br>$4 = 13 <br>(gdb) set width=47 <br>Invalid syntax in expression. <br>因为，set width是GDB的命令，所以，出现了&#8220;Invalid syntax in expression&#8221;的设置错误，<br>此时，你可以使用set var命令来告诉GDB，width不是你GDB的参数，而是程序的变量名，<br>如： <br>(gdb) set var width=47 <br>另外，还可能有些情况，GDB并不报告这种错误，所以保险起见，在你改变程序变量取值<br>时，最好都使用set var格式的GDB命令。 <br>二、跳转执行 <br>一般来说，被调试程序会按照程序代码的运行顺序依次执行。GDB提供了乱序执行的功能，<br>也就是说，GDB可以修改程序的执行顺序，可以让程序执行随意跳跃。这个功能可以由GDB<br>的jump命令来完： <br>jump </p>
<p>指定下一条语句的运行点。可以是文件的行号，可以是file:line格式，可以是+num这种偏<br>移量格式。表式着下一条运行语句从哪里开始。 <br>jump <br>这里的 <br>是代码行的内存地址。 <br>注意，jump命令不会改变当前的程序栈中的内容，所以，当你从一个函数跳到另一个函数<br>时，当函数运行完返回时进行弹栈操作时必然会发生错误，可能结果还是非常奇怪的，甚至<br>于产生程序Core Dump。所以最好是同一个函数中进行跳转。 <br>熟悉汇编的人都知道，程序运行时，有一个寄存器用于保存当前代码所在的内存地址。所以，<br>jump命令也就是改变了这个寄存器中的值。于是，你可以使用&#8220;set $pc&#8221;来更改跳转执行<br>的地址。如： <br>set $pc = 0x485 <br>三、产生信号量 <br>使用singal命令，可以产生一个信号量给被调试的程序。如：中断信号Ctrl+C。这非常方便<br>于程序的调试，可以在程序运行的任意位置设置断点，并在该断点用GDB产生一个信号量，<br>这种精确地在某处产生信号非常有利程序的调试。 <br>语法是：signal ，UNIX的系统信号量通常从1到15。所以取值也在这个范围。 <br>single命令和shell的kill命令不同，系统的kill命令发信号给被调试程序时，是由GDB截<br>获的，而single命令所发出一信号则是直接发给被调试程序的。 <br>四、强制函数返回 <br>如果你的调试断点在某个函数中，并还有语句没有执行完。你可以使用return命令强制函数<br>忽略还没有执行的语句并返回。 <br>return <br>return <br>使用return命令取消当前函数的执行，并立即返回，如果指定了，那么该表达式的值会被认<br>作函数的返回值。 <br>五、强制调用函数 </p>
<p>call <br>表达式中可以一是函数，以此达到强制调用函数的目的。并显示函数的返回值，如果函数返<br>回值是void，那么就不显示。 <br>另一个相似的命令也可以完成这一功能——print，print后面可以跟表达式，所以也可以用他<br>来调用函数，print和call的不同是，如果函数返回void，call则不显示，print则显示函数返<br>回值，并把该值存入历史数据中。 <br>在不同语言中使用GDB <br>—————————— <br>GDB支持下列语言：C, C++, Fortran, PASCAL, Java, Chill, assembly, 和 Modula-2。一般说<br>来，GDB会根据你所调试的程序来确定当然的调试语言，比如：发现文件名后缀为&#8220;.c&#8221;<br>的，GDB会认为是C程序。文件名后缀为&#8220;.C, .cc, .cp, .cpp, .cxx, .c++&#8221;的，GDB会认为是<br>C++程序。而后缀是&#8220;.f, .F&#8221;的，GDB会认为是Fortran程序，还有，后缀为如果是&#8220;.s, .S&#8221;<br>的会认为是汇编语言。 <br>也就是说，GDB会根据你所调试的程序的语言，来设置自己的语言环境，并让GDB的命令<br>跟着语言环境的改变而改变。比如一些GDB命令需要用到表达式或变量时，这些表达式或<br>变量的语法，完全是根据当前的语言环境而改变的。例如C/C++中对指针的语法是*p，而在<br>Modula-2中则是p^。并且，如果你当前的程序是由几种不同语言一同编译成的，那到在调<br>试过程中，GDB也能根据不同的语言自动地切换语言环境。这种跟着语言环境而改变的功<br>能，真是体贴开发人员的一种设计。 <br>下面是几个相关于GDB语言环境的命令： <br>show language <br>查看当前的语言环境。如果GDB不能识为你所调试的编程语言，那么，C语言被认为是默<br>认的环境。 <br>info frame <br>查看当前函数的程序语言。 <br>info source <br>查看当前文件的程序语言。 <br>如果GDB没有检测出当前的程序语言，那么你也可以手动设置当前的程序语言。使用set <br>language命令即可做到。 <br>当set language命令后什么也不跟的话，你可以查看GDB所支持的语言种类： </p>
<p>(gdb) set language <br>The currently understood settings are: <br>local or auto Automatic setting based on source file <br>c Use the C language <br>c++ Use the C++ language <br>asm Use the Asm language <br>chill Use the Chill language <br>fortran Use the Fortran language <br>java Use the Java language <br>modula-2 Use the Modula-2 language <br>pascal Use the Pascal language <br>scheme Use the Scheme language <br>于是你可以在set language后跟上被列出来的程序语言名，来设置当前的语言环境。 <br>后记 <br>—— <br>GDB是一个强大的命令行调试工具。大家知道命令行的强大就是在于，其可以形成执行序<br>列，形成脚本。UNIX下的软件全是命令行的，这给程序开发提代供了极大的便利，命令行<br>软件的优势在于，它们可以非常容易的集成在一起，使用几个简单的已有工具的命令，就可<br>以做出一个非常强大的功能。 <br>于是UNIX下的软件比Windows下的软件更能有机地结合，各自发挥各自的长处，组合成<br>更为强劲的功能。而Windows下的图形软件基本上是各自为营，互相不能调用，很不利于<br>各种软件的相互集成。在这里并不是要和Windows做个什么比较，所谓&#8220;寸有所长，尺有<br>所短&#8221;，图形化工具还是有不如命令行的地方。（看到这句话时，希望各位千万再也不要认为<br>我就是&#8220;鄙视图形界面&#8221;，和我抬杠了 ） <br>我是根据版本为5.1.1的GDB所写的这篇文章，所以可能有些功能已被修改，或是又有更<br>为强劲的功能。而且，我写得非常仓促，写得比较简略，并且，其中我已经看到有许多错别<br>字了（我用五笔，所以错字让你看不懂），所以，我在这里对我文中的差错表示万分的歉意。 <br>文中所罗列的GDB的功能时，我只是罗列了一些带用的GDB的命令和使用方法，其实，<br>我这里只讲述的功能大约只占GDB所有功能的60%吧，详细的文档，还是请查看GDB的<br>帮助和使用手册吧，或许，过段时间，如果我有空，我再写一篇GDB的高级使用。 <br>我个人非常喜欢GDB的自动调试的功能，这个功能真的很强大，试想，我在UNIX下写个<br>脚本，让脚本自动编译我的程序，被自动调试，并把结果报告出来，调试成功，自动checkin<br>源码库。一个命令，编译带着调试带着checkin，多爽啊。只是GDB对自动化调试目前支持<br>还不是很成熟，只能实现半自动化，真心期望着GDB的自动化调试功能的成熟。 </p>
<p>如果各位对GDB或是别的技术问题有兴趣的话，欢迎和我讨论交流。本人目前主要在UNIX<br>下做产品软件的开发，所以，对UNIX下的软件开发比较熟悉，当然，不单单是技术，对软<br>件工程实施，软件设计，系统分析，项目管理我也略有心得。欢迎大家找我交流，（QQ是：<br>753640，MSN是：<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#104;&#97;&#111;&#101;&#108;&#64;&#104;&#111;&#116;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;"><font color=#336699>haoel@hotmail.com</font></a>） <br>RelatedEntries： <br>文件操作 - 10 28, 2003 <br>OSW:12.CVS设置与应用 - 10 28, 2003 <br>MySQL AB Acquires Alzato - 10 24, 2003 <br>cvs - 10 24, 2003 <br>使用CVS进行版本管理 - 10 23, 2003</p>
<img src ="http://www.cppblog.com/true/aggbug/71749.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2009-01-11 20:21 <a href="http://www.cppblog.com/true/archive/2009/01/11/71749.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>std::queue的front的问题</title><link>http://www.cppblog.com/true/archive/2008/06/10/52731.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Tue, 10 Jun 2008 03:39:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2008/06/10/52731.html</guid><wfw:comment>http://www.cppblog.com/true/comments/52731.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2008/06/10/52731.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/52731.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/52731.html</trackback:ping><description><![CDATA[<p>&nbsp;</p>
msdn上的解析：<br>
<pre><strong>value_type&amp; front( );</strong>&nbsp;
<strong>const value_type&amp; front( ) const;</strong></pre>
<p>Returns a reference to the first element at the front of the queue.<br><br>请看下面示例代码<br>queue&lt;int&gt; intqueue;<br>intqueue.push(1);<br>intqueue.push(2);<br>int head = intqueue.front();//int&amp;可以隐式转换为int?<br>intqueue.pop();//将对头元素弹出队列<br>cout &lt;&lt; head &lt;&lt; endl;//输出1，front应该返回的是"引用",但pop之后，为什么head的输出还有效(引用还有效？)？<br><br></p>
<img src ="http://www.cppblog.com/true/aggbug/52731.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2008-06-10 11:39 <a href="http://www.cppblog.com/true/archive/2008/06/10/52731.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>epoll()实现分析【ZT】</title><link>http://www.cppblog.com/true/archive/2008/06/01/51848.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Sun, 01 Jun 2008 14:55:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2008/06/01/51848.html</guid><wfw:comment>http://www.cppblog.com/true/comments/51848.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2008/06/01/51848.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/51848.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/51848.html</trackback:ping><description><![CDATA[<div class=tit>epoll()实现分析<br><br><a href="http://hi.baidu.com/rwen2012/blog/item/0f2f8c13eb7f3621dd5401a8.html">http://hi.baidu.com/rwen2012/blog/item/0f2f8c13eb7f3621dd5401a8.html</a></div>
<div class=date>2007-07-04 17:50</div>
<table style="TABLE-LAYOUT: fixed">
    <tbody>
        <tr>
            <td>
            <div class=cnt id=blog_text>&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; <br>/*<br>* This structure is stored inside the "private_data" member of the file<br>* structure and rapresent the main data sructure for the eventpoll<br>* interface.<br>*/<br>struct eventpoll {<br>&nbsp;&nbsp; /* Protect the this structure access */<br>&nbsp;&nbsp; rwlock_t lock;<br></div>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/true/aggbug/51848.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2008-06-01 22:55 <a href="http://www.cppblog.com/true/archive/2008/06/01/51848.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在Linux上开发网络服务器的一些相关细节:poll与epoll[转]</title><link>http://www.cppblog.com/true/archive/2008/06/01/51843.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Sun, 01 Jun 2008 14:15:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2008/06/01/51843.html</guid><wfw:comment>http://www.cppblog.com/true/comments/51843.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2008/06/01/51843.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/51843.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/51843.html</trackback:ping><description><![CDATA[<div class=tit><a href="http://hi.baidu.com/xproduct/blog/calendar/200706">http://hi.baidu.com/xproduct/blog/calendar/200706</a><br></div>
<div class=date>2007-06-30 23:58</div>
<table style="TABLE-LAYOUT: fixed">
    <tbody>
        <tr>
            <td>
            <div class=cnt>随着2.6内核对epoll的完全支持，网络上很多的文章和示例代码都提供了这样一个信息：使用epoll代替传统的poll能给网络服务应用带来性能上 的提升。但大多文章里关于性能提升的原因解释的较少，这里我将试分析一下内核（2.6.21.1）代码中poll与epoll的工作原理，然后再通过一些 测试数据来对比具体效果。<br><br>&nbsp;&nbsp; POLL：<br><br>&nbsp;&nbsp; 先说poll，poll或select为大部分Unix/Linux程序员所熟悉，这俩个东西原理类似，性能上也不存在明显差异，但select对所监控的文件描述符数量有限制，所以这里选用poll做说明。<br><br>&nbsp;&nbsp; poll是一个系统调用，其内核入口函数为sys_poll，sys_poll几乎不做任何处理直接调用do_sys_poll，do_sys_poll的执行过程可以分为三个部分：<br><br>&nbsp;&nbsp; 1，将用户传入的pollfd数组拷贝到内核空间，因为拷贝操作和数组长度相关，时间上这是一个O（n）操作，这一步的代码在do_sys_poll中包括从函数开始到调用do_poll前的部分。<br><br>&nbsp;&nbsp; 2，查询每个文件描述符对应设备的状态，如果该设备尚未就绪，则在该设备的等待队列中加入一项并继续查询下一设备的状态。查询完所有设备后如果没有一个设 备就绪，这时则需要挂起当前进程等待，直到设备就绪或者超时，挂起操作是通过调用schedule_timeout执行的。设备就绪后进程被通知继续运 行，这时再次遍历所有设备，以查找就绪设备。这一步因为两次遍历所有设备，时间复杂度也是O（n），这里面不包括等待时间。相关代码在do_poll函数 中。<br><br>&nbsp;&nbsp; 3，将获得的数据传送到用户空间并执行释放内存和剥离等待队列等善后工作，向用户空间拷贝数据与剥离等待队列等操作的的时间复杂度同样是O（n），具体代码包括do_sys_poll函数中调用do_poll后到结束的部分。<br><br>&nbsp;&nbsp; EPOLL：<br><br>&nbsp;&nbsp; 接下来分析epoll，与poll/select不同，epoll不再是一个单独的系统调用，而是由epoll_create/epoll_ctl/epoll_wait三个系统调用组成，后面将会看到这样做的好处。<br><br>&nbsp;&nbsp; 先来看sys_epoll_create(epoll_create对应的内核函数），这个函数主要是做一些准备工作，比如创建数据结构，初始化数据并最终返回一个文件描述符（表示新创建的虚拟epoll文件），这个操作可以认为是一个固定时间的操作。<br><br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; epoll是做为一个虚拟文件系统来实现的，这样做至少有以下两个好处：<br><br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 1，可以在内核里维护一些信息，这些信息在多次epoll_wait间是保持的，比如所有受监控的文件描述符。<br><br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 2， epoll本身也可以被poll/epoll;<br><br>&nbsp;&nbsp; 具体epoll的虚拟文件系统的实现和性能分析无关，不再赘述。<br><br>&nbsp;&nbsp; 在sys_epoll_create中还能看到一个细节，就是epoll_create的参数size在现阶段是没有意义的，只要大于零就行。<br><br>&nbsp;&nbsp; 接着是sys_epoll_ctl(epoll_ctl对应的内核函数），需要明确的是每次调用sys_epoll_ctl只处理一个文件描述符，这里主 要描述当op为EPOLL_CTL_ADD时的执行过程，sys_epoll_ctl做一些安全性检查后进入ep_insert，ep_insert里将 ep_poll_callback做为回掉函数加入设备的等待队列（假定这时设备尚未就绪），由于每次poll_ctl只操作一个文件描述符，因此也可以 认为这是一个O(1)操作<br><br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; ep_poll_callback函数很关键，它在所等待的设备就绪后被系统回掉，执行两个操作：<br><br>&nbsp;&nbsp; 1，将就绪设备加入就绪队列，这一步避免了像poll那样在设备就绪后再次轮询所有设备找就绪者，降低了时间复杂度，由O（n）到O（1）;<br><br>&nbsp;&nbsp; 2，唤醒虚拟的epoll文件;<br><br>&nbsp;&nbsp; 最后是sys_epoll_wait，这里实际执行操作的是ep_poll函数。该函数等待将进程自身插入虚拟epoll文件的等待队列，直到被唤醒（见 上面ep_poll_callback函数描述），最后执行ep_events_transfer将结果拷贝到用户空间。由于只拷贝就绪设备信息，所以这 里的拷贝是一个O(1）操作。<br><br>&nbsp;&nbsp; 还有一个让人关心的问题就是epoll对EPOLLET的处理，即边沿触发的处理，粗略看代码就是把一部分水平触发模式下内核做的工作交给用户来处理，直觉上不会对性能有太大影响，感兴趣的朋友欢迎讨论。<br><br>&nbsp;&nbsp; POLL/EPOLL对比：<br><br>&nbsp;&nbsp; 表面上poll的过程可以看作是由一次epoll_create/若干次epoll_ctl/一次epoll_wait/一次close等系统调用构成， 实际上epoll将poll分成若干部分实现的原因正是因为服务器软件中使用poll的特点（比如Web服务器）：<br><br>&nbsp;&nbsp; 1，需要同时poll大量文件描述符;<br><br>&nbsp;&nbsp; 2，每次poll完成后就绪的文件描述符只占所有被poll的描述符的很少一部分。<br><br>&nbsp;&nbsp; 3，前后多次poll调用对文件描述符数组（ufds）的修改只是很小; <br><br>&nbsp;&nbsp; 传统的poll函数相当于每次调用都重起炉灶，从用户空间完整读入ufds，完成后再次完全拷贝到用户空间，另外每次poll都需要对所有设备做至少做一次加入和删除等待队列操作，这些都是低效的原因。<br><br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; epoll将以上情况都细化考虑，不需要每次都完整读入输出ufds，只需使用epoll_ctl调整其中一小部分，不需要每次 epoll_wait都执行一次加入删除等待队列操作，另外改进后的机制使的不必在某个设备就绪后搜索整个设备数组进行查找，这些都能提高效率。另外最明 显的一点，从用户的使用来说，使用epoll不必每次都轮询所有返回结果已找出其中的就绪部分，O（n）变O（1），对性能也提高不少。<br><br>&nbsp;&nbsp; 此外这里还发现一点，是不是将epoll_ctl改成一次可以处理多个fd（像semctl那样）会提高些许性能呢？特别是在假设系统调用比较耗时的基础上。不过关于系统调用的耗时问题还会在以后分析。<br><br>&nbsp;&nbsp; POLL/EPOLL测试数据对比：<br><br>&nbsp;&nbsp; 测试的环境：我写了三段代码来分别模拟服务器，活动的客户端，僵死的客户端，服务器运行于一个自编译的标准2.6.11内核系统上，硬件为 PIII933，两个客户端各自运行在另外的PC上，这两台PC比服务器的硬件性能要好，主要是保证能轻易让服务器满载，三台机器间使用一个100M交换 机连接。<br><br>&nbsp;&nbsp; 服务器接受并poll所有连接，如果有request到达则回复一个response，然后继续poll。<br><br>&nbsp;&nbsp; 活动的客户端（Active Client）模拟若干并发的活动连接，这些连接不间断的发送请求接受回复。<br><br>&nbsp;&nbsp; 僵死的客户端（zombie）模拟一些只连接但不发送请求的客户端，其目的只是占用服务器的poll描述符资源。<br><br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 测试过程：保持10个并发活动连接，不断的调整僵并发连接数，记录在不同比例下使用poll与epoll的性能差别。僵死并发连接数根据比例分别是：0，10，20，40，80，160，320，640，1280，2560，5120，10240。<br><br>&nbsp;&nbsp; 下图中横轴表示僵死并发连接与活动并发连接之比，纵轴表示完成40000次请求回复所花费的时间，以秒为单位。红色线条表示poll数据，绿色表示 epoll数据。可以看出，poll在所监控的文件描述符数量增加时，其耗时呈线性增长，而epoll则维持了一个平稳的状态，几乎不受描述符个数影响。<br><br>&nbsp;&nbsp; 在监控的所有客户端都是活动时，poll的效率会略高于epoll（主要在原点附近，即僵死并发连接为0时，图上不易看出来），究竟epoll实现比poll复杂，监控少量描述符并非它的长</div>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/true/aggbug/51843.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2008-06-01 22:15 <a href="http://www.cppblog.com/true/archive/2008/06/01/51843.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CMake-开源跨平台的编译系统</title><link>http://www.cppblog.com/true/archive/2008/05/24/50961.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Sat, 24 May 2008 08:39:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2008/05/24/50961.html</guid><wfw:comment>http://www.cppblog.com/true/comments/50961.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2008/05/24/50961.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/50961.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/50961.html</trackback:ping><description><![CDATA[<blockquote>
<p>官网：<a title=http://www.cmake.org/HTML/index.html href="http://www.cmake.org/HTML/index.html">http://www.cmake.org/HTML/index.html</a></p>
<p>本文主要尝试用CMake编译源代码包中的Demo</p>
<p>1.下载最新版2.6.0，我在windows下载的是源代码包<a href="http://www.cmake.org/files/v2.6/cmake-2.6.0.zip">cmake-2.6.0.zip</a>和预编译好的二进制版本<a href="http://www.cmake.org/files/v2.6/cmake-2.6.0-win32-x86.zip">cmake-2.6.0-win32-x86.zip</a>.并分别加压到dir目录（目录随便即可），得到dir/cmake-2.6.0,dir/cmake-2.6.0-win32-x86两个文件夹。</p>
<p>2.将dir/cmake-2.6.0-win32-x86/bin加到path环境变量中。</p>
<p>3.dos窗口下进入到dir/cmake-2.6.0下面。</p>
<p>4.cd Example</p>
<p>5.mkdir build</p>
<p>6.cd build</p>
<p>7.cmake ..&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (两个点表示在上一级目录)</p>
<p>下面会自动产生了sln和vcproj文件，VS2005打开后，可以直接编译运行</p>
</blockquote>
<img src ="http://www.cppblog.com/true/aggbug/50961.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2008-05-24 16:39 <a href="http://www.cppblog.com/true/archive/2008/05/24/50961.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux thread在线参考</title><link>http://www.cppblog.com/true/archive/2008/05/17/50199.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Sat, 17 May 2008 14:12:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2008/05/17/50199.html</guid><wfw:comment>http://www.cppblog.com/true/comments/50199.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2008/05/17/50199.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/50199.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/50199.html</trackback:ping><description><![CDATA[<a href="http://www.sbin.org/doc/glibc/libc_34.html">http://www.sbin.org/doc/glibc/libc_34.html</a>
<img src ="http://www.cppblog.com/true/aggbug/50199.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2008-05-17 22:12 <a href="http://www.cppblog.com/true/archive/2008/05/17/50199.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>为什么网络传输ascii码不需要进行字节序转换？</title><link>http://www.cppblog.com/true/archive/2008/05/14/49789.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Wed, 14 May 2008 01:48:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2008/05/14/49789.html</guid><wfw:comment>http://www.cppblog.com/true/comments/49789.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2008/05/14/49789.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/49789.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/49789.html</trackback:ping><description><![CDATA[ascii是8byte编码，所以没有这个问题
<img src ="http://www.cppblog.com/true/aggbug/49789.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2008-05-14 09:48 <a href="http://www.cppblog.com/true/archive/2008/05/14/49789.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>内存越界和泄露调试工具[转]</title><link>http://www.cppblog.com/true/archive/2008/03/21/45030.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Fri, 21 Mar 2008 02:07:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2008/03/21/45030.html</guid><wfw:comment>http://www.cppblog.com/true/comments/45030.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2008/03/21/45030.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/45030.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/45030.html</trackback:ping><description><![CDATA[<div class=postcontent twffan="done"><span class=a14c id=zoom twffan="done">
<p style="TEXT-INDENT: 2em"><span class=b twffan="done">作者：sixth</span></p>
<p style="TEXT-INDENT: 2em">用C/C++开发其中最令人头疼的一个问题就是内存管理，有时候为了查找一个内存泄漏或者一个内存访问越界，需要要花上好几天时间，如果有一款工具能够帮助我们做这件事情就好了，valgrind正好就是这样的一款工具。 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">Valgrind是一款基于模拟linux下的程序调试器和剖析器的软件套件，可以运行于x86, amd64和ppc32架构上。valgrind包含一个核心，它提供一个虚拟的CPU运行程序，还有一系列的工具，它们完成调试，剖析和一些类似的任务。valgrind是高度模块化的，所以开发人员或者用户可以给它添加新的工具而不会损坏己有的结构。 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">valgrind的官方网址是：http://valgrind.org </p>
<p style="TEXT-INDENT: 2em">你可以在它的网站上下载到最新的valgrind，它是开放源码和免费的。 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em"><strong>一、介绍</strong> </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">valgrind包含几个标准的工具，它们是： </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">1、memcheck </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">memcheck探测程序中内存管理存在的问题。它检查所有对内存的读/写操作，并截取所有的malloc/new/free/delete调用。因此memcheck工具能够探测到以下问题： </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">1）使用未初始化的内存 </p>
<p style="TEXT-INDENT: 2em">2）读/写已经被释放的内存 </p>
<p style="TEXT-INDENT: 2em">3）读/写内存越界 </p>
<p style="TEXT-INDENT: 2em">4）读/写不恰当的内存栈空间 </p>
<p style="TEXT-INDENT: 2em">5）内存泄漏 </p>
<p style="TEXT-INDENT: 2em">6）使用malloc/new/new[]和free/delete/delete[]不匹配。 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">2、cachegrind </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">cachegrind是一个cache剖析器。它模拟执行CPU中的L1, D1和L2 cache，因此它能很精确的指出代码中的cache未命中。如果你需要，它可以打印出cache未命中的次数，内存引用和发生cache未命中的每一行代码，每一个函数，每一个模块和整个程序的摘要。如果你要求更细致的信息，它可以打印出每一行机器码的未命中次数。在x86和amd64上， cachegrind通过CPUID自动探测机器的cache配置，所以在多数情况下它不再需要更多的配置信息了。 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">3、helgrind </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">helgrind查找多线程程序中的竞争数据。helgrind查找内存地址，那些被多于一条线程访问的内存地址，但是没有使用一致的锁就会被查出。这表示这些地址在多线程间访问的时候没有进行同步，很可能会引起很难查找的时序问题。 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em"><strong>二、valgrind对你的程序都做了些什么</strong> </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">valgrind被设计成非侵入式的，它直接工作于可执行文件上，因此在检查前不需要重新编译、连接和修改你的程序。要检查一个程序很简单，只需要执行下面的命令就可以了 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">valgrind --tool=tool_name program_name </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">比如我们要对ls -l命令做内存检查，只需要执行下面的命令就可以了 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">valgrind --tool=memcheck ls -l </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">不管是使用哪个工具，valgrind在开始之前总会先取得对你的程序的控制权，从可执行关联库里读取调试信息。然后在valgrind核心提供的虚拟CPU上运行程序，valgrind会根据选择的工具来处理代码，该工具会向代码中加入检测代码，并把这些代码作为最终代码返回给valgrind核心，最后valgrind核心运行这些代码。 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">如果要检查内存泄漏，只需要增加--leak-check=yes就可以了，命令如下 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">valgrind --tool=memcheck --leak-check=yes ls -l </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">不同工具间加入的代码变化非常的大。在每个作用域的末尾，memcheck加入代码检查每一片内存的访问和进行值计算，代码大小至少增加12倍，运行速度要比平时慢25到50倍。 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">valgrind模拟程序中的每一条指令执行，因此，检查工具和剖析工具不仅仅是对你的应用程序，还有对共享库，GNU C库，X的客户端库都起作用。 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em"><strong>三、现在开始</strong> </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">首先，在编译程序的时候打开调试模式（gcc编译器的-g选项）。如果没有调试信息，即使最好的valgrind工具也将中能够猜测特定的代码是属于哪一个函数。打开调试选项进行编译后再用valgrind检查，valgrind将会给你的个详细的报告，比如哪一行代码出现了内存泄漏。 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">当检查的是C++程序的时候，还应该考虑另一个选项 -fno-inline。它使得函数调用链很清晰，这样可以减少你在浏览大型C++程序时的混乱。比如在使用这个选项的时候，用memcheck检查 openoffice就很容易。当然，你可能不会做这项工作，但是使用这一选项使得valgrind生成更精确的错误报告和减少混乱。 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">一些编译优化选项(比如-O2或者更高的优化选项)，可能会使得memcheck提交错误的未初始化报告，因此，为了使得valgrind的报告更精确，在编译的时候最好不要使用优化选项。 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">如果程序是通过脚本启动的，可以修改脚本里启动程序的代码，或者使用--trace-children=yes选项来运行脚本。 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">下面是用memcheck检查ls -l命令的输出报告，在终端下执行下面的命令 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">valgrind --tool=memcheck ls -l </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">程序会打印出ls -l命令的结果，最后是valgrind的检查报告如下： </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">==4187== </p>
<p style="TEXT-INDENT: 2em">==4187== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 19 from 2) </p>
<p style="TEXT-INDENT: 2em">==4187== malloc/free: in use at exit: 15,154 bytes in 105 blocks. </p>
<p style="TEXT-INDENT: 2em">==4187== malloc/free: 310 allocs, 205 frees, 60,093 bytes allocated. </p>
<p style="TEXT-INDENT: 2em">==4187== For counts of detected errors, rerun with: -v </p>
<p style="TEXT-INDENT: 2em">==4187== searching for pointers to 105 not-freed blocks. </p>
<p style="TEXT-INDENT: 2em">==4187== checked 145,292 bytes. </p>
<p style="TEXT-INDENT: 2em">==4187== </p>
<p style="TEXT-INDENT: 2em">==4187== LEAK SUMMARY: </p>
<p style="TEXT-INDENT: 2em">==4187== definitely lost: 0 bytes in 0 blocks. </p>
<p style="TEXT-INDENT: 2em">==4187== possibly lost: 0 bytes in 0 blocks. </p>
<p style="TEXT-INDENT: 2em">==4187== still reachable: 15,154 bytes in 105 blocks. </p>
<p style="TEXT-INDENT: 2em">==4187== suppressed: 0 bytes in 0 blocks. </p>
<p style="TEXT-INDENT: 2em">==4187== Reachable blocks (those to which a pointer was found) are not shown. </p>
<p style="TEXT-INDENT: 2em">==4187== To see them, rerun with: --show-reachable=yes </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">这里的&#8220;4187&#8221;指的是执行ls -l的进程ID，这有利于区别不同进程的报告。memcheck会给出报告，分配置和释放了多少内存，有多少内存泄漏了，还有多少内存的访问是可达的，检查了多少字节的内存。 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">下面举两个用valgrind做内存检查的例子 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">例子一 (test.c)： </p>
<p style="TEXT-INDENT: 2em"></p>
<center><ccid_nobr></ccid_nobr>
<table cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
    <tbody>
        <tr>
            <td class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6>
            <pre><ccid_code>#include &lt;string.h&gt;<br><br>int main(int argc, char *argv[])<br>{<br>    char *ptr;<br><br>    ptr = (char*) malloc(10);<br>    strcpy(ptr, "01234567890");<br><br>    return 0;<br>}</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</center>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">编译程序 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">gcc -g -o test test.c </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">用valgrind执行命令 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">valgrind --tool=memcheck --leak-check=yes ./test </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">报告如下 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">==4270== Memcheck, a memory error detector. </p>
<p style="TEXT-INDENT: 2em">==4270== Copyright (C) 2002-2006, and GNU GPL'd, by Julian Seward et al. </p>
<p style="TEXT-INDENT: 2em">==4270== Using LibVEX rev 1606, a library for dynamic binary translation. </p>
<p style="TEXT-INDENT: 2em">==4270== Copyright (C) 2004-2006, and GNU GPL'd, by OpenWorks LLP. </p>
<p style="TEXT-INDENT: 2em">==4270== Using valgrind-3.2.0, a dynamic binary instrumentation framework. </p>
<p style="TEXT-INDENT: 2em">==4270== Copyright (C) 2000-2006, and GNU GPL'd, by Julian Seward et al. </p>
<p style="TEXT-INDENT: 2em">==4270== For more details, rerun with: -v </p>
<p style="TEXT-INDENT: 2em">==4270== </p>
<p style="TEXT-INDENT: 2em">==4270== Invalid write of size 1 </p>
<p style="TEXT-INDENT: 2em">==4270== at 0x4006190: strcpy (mc_replace_strmem.c:271) </p>
<p style="TEXT-INDENT: 2em">==4270== by 0x80483DB: main (test.c:8) </p>
<p style="TEXT-INDENT: 2em">==4270== Address 0x4023032 is 0 bytes after a block of size 10 alloc'd </p>
<p style="TEXT-INDENT: 2em">==4270== at 0x40044F6: malloc (vg_replace_malloc.c:149) </p>
<p style="TEXT-INDENT: 2em">==4270== by 0x80483C5: main (test.c:7) </p>
<p style="TEXT-INDENT: 2em">==4270== </p>
<p style="TEXT-INDENT: 2em">==4270== Invalid write of size 1 </p>
<p style="TEXT-INDENT: 2em">==4270== at 0x400619C: strcpy (mc_replace_strmem.c:271) </p>
<p style="TEXT-INDENT: 2em">==4270== by 0x80483DB: main (test.c:8) </p>
<p style="TEXT-INDENT: 2em">==4270== Address 0x4023033 is 1 bytes after a block of size 10 alloc'd </p>
<p style="TEXT-INDENT: 2em">==4270== at 0x40044F6: malloc (vg_replace_malloc.c:149) </p>
<p style="TEXT-INDENT: 2em">==4270== by 0x80483C5: main (test.c:7) </p>
<p style="TEXT-INDENT: 2em">==4270== </p>
<p style="TEXT-INDENT: 2em">==4270== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 12 from 1) </p>
<p style="TEXT-INDENT: 2em">==4270== malloc/free: in use at exit: 10 bytes in 1 blocks. </p>
<p style="TEXT-INDENT: 2em">==4270== malloc/free: 1 allocs, 0 frees, 10 bytes allocated. </p>
<p style="TEXT-INDENT: 2em">==4270== For counts of detected errors, rerun with: -v </p>
<p style="TEXT-INDENT: 2em">==4270== searching for pointers to 1 not-freed blocks. </p>
<p style="TEXT-INDENT: 2em">==4270== checked 51,496 bytes. </p>
<p style="TEXT-INDENT: 2em">==4270== </p>
<p style="TEXT-INDENT: 2em">==4270== </p>
<p style="TEXT-INDENT: 2em">==4270== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1 </p>
<p style="TEXT-INDENT: 2em">==4270== at 0x40044F6: malloc (vg_replace_malloc.c:149) </p>
<p style="TEXT-INDENT: 2em">==4270== by 0x80483C5: main (test.c:7) </p>
<p style="TEXT-INDENT: 2em">==4270== </p>
<p style="TEXT-INDENT: 2em">==4270== LEAK SUMMARY: </p>
<p style="TEXT-INDENT: 2em">==4270== definitely lost: 10 bytes in 1 blocks. </p>
<p style="TEXT-INDENT: 2em">==4270== possibly lost: 0 bytes in 0 blocks. </p>
<p style="TEXT-INDENT: 2em">==4270== still reachable: 0 bytes in 0 blocks. </p>
<p style="TEXT-INDENT: 2em">==4270== suppressed: 0 bytes in 0 blocks. </p>
<p style="TEXT-INDENT: 2em">==4270== Reachable blocks (those to which a pointer was found) are not shown. </p>
<p style="TEXT-INDENT: 2em">==4270== To see them, rerun with: --show-reachable=yes </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">从这份报告可以看出，进程号是4270，test.c的第8行写内存越界了，引起写内存越界的是strcpy函数， </p>
<p style="TEXT-INDENT: 2em">第7行泄漏了10个字节的内存，引起内存泄漏的是malloc函数。 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">例子二（test2.c) </p>
<p style="TEXT-INDENT: 2em"></p>
<center><ccid_nobr></ccid_nobr>
<table cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
    <tbody>
        <tr>
            <td class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6>
            <pre><ccid_code>#include &lt;stdio.h&gt;<br><br>int foo(int x)<br>{<br>    if (x &lt; 0) {<br>        printf("%d ", x);<br>    }<br><br>    return 0;<br>}<br><br>int main(int argc, char *argv[])<br>{<br>    int x;<br>   <br>    foo(x);<br><br>    return 0;<br>}</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</center>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">编译程序 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">gcc -g -o test2 test2.c </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">用valgrind做内存检查 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">valgrind --tool=memcheck ./test2 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">输出报告如下 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">==4285== Memcheck, a memory error detector. </p>
<p style="TEXT-INDENT: 2em">==4285== Copyright (C) 2002-2006, and GNU GPL'd, by Julian Seward et al. </p>
<p style="TEXT-INDENT: 2em">==4285== Using LibVEX rev 1606, a library for dynamic binary translation. </p>
<p style="TEXT-INDENT: 2em">==4285== Copyright (C) 2004-2006, and GNU GPL'd, by OpenWorks LLP. </p>
<p style="TEXT-INDENT: 2em">==4285== Using valgrind-3.2.0, a dynamic binary instrumentation framework. </p>
<p style="TEXT-INDENT: 2em">==4285== Copyright (C) 2000-2006, and GNU GPL'd, by Julian Seward et al. </p>
<p style="TEXT-INDENT: 2em">==4285== For more details, rerun with: -v </p>
<p style="TEXT-INDENT: 2em">==4285== </p>
<p style="TEXT-INDENT: 2em">==4285== Conditional jump or move depends on uninitialised value(s) </p>
<p style="TEXT-INDENT: 2em">==4285== at 0x8048372: foo (test2.c:5) </p>
<p style="TEXT-INDENT: 2em">==4285== by 0x80483B4: main (test2.c:16) </p>
<p style="TEXT-INDENT: 2em">==4285==p p </p>
<p style="TEXT-INDENT: 2em">==4285== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 12 from 1) </p>
<p style="TEXT-INDENT: 2em">==4285== malloc/free: in use at exit: 0 bytes in 0 blocks. </p>
<p style="TEXT-INDENT: 2em">==4285== malloc/free: 0 allocs, 0 frees, 0 bytes allocated. </p>
<p style="TEXT-INDENT: 2em">==4285== For counts of detected errors, rerun with: -v </p>
<p style="TEXT-INDENT: 2em">==4285== All heap blocks were freed -- no leaks are possible. </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">从这份报告可以看出进程PID是4285，test2.c文件的第16行调用了foo函数，在test2.c文件的第5行foo函数使用了一个未初始化的变量。 </p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">valgrind还有很多使用选项，具体可以查看valgrind的man手册页和valgrind官方网站的在线文档。</p>
<br>
<p style="TEXT-INDENT: 2em"><br></p>
<br>
<p style="TEXT-INDENT: 2em"><br></p>
<p style="TEXT-INDENT: 2em">Windows用户不必沮丧，虽然在Windows上没有Valgrind可用，但是你可以试一试IBM的<a href="http://www-306.ibm.com/software/awdtools/purify/"><font color=#8d8c8c>Purify</font></a>，它在功能上和Valgrind相似。<br></p>
</span></div>
<img src ="http://www.cppblog.com/true/aggbug/45030.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2008-03-21 10:07 <a href="http://www.cppblog.com/true/archive/2008/03/21/45030.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux Deamon编程方法[又是转的：（]</title><link>http://www.cppblog.com/true/archive/2007/11/26/37299.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Mon, 26 Nov 2007 02:22:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2007/11/26/37299.html</guid><wfw:comment>http://www.cppblog.com/true/comments/37299.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2007/11/26/37299.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/37299.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/37299.html</trackback:ping><description><![CDATA[<div class=postTitle twffan="done">&nbsp;</div>
守护进程（Daemon）是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是一种很有用的进程。 Linux的大多数服务器就是用守护进程实现的。比如，Internet服务器inetd，Web服务器httpd等。同时，守护进程完成许多系统任务。比如，作业规划进程crond，打印进程lpd等。<br>守护进程的编程本身并不复杂，复杂的是各种版本的Unix的实现机制不尽相同，造成不同 Unix环境下守护进程的编程规则并不一致。需要注意，照搬某些书上的规则（特别是BSD4.3和低版本的System V）到Linux会出现错误的。下面将给出Linux下守护进程的编程要点和详细实例。<br>一． 守护进程及其特性<br>守护进程最重要的特性是后台运行。在这一点上DOS下的常驻内存程序TSR与之相似。其次，守护进程必须与其运行前的环境隔离开来。这些环境包括未关闭的文件描述符，控制终端，会话和进程组，工作目录以及文件创建掩模等。这些环境通常是守护进程从执行它的父进程（特别是shell）中继承下来的。最后，守护进程的启动方式有其特殊之处。它可以在Linux系统启动时从启动脚本/etc/rc.d中启动，可以由作业规划进程crond启动，还可以由用户终端（通常是 shell）执行。<br>总之，除开这些特殊性以外，守护进程与普通进程基本上没有什么区别。因此，编写守护进程实际上是把一个普通进程按照上述的守护进程的特性改造成为守护进程。如果对进程有比较深入的认识就更容易理解和编程了。<br>二． 守护进程的编程要点<br>前面讲过，不同Unix环境下守护进程的编程规则并不一致。所幸的是守护进程的编程原则其实都一样，区别在于具体的实现细节不同。这个原则就是要满足守护进程的特性。同时，Linux是基于Syetem V的SVR4并遵循Posix标准，实现起来与BSD4相比更方便。编程要点如下；<br>1. 在后台运行。<br>为避免挂起控制终端将Daemon放入后台执行。方法是在进程中调用fork使父进程终止，让Daemon在子进程中后台执行。<br>if(pid=fork())<br>exit(0);//是父进程，结束父进程，子进程继续<br>2. 脱离控制终端，登录会话和进程组<br>有必要先介绍一下Linux中的进程与控制终端，登录会话和进程组之间的关系：进程属于一个进程组，进程组号（GID）就是进程组长的进程号（PID）。登录会话可以包含多个进程组。这些进程组共享一个控制终端。这个控制终端通常是创建进程的登录终端。<br>控制终端，登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们，使之不受它们的影响。方法是在第1点的基础上，调用setsid()使进程成为会话组长：<br>setsid();<br>说明：当进程是会话组长时setsid()调用失败。但第一点已经保证进程不是会话组长。setsid()调用成功后，进程成为新的会话组长和新的进程组长，并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性，进程同时与控制终端脱离。<br>3. 禁止进程重新打开控制终端<br>现在，进程已经成为无终端的会话组长。但它可以重新申请打开一个控制终端。可以通过使进程不再成为会话组长来禁止进程重新打开控制终端：<br><br>if(pid=fork())<br>exit(0);//结束第一子进程，第二子进程继续（第二子进程不再是会话组长）<br>4. 关闭打开的文件描述符<br>进程从创建它的父进程那里继承了打开的文件描述符。如不关闭，将会浪费系统资源，造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们：<br>for(i=0;i 关闭打开的文件描述符close(i);&gt;<br>5. 改变当前工作目录<br>进程活动时，其工作目录所在的文件系统不能卸下。一般需要将工作目录改变到根目录。对于需要转储核心，写运行日志的进程将工作目录改变到特定目录如/tmpchdir("/")<br>6. 重设文件创建掩模<br>进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。为防止这一点，将文件创建掩模清除：umask(0);<br>7. 处理SIGCHLD信号<br>处理SIGCHLD信号并不是必须的。但对于某些进程，特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束，子进程将成为僵尸进程（zombie）从而占用系统资源。如果父进程等待子进程结束，将增加父进程的负担，影响服务器进程的并发性能。在Linux下可以简单地将 SIGCHLD信号的操作设为SIG_IGN。<br>signal(SIGCHLD,SIG_IGN);<br>这样，内核在子进程结束时不会产生僵尸进程。这一点与BSD4不同，BSD4下必须显式等待子进程结束才能释放僵尸进程。<br>三． 守护进程实例<br>守护进程实例包括两部分：主程序test.c和初始化程序init.c。主程序每隔一分钟向/tmp目录中的日志test.log报告运行状态。初始化程序中的init_daemon函数负责生成守护进程。读者可以利用init_daemon函数生成自己的守护进程。<br>1． init.c清单<br><br>#include &lt; unistd.h &gt;<br>#include &lt; signal.h &gt;<br>#include &lt; sys/param.h &gt;<br>#include &lt; sys/types.h &gt;<br>#include &lt; sys/stat.h &gt;<br>void init_daemon(void)<br>{<br>int pid;<br>int i;<br>if(pid=fork())<br>exit(0);//是父进程，结束父进程<br>else if(pid&lt; 0)<br>exit(1);//fork失败，退出<br>//是第一子进程，后台继续执行<br>setsid();//第一子进程成为新的会话组长和进程组长<br>//并与控制终端分离<br>if(pid=fork())<br>exit(0);//是第一子进程，结束第一子进程<br>else if(pid&lt; 0)<br>exit(1);//fork失败，退出<br>//是第二子进程，继续<br>//第二子进程不再是会话组长<br><br>for(i=0;i&lt; NOFILE;++i)//关闭打开的文件描述符<br>close(i);<br>chdir("/tmp");//改变工作目录到/tmp<br>umask(0);//重设文件创建掩模<br>return;<br>}<br>2． test.c清单<br>#include &lt; stdio.h &gt;<br>#include &lt; time.h &gt;<br><br>void init_daemon(void);//守护进程初始化函数<br><br>main()<br>{<br>FILE *fp;<br>time_t t;<br>init_daemon();//初始化为Daemon<br><br>while(1)//每隔一分钟向test.log报告运行状态<br>{<br>sleep(60);//睡眠一分钟<br>if((fp=fopen("test.log","a")) &gt;=0)<br>{<br>t=time(0);<br>fprintf(fp,"Im here at %sn",asctime(localtime(&amp;t)) );<br>fclose(fp);<br>}<br>}<br>}<br>以上程序在RedHat Linux6.0下编译通过。步骤如下：<br>编译：gcc -g -o test init.c test.c<br>执行：./test<br>查看进程：ps -ef<br>从输出可以发现test守护进程的各种特性满足上面的要求。 
<img src ="http://www.cppblog.com/true/aggbug/37299.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2007-11-26 10:22 <a href="http://www.cppblog.com/true/archive/2007/11/26/37299.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>awk用法小结【转】</title><link>http://www.cppblog.com/true/archive/2007/11/13/36495.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Tue, 13 Nov 2007 04:02:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2007/11/13/36495.html</guid><wfw:comment>http://www.cppblog.com/true/comments/36495.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2007/11/13/36495.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/36495.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/36495.html</trackback:ping><description><![CDATA[<h2>awk 用法小结</h2>
<div class=t_msgfont id=postmessage_8063 twffan="done">awk 用法：awk ' pattern {action} '<br><br>变量名 含义<br>ARGC 命令行变元个数<br>ARGV 命令行变元数组<br>FILENAME 当前输入文件名<br>FNR 当前文件中的记录号<br>FS 输入域分隔符，默认为一个空格<br>RS 输入记录分隔符<br>NF 当前记录里域个数<br>NR 到目前为止记录数<br>OFS 输出域分隔符<br>ORS 输出记录分隔符<br><br>1、awk '/101/' file 显示文件file中包含101的匹配行。<br>awk '/101/,/105/' file<br>awk '$1 == 5' file<br>awk '$1 == "CT"' file 注意必须带双引号<br>awk '$1 * $2 &gt;100 ' file<br>awk '$2 &gt;5 &amp;&amp; $2&lt;=15' file<br>2、awk '{print NR,NF,$1,$NF,}' file 显示文件file的当前记录号、域数和每一行的第一个和最后一个域。<br>awk '/101/ {print $1,$2 + 10}' file 显示文件file的匹配行的第一、二个域加10。<br>awk '/101/ {print $1$2}' file<br>awk '/101/ {print $1 $2}' file 显示文件file的匹配行的第一、二个域，但显示时域中间没有分隔符。<br>3、df | awk '$4&gt;1000000 ' 通过管道符获得输入，如：显示第4个域满足条件的行。<br>4、awk -F "|" '{print $1}' file 按照新的分隔符&#8220;|&#8221;进行操作。<br>awk 'BEGIN { FS="[: \t|]" }<br>{print $1,$2,$3}' file 通过设置输入分隔符（FS="[: \t|]"）修改输入分隔符。<br><br>Sep="|"<br>awk -F $Sep '{print $1}' file 按照环境变量Sep的值做为分隔符。 <br>awk -F '[ :\t|]' '{print $1}' file 按照正则表达式的值做为分隔符，这里代表空格、:、TAB、|同时做为分隔符。<br>awk -F '[][]' '{print $1}' file 按照正则表达式的值做为分隔符，这里代表[、]<br>5、awk -f awkfile file 通过文件awkfile的内容依次进行控制。<br>cat awkfile<br>/101/{print "\047 Hello! \047"} --遇到匹配行以后打印 ' Hello! '.\047代表单引号。<br>{print $1,$2} --因为没有模式控制，打印每一行的前两个域。<br>6、awk '$1 ~ /101/ {print $1}' file 显示文件中第一个域匹配101的行（记录）。<br>7、awk 'BEGIN { OFS="%"}<br>{print $1,$2}' file 通过设置输出分隔符（OFS="%"）修改输出格式。<br>8、awk 'BEGIN { max=100 ;print "max=" max} BEGIN 表示在处理任意行之前进行的操作。<br>{max=($1 &gt;max ?$1:max); print $1,"Now max is "max}' file 取得文件第一个域的最大值。<br>（表达式1?表达式2:表达式3 相当于：<br>if (表达式1)<br>表达式2<br>else<br>表达式3<br>awk '{print ($1&gt;4 ? "high "$1: "low "$1)}' file<br>9、awk '$1 * $2 &gt;100 {print $1}' file 显示文件中第一个域匹配101的行（记录）。<br>10、awk '{$1 == 'Chi' {$3 = 'China'; print}' file 找到匹配行后先将第3个域替换后再显示该行（记录）。<br>awk '{$7 %= 3; print $7}' file 将第7域被3除，并将余数赋给第7域再打印。<br>11、awk '/tom/ {wage=$2+$3; printf wage}' file 找到匹配行后为变量wage赋值并打印该变量。<br>12、awk '/tom/ {count++;}<br>END {print "tom was found "count" times"}' file END表示在所有输入行处理完后进行处理。<br>13、awk 'gsub(/\$/,"");gsub(/,/,""); cost+=$4;<br>END {print "The total is $" cost&gt;"filename"}' file gsub函数用空串替换$和,再将结果输出到filename中。<br>1 2 3 $1,200.00<br>1 2 3 $2,300.00<br>1 2 3 $4,000.00<br><br>awk '{gsub(/\$/,"");gsub(/,/,"");<br>if ($4&gt;1000&amp;&amp;$4&lt;2000) c1+=$4;<br>else if ($4&gt;2000&amp;&amp;$4&lt;3000) c2+=$4;<br>else if ($4&gt;3000&amp;&amp;$4&lt;4000) c3+=$4;<br>else c4+=$4; }<br>END {printf "c1=[%d];c2=[%d];c3=[%d];c4=[%d]\n",c1,c2,c3,c4}"' file<br>通过if和else if完成条件语句<br><br>awk '{gsub(/\$/,"");gsub(/,/,"");<br>if ($4&gt;3000&amp;&amp;$4&lt;4000) exit;<br>else c4+=$4; }<br>END {printf "c1=[%d];c2=[%d];c3=[%d];c4=[%d]\n",c1,c2,c3,c4}"' file<br>通过exit在某条件时退出，但是仍执行END操作。<br>awk '{gsub(/\$/,"");gsub(/,/,"");<br>if ($4&gt;3000) next;<br>else c4+=$4; }<br>END {printf "c4=[%d]\n",c4}"' file<br>通过next在某条件时跳过该行，对下一行执行操作。<br><br><br>14、awk '{ print FILENAME,$0 }' file1 file2 file3&gt;fileall 把file1、file2、file3的文件内容全部写到fileall中，格式为<br>打印文件并前置文件名。<br>15、awk ' $1!=previous { close(previous); previous=$1 } <br>{print substr($0,index($0," ") +1)&gt;$1}' fileall 把合并后的文件重新分拆为3个文件。并与原文件一致。<br>16、awk 'BEGIN {"date"|getline d; print d}' 通过管道把date的执行结果送给getline，并赋给变量d，然后打印。<br>17、awk 'BEGIN {system("echo \"Input your name:\\c\""); getline d;print "\nYour name is",d,"\b!\n"}'<br>通过getline命令交互输入name，并显示出来。<br>awk 'BEGIN {FS=":"; while(getline&lt; "/etc/passwd" &gt;0) { if($1~"050[0-9]_") print $1}}'<br>打印/etc/passwd文件中用户名包含050x_的用户名。<br><br>18、awk '{ i=1;while(i&lt;NF) {print NF,$i;i++}}' file 通过while语句实现循环。<br>awk '{ for(i=1;i&lt;NF;i++) {print NF,$i}}' file 通过for语句实现循环。 <br>type file|awk -F "/" '<br>{ for(i=1;i&lt;NF;i++)<br>{ if(i==NF-1) { printf "%s",$i }<br>else { printf "%s/",$i } }}' 显示一个文件的全路径。<br>用for和if显示日期<br>awk 'BEGIN {<br>for(j=1;j&lt;=12;j++)<br>{ flag=0;<br>printf "\n%d月份\n",j;<br>for(i=1;i&lt;=31;i++)<br>{<br>if (j==2&amp;&amp;i&gt;28) flag=1;<br>if ((j==4||j==6||j==9||j==11)&amp;&amp;i&gt;30) flag=1;<br>if (flag==0) {printf "%02d%02d ",j,i}<br>}<br>}<br>}'<br>19、在awk中调用系统变量必须用单引号，如果是双引号，则表示字符串<br>Flag=abcd<br>awk '{print '$Flag'}' 结果为abcd<br>awk '{print "$Flag"}' 结果为$Flag</div>
<img src ="http://www.cppblog.com/true/aggbug/36495.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2007-11-13 12:02 <a href="http://www.cppblog.com/true/archive/2007/11/13/36495.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>网络连接程序SSH为Linux助力[转]</title><link>http://www.cppblog.com/true/archive/2007/07/30/28998.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Mon, 30 Jul 2007 03:03:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2007/07/30/28998.html</guid><wfw:comment>http://www.cppblog.com/true/comments/28998.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2007/07/30/28998.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/28998.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/28998.html</trackback:ping><description><![CDATA[　　SSH (Secure Shell)是一套安全的网络连接程序,它可以让你通过网络连接至其他电脑，在其他电脑上执行程序,在电脑之间拷贝文件,它甚至可以提供给你更安全的X连接，而以上的这些连接，都是在编码的保护下完成的。也就是说安装了SSH后就可以将不安全的Telnet和FTP给关掉了。 <br>　　为什么要使用SSH <br>　　上面所说的各项功能,早期BSD所提供的r指令(rsh, rlogin, rcp)几乎都能完成,那为什么要用SSH呢?理由就在于r指令所提供的连接并没有经过编码加密,有心人只要使用合适的工具就能够截下你所输入的每一个字,包括密码。如果你利用X protocol在远端机器执行X程序，也可以截下你传输的资料,当然也包括密码。而SSH就针对了这些弱点做了弥补,对所传输的资料加以编码。 <br>　　SSH2与SSH1 <br>　　SSH2对SSH1的程序码做了大幅度的改写,根据SSH公司的说法, SSH2有98%的程序码和SSH1的不一样。除了SSH1所提供的RSA法之外，SSH2也提供了另外的公开金匙编码法以及金匙交换法,SSH2预设采用DSA编码以及Diffie-Hellman金匙交换法。此外,更提供了SFTP,使我们能在FTP方面也得到安全的保障。 <br>　　文件下载：<a href="ftp://ftp.ssh.com/pub/SSH/">ftp://ftp.SSH.com/pub/SSH/</a>；linuxnews.idv.tw/download/SSH-1.2.31.tar.gz 1MB for Linux Server；linuxnews.idv.tw/download/SSHWin-2.4.0-pl2.exe 5MB for Win32 Client <br>　　安装环境：Redhat 7.0 <br>　　1、下载完后将文件解压缩 <br>　　tar zxvf SSH-1.2.31.tar.gz <br>　　2、开始编译，安装 <br>　　cd SSH-1.2.31; <br>　　./configure; <br>　　make; <br>　　make install; <br>　　3、编辑/etc/rc.d/rc.local加入/usr/sbin/SSHd以便开机自动启动。 <br>　　4、完成 <br>　　如果你是2台Linux要相连就都要装这个程序，如果是Windows系统要连Linux的话就要安装for Winxx的程序。Winxx部分请自己试试。 <br>　　Linux的使用方法 <br>　　/usr/bin/SSH -l username 187.136.5.1 <br>　　然后输入密码，连进去后是一般的文字界面，就可以开始用了。 <br>　　另外，SSH可以直接使用root登入。 <br>　　注：如果你要对连接进来的IP做限制的话可以编辑/etc/hosts.deny和/etc/hosts.allow <br>　　示例如下： <br>　　/etc/hosts.deny： <br>　　ALL:ALL <br>　　#禁止所有IP使用所有的服务 <br>　　/etc/hosts.allow： <br>　　SSHd:111.222.333.444 <br>　　#开放111.222.333.444使用SSH连接 <br>　　利用SSH来ftp <br>　　1、Linux对Linux传文件： <br>　　上传：scp wrong.php <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#98;&#104;&#97;&#64;&#49;&#56;&#55;&#46;&#49;&#51;&#54;&#46;&#53;&#46;&#49;">bha@187.136.5.1</a>: <br>　　这时会问你密码，输入密码吧。 <br>　&nbsp;说明： <br>　　scp是指令 <br>　　wrong.php是本地端的文件名 <br>　　<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#98;&#104;&#97;&#64;&#49;&#56;&#55;&#46;&#49;&#51;&#54;&#46;&#53;&#46;&#49;">bha@187.136.5.1</a>是远端的用户(user name)和IP <br>　　最后记住那个冒号一定要加，那是远端的home directory。 <br>　下传：scp <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#98;&#104;&#97;&#64;&#49;&#56;&#55;&#46;&#49;&#51;&#54;&#46;&#53;&#46;&#49;&#58;&#119;&#114;&#111;&#110;&#103;&#46;&#112;&#104;&#112;">bha@187.136.5.1:wrong.php</a> . <br>　　说明： <br>　　用scp将<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#98;&#104;&#97;&#64;&#49;&#56;&#55;&#46;&#49;&#51;&#54;&#46;&#53;&#46;&#49;">bha@187.136.5.1</a>目录的wrong.php拷贝到目前的目录(就是那个.) <br>　　2、Win对Linux传文件： <br>　　ls：就是dir <br>　　et：下传文件 <br>　　put：上传文件 <br>　　exit：退出ftp程序 <br>　　指令：psftp-x86 187.136.5.1 <br>　　这时会问名字密码和要不要产生加密键值，然后再用put和get来上下传文件
<img src ="http://www.cppblog.com/true/aggbug/28998.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2007-07-30 11:03 <a href="http://www.cppblog.com/true/archive/2007/07/30/28998.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>系统时间C、C＋＋</title><link>http://www.cppblog.com/true/archive/2007/04/12/21709.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Thu, 12 Apr 2007 04:04:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2007/04/12/21709.html</guid><wfw:comment>http://www.cppblog.com/true/comments/21709.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2007/04/12/21709.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/21709.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/21709.html</trackback:ping><description><![CDATA[/C++中的日期和时间<!-- begin content -->
<div class="node " twffan="done"><span class=submitted twffan="done"><font color=#999999 size=1>由 zhanjun 在 周五, 2006-03-24 08:11 提交</font></span>
<div class=content twffan="done">
<p>C/C++中的日期和时间<br>作者：日期和时间 出处：日期和时间 更新时间： 2005年09月15日<br>摘要：<br>本文从介绍基础概念入手，探讨了在C/C++中对日期和时间操作所用到的数据结构和函数，并对计时、时间的获取、时间的计算和显示格式等方面进行了阐述。本文还通过大量的实例向你展示了time.h头文件中声明的各种函数和数据结构的详细使用方法。</p>
<p>关键字：UTC（世界标准时间），Calendar Time（日历时间），epoch（时间点），clock tick（时钟计时单元）</p>
<p>1．概念<br>在C/C++中，对字符串的操作有很多值得注意的问题，同样，C/C++对时间的操作也有许多值得大家注意的地方。最近，在技术群中有很多网友也多次问到过C++语言中对时间的操作、获取和显示等等的问题。下面，在这篇文章中，笔者将主要介绍在C/C++中时间和日期的使用方法.</p>
<p>通过学习许多C/C++库，你可以有很多操作、使用时间的方法。但在这之前你需要了解一些&#8220;时间&#8221;和&#8220;日期&#8221;的概念，主要有以下几个：</p>
<p>Coordinated Universal Time（UTC）：协调世界时，又称为世界标准时间，也就是大家所熟知的格林威治标准时间（Greenwich Mean Time，GMT）。比如，中国内地的时间与UTC的时差为+8，也就是UTC+8。美国是UTC-5。</p>
<p>Calendar Time：日历时间，是用&#8220;从一个标准时间点到此时的时间经过的秒数&#8221;来表示的时间。这个标准时间点对不同的编译器来说会有所不同，但对一个编译系统来说，这个标准时间点是不变的，该编译系统中的时间对应的日历时间都通过该标准时间点来衡量，所以可以说日历时间是&#8220;相对时间&#8221;，但是无论你在哪一个时区，在同一时刻对同一个标准时间点来说，日历时间都是一样的。</p>
<p>epoch：时间点。时间点在标准C/C++中是一个整数，它用此时的时间和标准时间点相差的秒数（即日历时间）来表示。</p>
<p>clock tick：时钟计时单元（而不把它叫做时钟滴答次数），一个时钟计时单元的时间长短是由CPU控制的。一个clock tick不是CPU的一个时钟周期，而是C/C++的一个基本计时单位。</p>
<p>我们可以使用ANSI标准库中的time.h头文件。这个头文件中定义的时间和日期所使用的方法，无论是在结构定义，还是命名，都具有明显的C语言风格。下面，我将说明在C/C++中怎样使用日期的时间功能。</p>
<p>2． 计时</p>
<p>C/C++中的计时函数是clock()，而与其相关的数据类型是clock_t。在MSDN中，查得对clock函数定义如下：</p>
<p>clock_t clock( void );</p>
<p>这个函数返回从&#8220;开启这个程序进程&#8221;到&#8220;程序中调用clock()函数&#8221;时之间的CPU时钟计时单元（clock tick）数，在MSDN中称之为挂钟时间（wal-clock）。其中clock_t是用来保存时间的数据类型，在time.h文件中，我们可以找到对它的定义：</p>
<p>#ifndef _CLOCK_T_DEFINED<br>typedef long clock_t;<br>#define _CLOCK_T_DEFINED<br>#endif</p>
<p>很明显，clock_t是一个长整形数。在time.h文件中，还定义了一个常量CLOCKS_PER_SEC，它用来表示一秒钟会有多少个时钟计时单元，其定义如下：</p>
<p>#define CLOCKS_PER_SEC ((clock_t)1000)</p>
<p>可以看到每过千分之一秒（1毫秒），调用clock（）函数返回的值就加1。下面举个例子，你可以使用公式clock()/CLOCKS_PER_SEC来计算一个进程自身的运行时间：</p>
<p>void elapsed_time()<br>{<br>printf("Elapsed time:%u secs.\n",clock()/CLOCKS_PER_SEC);<br>}</p>
<p>当然，你也可以用clock函数来计算你的机器运行一个循环或者处理其它事件到底花了多少时间：</p>
<p>#include &#8220;stdio.h&#8221;<br>#include &#8220;stdlib.h&#8221;<br>#include &#8220;time.h&#8221;</p>
<p>int main( void )<br>{<br>long i = 10000000L;<br>clock_t start, finish;<br>double duration;<br>/* 测量一个事件持续的时间*/<br>printf( "Time to do %ld empty loops is ", i );<br>start = clock();<br>while( i-- )<br>finish = clock();<br>duration = (double)(finish - start) / CLOCKS_PER_SEC;<br>printf( "%f seconds\n", duration );<br>system("pause");<br>}</p>
<p>在笔者的机器上，运行结果如下：</p>
<p>Time to do 10000000 empty loops is 0.03000 seconds</p>
<p>上面我们看到时钟计时单元的长度为1毫秒，那么计时的精度也为1毫秒，那么我们可不可以通过改变CLOCKS_PER_SEC的定义，通过把它定义的大一些，从而使计时精度更高呢？通过尝试，你会发现这样是不行的。在标准C/C++中，最小的计时单位是一毫秒。</p>
<p>3．与日期和时间相关的数据结构</p>
<p>在标准C/C++中，我们可通过tm结构来获得日期和时间，tm结构在time.h中的定义如下：</p>
<p>#ifndef _TM_DEFINED<br>struct tm {<br>int tm_sec; /* 秒 &#8211; 取值区间为[0,59] */<br>int tm_min; /* 分 - 取值区间为[0,59] */<br>int tm_hour; /* 时 - 取值区间为[0,23] */<br>int tm_mday; /* 一个月中的日期 - 取值区间为[1,31] */<br>int tm_mon; /* 月份（从一月开始，0代表一月） - 取值区间为[0,11] */<br>int tm_year; /* 年份，其值等于实际年份减去1900 */<br>int tm_wday; /* 星期 &#8211; 取值区间为[0,6]，其中0代表星期天，1代表星期一，以此类推 */<br>int tm_yday; /* 从每年的1月1日开始的天数 &#8211; 取值区间为[0,365]，其中0代表1月1日，1代表1月2日，以此类推 */<br>int tm_isdst; /* 夏令时标识符，实行夏令时的时候，tm_isdst为正。不实行夏令时的进候，tm_isdst为0；不了解情况时，tm_isdst()为负。*/<br>};<br>#define _TM_DEFINED<br>#endif</p>
<p>ANSI C标准称使用tm结构的这种时间表示为分解时间(broken-down time)。</p>
<p>而日历时间（Calendar Time）是通过time_t数据类型来表示的，用time_t表示的时间（日历时间）是从一个时间点（例如：1970年1月1日0时0分0秒）到此时的秒数。在time.h中，我们也可以看到time_t是一个长整型数：</p>
<p>#ifndef _TIME_T_DEFINED<br>typedef long time_t; /* 时间值 */<br>#define _TIME_T_DEFINED /* 避免重复定义 time_t */<br>#endif</p>
<p>大家可能会产生疑问：既然time_t实际上是长整型，到未来的某一天，从一个时间点（一般是1970年1月1日0时0分0秒）到那时的秒数（即日历时间）超出了长整形所能表示的数的范围怎么办？对time_t数据类型的值来说，它所表示的时间不能晚于2038年1月18日19时14分07秒。为了能够表示更久远的时间，一些编译器厂商引入了64位甚至更长的整形数来保存日历时间。比如微软在Visual C++中采用了__time64_t数据类型来保存日历时间，并通过_time64()函数来获得日历时间（而不是通过使用32位字的time()函数），这样就可以通过该数据类型保存3001年1月1日0时0分0秒（不包括该时间点）之前的时间。</p>
<p>在time.h头文件中，我们还可以看到一些函数，它们都是以time_t为参数类型或返回值类型的函数：</p>
<p>double difftime(time_t time1, time_t time0);<br>time_t mktime(struct tm * timeptr);<br>time_t time(time_t * timer);<br>char * asctime(const struct tm * timeptr);<br>char * ctime(const time_t *timer);</p>
<p>此外，time.h还提供了两种不同的函数将日历时间（一个用time_t表示的整数）转换为我们平时看到的把年月日时分秒分开显示的时间格式tm：</p>
<p>struct tm * gmtime(const time_t *timer);<br>struct tm * localtime(const time_t * timer);</p>
<p>通过查阅MSDN，我们可以知道Microsoft C/C++ 7.0中时间点的值（time_t对象的值）是从1899年12月31日0时0分0秒到该时间点所经过的秒数，而其它各种版本的Microsoft C/C++和所有不同版本的Visual C++都是计算的从1970年1月1日0时0分0秒到该时间点所经过的秒数。</p>
<p>4．与日期和时间相关的函数及应用<br>在本节，我将向大家展示怎样利用time.h中声明的函数对时间进行操作。这些操作包括取当前时间、计算时间间隔、以不同的形式显示时间等内容。</p>
<p>4.1 获得日历时间</p>
<p>我们可以通过time()函数来获得日历时间（Calendar Time），其原型为：</p>
<p>time_t time(time_t * timer);</p>
<p>如果你已经声明了参数timer，你可以从参数timer返回现在的日历时间，同时也可以通过返回值返回现在的日历时间，即从一个时间点（例如：1970年1月1日0时0分0秒）到现在此时的秒数。如果参数为空（NUL），函数将只通过返回值返回现在的日历时间，比如下面这个例子用来显示当前的日历时间：</p>
<p>#include "time.h"<br>#include "stdio.h"<br>int main(void)<br>{<br>struct tm *ptr;<br>time_t lt;<br>lt =time(NUL);<br>printf("The Calendar Time now is %d\n",lt);<br>return 0;<br>}</p>
<p>运行的结果与当时的时间有关，我当时运行的结果是：</p>
<p>The Calendar Time now is 1122707619</p>
<p>其中1122707619就是我运行程序时的日历时间。即从1970年1月1日0时0分0秒到此时的秒数。</p>
<p>4.2 获得日期和时间</p>
<p>这里说的日期和时间就是我们平时所说的年、月、日、时、分、秒等信息。从第2节我们已经知道这些信息都保存在一个名为tm的结构体中，那么如何将一个日历时间保存为一个tm结构的对象呢？</p>
<p>其中可以使用的函数是gmtime()和localtime()，这两个函数的原型为：</p>
<p>struct tm * gmtime(const time_t *timer);<br>struct tm * localtime(const time_t * timer);</p>
<p>其中gmtime()函数是将日历时间转化为世界标准时间（即格林尼治时间），并返回一个tm结构体来保存这个时间，而localtime()函数是将日历时间转化为本地时间。比如现在用gmtime()函数获得的世界标准时间是2005年7月30日7点18分20秒，那么我用localtime()函数在中国地区获得的本地时间会比世界标准时间晚8个小时，即2005年7月30日15点18分20秒。下面是个例子：</p>
<p>#include "time.h"<br>#include "stdio.h"<br>int main(void)<br>{<br>struct tm *local;<br>time_t t;<br>t=time(NUL);<br>local=localtime(&amp;t);<br>printf("Local hour is: %d\n",local-&gt;tm_hour);<br>local=gmtime(&amp;t);<br>printf("UTC hour is: %d\n",local-&gt;tm_hour);<br>return 0;<br>}</p>
<p>运行结果是：</p>
<p>Local hour is: 15<br>UTC hour is: 7</p>
<p>4.3 固定的时间格式</p>
<p>我们可以通过asctime()函数和ctime()函数将时间以固定的格式显示出来，两者的返回值都是char*型的字符串。返回的时间格式为：</p>
<p>星期几 月份 日期 时:分:秒 年\n\0<br>例如：Wed Jan 02 02:03:55 1980\n\0</p>
<p>其中\n是一个换行符，\0是一个空字符，表示字符串结束。下面是两个函数的原型：</p>
<p>char * asctime(const struct tm * timeptr);<br>char * ctime(const time_t *timer);</p>
<p>其中asctime()函数是通过tm结构来生成具有固定格式的保存时间信息的字符串，而ctime()是通过日历时间来生成时间字符串。这样的话，asctime（）函数只是把tm结构对象中的各个域填到时间字符串的相应位置就行了，而ctime（）函数需要先参照本地的时间设置，把日历时间转化为本地时间，然后再生成格式化后的字符串。在下面，如果t是一个非空的time_t变量的话，那么：</p>
<p>printf(ctime(&amp;t));</p>
<p>等价于：</p>
<p>struct tm *ptr;<br>ptr=localtime(&amp;t);<br>printf(asctime(ptr));</p>
<p>那么，下面这个程序的两条printf语句输出的结果就是不同的了（除非你将本地时区设为世界标准时间所在的时区）：</p>
<p>#include "time.h"<br>#include "stdio.h"<br>int main(void)<br>{<br>struct tm *ptr;<br>time_t lt;<br>lt =time(NUL);<br>ptr=gmtime(&lt;);<br>printf(asctime(ptr));<br>printf(ctime(&lt;));<br>return 0;<br>}</p>
<p>运行结果：</p>
<p>Sat Jul 30 08:43:03 2005<br>Sat Jul 30 16:43:03 2005</p>
<p>4.4 自定义时间格式</p>
<p>我们可以使用strftime（）函数将时间格式化为我们想要的格式。它的原型如下：</p>
<p>size_t strftime(<br>char *strDest,<br>size_t maxsize,<br>const char *format,<br>const struct tm *timeptr<br>);</p>
<p>我们可以根据format指向字符串中格式命令把timeptr中保存的时间信息放在strDest指向的字符串中，最多向strDest中存放maxsize个字符。该函数返回向strDest指向的字符串中放置的字符数。</p>
<p>函数strftime()的操作有些类似于sprintf()：识别以百分号(%)开始的格式命令集合，格式化输出结果放在一个字符串中。格式化命令说明串strDest中各种日期和时间信息的确切表示方法。格式串中的其他字符原样放进串中。格式命令列在下面，它们是区分大小写的。</p>
<p>%a 星期几的简写<br>%A 星期几的全称<br>%b 月分的简写<br>%B 月份的全称<br>%c 标准的日期的时间串<br>%C 年份的后两位数字<br>%d 十进制表示的每月的第几天<br>%D 月/天/年<br>%e 在两字符域中，十进制表示的每月的第几天<br>%F 年-月-日<br>%g 年份的后两位数字，使用基于周的年<br>%G 年分，使用基于周的年<br>%h 简写的月份名<br>%H 24小时制的小时<br>%I 12小时制的小时<br>%j 十进制表示的每年的第几天<br>%m 十进制表示的月份<br>%M 十时制表示的分钟数<br>%n 新行符<br>%p 本地的AM或PM的等价显示<br>%r 12小时的时间<br>%R 显示小时和分钟：hh:mm<br>%S 十进制的秒数<br>%t 水平制表符<br>%T 显示时分秒：hh:mm:ss<br>%u 每周的第几天，星期一为第一天 （值从0到6，星期一为0）<br>%U 第年的第几周，把星期日做为第一天（值从0到53）<br>%V 每年的第几周，使用基于周的年<br>%w 十进制表示的星期几（值从0到6，星期天为0）<br>%W 每年的第几周，把星期一做为第一天（值从0到53）<br>%x 标准的日期串<br>%X 标准的时间串<br>%y 不带世纪的十进制年份（值从0到99）<br>%Y 带世纪部分的十进制年份<br>%z，%Z 时区名称，如果不能得到时区名称则返回空字符。<br>%% 百分号</p>
<p>如果想显示现在是几点了，并以12小时制显示，就象下面这段程序：</p>
<p>#include &#8220;time.h&#8221;<br>#include &#8220;stdio.h&#8221;<br>int main(void)<br>{<br>struct tm *ptr;<br>time_t lt;<br>char str[80];<br>lt=time(NUL);<br>ptr=localtime(&lt;);<br>strftime(str,100,"It is now %I %p",ptr);<br>printf(str);<br>return 0;<br>}</p>
<p>其运行结果为：<br>It is now 4PM</p>
<p>而下面的程序则显示当前的完整日期：</p>
<p>#include &lt;stdio.h&gt;<br>#include &lt;time.h&gt;</p>
<p>void main( void )<br>{<br>struct tm *newtime;<br>char tmpbuf[128];<br>time_t lt1;<br>time( &lt;1 );<br>newtime=localtime(&lt;1);<br>strftime( tmpbuf, 128, "Today is %A, day %d of %B in the year %Y.\n", newtime);<br>printf(tmpbuf);<br>}</p>
<p>运行结果：</p>
<p>Today is Saturday, day 30 of July in the year 2005.</p>
<p>4.5 计算持续时间的长度</p>
<p>有时候在实际应用中要计算一个事件持续的时间长度，比如计算打字速度。在第1节计时部分中，我已经用clock函数举了一个例子。Clock()函数可以精确到毫秒级。同时，我们也可以使用difftime()函数，但它只能精确到秒。该函数的定义如下：</p>
<p>double difftime(time_t time1, time_t time0);</p>
<p>虽然该函数返回的以秒计算的时间间隔是double类型的，但这并不说明该时间具有同double一样的精确度，这是由它的参数觉得的（time_t是以秒为单位计算的）。比如下面一段程序：</p>
<p>#include "time.h"<br>#include "stdio.h"<br>#include "stdlib.h"<br>int main(void)<br>{<br>time_t start,end;<br>start = time(NUL);<br>system("pause");<br>end = time(NUL);<br>printf("The pause used %f seconds.\n",difftime(end,start));//&lt;-<br>system("pause");<br>return 0;<br>}</p>
<p>运行结果为：<br>请按任意键继续. . .<br>The pause used 2.000000 seconds.<br>请按任意键继续. . .</p>
<p>可以想像，暂停的时间并不那么巧是整整2秒钟。其实，你将上面程序的带有&#8220;//&lt;-&#8221;注释的一行用下面的一行代码替换：</p>
<p>printf("The pause used %f seconds.\n",end-start);</p>
<p>其运行结果是一样的。</p>
<p>4.6 分解时间转化为日历时间</p>
<p>这里说的分解时间就是以年、月、日、时、分、秒等分量保存的时间结构，在C/C++中是tm结构。我们可以使用mktime（）函数将用tm结构表示的时间转化为日历时间。其函数原型如下：</p>
<p>time_t mktime(struct tm * timeptr);</p>
<p>其返回值就是转化后的日历时间。这样我们就可以先制定一个分解时间，然后对这个时间进行操作了，下面的例子可以计算出1997年7月1日是星期几：</p>
<p>#include "time.h"<br>#include "stdio.h"<br>#include "stdlib.h"<br>int main(void)<br>{<br>struct tm t;<br>time_t t_of_day;<br>t.tm_year=1997-1900;<br>t.tm_mon=6;<br>t.tm_mday=1;<br>t.tm_hour=0;<br>t.tm_min=0;<br>t.tm_sec=1;<br>t.tm_isdst=0;<br>t_of_day=mktime(&amp;t);<br>printf(ctime(&amp;t_of_day));<br>return 0;<br>}</p>
<p>运行结果：</p>
<p>Tue Jul 01 00:00:01 1997</p>
<p>现在注意了，有了mktime()函数，是不是我们可以操作现在之前的任何时间呢？你可以通过这种办法算出1945年8月15号是星期几吗？答案是否定的。因为这个时间在1970年1月1日之前，所以在大多数编译器中，这样的程序虽然可以编译通过，但运行时会异常终止。</p>
<p>5．总结</p>
<p>本文介绍了标准C/C++中的有关日期和时间的概念，并通过各种实例讲述了这些函数和数据结构的使用方法。笔者认为，和时间相关的一些概念是相当重要的，理解这些概念是理解各种时间格式的转换的基础，更是应用这些函数和数据结构的基础。<br><br><br>&nbsp;上面是转的，本人自己再加点，以备后用：<br><br>/*&nbsp;//时间格式化为2007.07.02 14：50<br>&nbsp;time_t tm_now ;<br>&nbsp;time(&amp;tm_now) ;<br>&nbsp;char szTime[64]="";<br>&nbsp;strftime(szTime,64,"%Y.%m.%d %H:%M",localtime(&amp;tm_now));<br>&nbsp;cout &lt;&lt; szTime &lt;&lt; endl;<br>&nbsp;*/</p>
</div>
</div>
<img src ="http://www.cppblog.com/true/aggbug/21709.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2007-04-12 12:04 <a href="http://www.cppblog.com/true/archive/2007/04/12/21709.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>通用makefile</title><link>http://www.cppblog.com/true/archive/2007/04/05/21301.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Thu, 05 Apr 2007 04:01:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2007/04/05/21301.html</guid><wfw:comment>http://www.cppblog.com/true/comments/21301.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2007/04/05/21301.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/21301.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/21301.html</trackback:ping><description><![CDATA[<div class=blogbody twffan="done">
<h2 class=title>通用Makefile及部分解释</h2>
<div class=posted twffan="done">作者 CB 12:13 | <img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height=11 alt=Permalink src="http://www.utblog.com/plog/templates/blog_1/myblueish/post.gif" width=10 twffan="done"> <a href="http://www.utblog.com/plog/1/article/341"><u><font color=#800080>静态链接网址</font></u></a> | <img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height=11 alt=Comments src="http://www.utblog.com/plog/templates/blog_1/myblueish/bubble.gif" width=11 twffan="done"> <a href="http://www.utblog.com/plog/1/article/341"><u><font color=#800080>最新回复 (1)</font></u></a> | <img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height=11 alt=Trackback src="http://www.utblog.com/plog/templates/blog_1/myblueish/trackback.gif" width=16 twffan="done"> <a href="http://www.utblog.com/plog/1/trackback/341"><u><font color=#0000ff>引用 (0)</font></u></a> | <a href="http://www.utblog.com/plog/1/category/34"><u><font color=#0000ff>立此存照</font></u></a> </div>
</div>
<p>######################################<br># Copyright (c) 1997 George Foot (george.foot@merton.ox.ac.uk)<br># All rights reserved.<br>######################################<br>#目标（可执行文档）名称，库（譬如stdcx,iostr,mysql等），头文件路径<br>DESTINATION := test<br>LIBS := <br>INCLUDES := .<br><br><br>RM := rm -f<br>#C,CC或CPP文件的后缀<br>PS=cpp<br># GNU Make的隐含变量定义<br>CC=g++<br>CPPFLAGS = -g -Wall -O3 -march=i486<br>CPPFLAGS += $(addprefix -I,$(INCLUDES))<br>CPPFLAGS += -MMD<br><br>#以下部分无需修改<br>SOURCE := $(wildcard *.$(PS))<br>OBJS := $(patsubst %.$(PS),%.o,$(SOURCE))<br>DEPS := $(patsubst %.o,%.d,$(OBJS))<br>MISSING_DEPS := $(filter-out $(wildcard $(DEPS)),$(DEPS))<br>MISSING_DEPS_SOURCES := $(wildcard $(patsubst %.d,%.$(PS),$(MISSING_DEPS)))<br><br>.PHONY : all deps objs clean rebuild<br><br>all : $(DESTINATION)<br><br>deps : $(DEPS)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$(CC) -MM -MMD $(SOURCE)</p>
<p><br>objs : $(OBJS)<br><br>clean :<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@$(RM) *.o<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@$(RM) *.d<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@$(RM) $(DESTINATION)<br><br>rebuild: clean all <br><br>ifneq ($(MISSING_DEPS),)<br>$(MISSING_DEPS) :<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@$(RM) $(patsubst %.d,%.o,$@)<br>endif<br><br>-include $(DEPS)<br><br>$(DESTINATION) : $(OBJS)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$(CC) -o $(DESTINATION) $(OBJS) $(addprefix -l,$(LIBS))<br>#结束<br></p>
<ul>
    <li>原作者是Gorge Foot，写这个Makefile的时候还是一个学生
    <li><strong>":="</strong>赋值，和"="不同的是，":="在赋值的同时，会将赋值语句中所有的变量就地展开，也就是说，A:=$(B)后，B的值的改变不再影响A
    <li><strong>隐含规则</strong>。GUN Make在不特别指定的情况下会使用诸如以下编译命令：$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $&lt; -o $@，这也是为什么这个Makefile最后一个命令没有添加$(CPPFLAGS)的原因，因为缺省是包含这个变量的
    <li><strong>函数</strong>和变量很相似："$ (函数名，空格，一列由逗号分隔的参数)"
    <li>SOURCES = $(wildcard *.cpp) 列出工作目录下文件名满足"*.cpp"条件的文件，以空格分隔，并将列表赋给SOURCE变量
    <li><strong>patsubst</strong>函数：3个参数。功能是将第三个参数中的每一项（由空格分隔）符合第一个参数描述的部分替换成第二个参数制定的值
    <li><strong>addprefix</strong>函数：2个参数。将源串（第2个参数，由空格分隔）中的每一项添加前缀（第1个参数）
    <li><strong>filter-out</strong>函数：2个参数。从第二串中过滤掉包含在第一个串中的项
    <li>$(CC) -MM -MMD $(SOURCE) : 对每个源文件生成依赖(dependence，Make通过依赖规则来判断是否需要重新编译某个文件)，"D"生成".d"文件，-MM表示去掉 depends里面的系统的头文件(使用&lt;&gt;包含的头文件)（若使用-M则全部包含，事实上，系统头文件被修改的可能性极小，不需要执行依赖检查）
    <li><strong>.PHONY</strong>，不检查后面制定各项是否存在同名文件
    <li><strong>ifneg...else...endif</strong>，Makefile中的条件语句
    <li><strong>-include</strong> $(DEPS) : 将DEPS中的文件包含进来，"-"表示忽略文件不存在的错误
    <li><strong>@</strong>$(RM) *.o : 开头的"@"表示在Make的时候，不显示这条命令（GNU Make缺省是显示的)
    <li>all : 作为第一个出现的目标项目，Make会将它作为主要和缺省项目("make"就表示"make all")
    <li>deps : 只生成依赖文件(.d文件)
    <li>objs : 为每一个源码程序生成或更新 '.d' 文件和'.o'文件
    <li>clean : 删除所有'.d','.o'和可执行文件
    <li>rebuild : clean然后重建
    <li>内部变量<strong>$@, $&lt; $^</strong> : 分别表示目标名(:前面的部分，比如all)，依靠列表（:后面的部分）中的第一个依靠文件，所有依靠文件 </li>
</ul>
<br><!-- comment these out if you want to see an example of custom fields, but remember to name the fields
in the same way they are named here: 'imfeeling' (livejournal.com style), 'listening' and 'new_field'
<p>
<strong></strong> <br/>
<strong>:</strong> <br/>
<strong></strong>?
</p>
-->
<img src ="http://www.cppblog.com/true/aggbug/21301.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2007-04-05 12:01 <a href="http://www.cppblog.com/true/archive/2007/04/05/21301.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于Linux下编写和编译程序的几个问题</title><link>http://www.cppblog.com/true/archive/2007/04/05/21289.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Thu, 05 Apr 2007 02:34:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2007/04/05/21289.html</guid><wfw:comment>http://www.cppblog.com/true/comments/21289.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2007/04/05/21289.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/21289.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/21289.html</trackback:ping><description><![CDATA[&nbsp;<br>&nbsp;&nbsp;&#183; 闫健勇&#183;CPCW <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;　　当前，虽然Linux还不很普及,在Linux下编写和编译程序的人不多。但是我相信,随着Linux性能的不断提升和逐渐普及，会有许多自由软件出现，也会有许多人成为Linux下的程序员。我结合自己的经验，介绍一下Linux下编写和编译程序所要注意的几个问题，奉献给希望为Linux的发展作出贡献的人们。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;Linux下怎样编译程序？ <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;大多数Linux程序都是由C语言编写的并由GNU C编译而成。现在GCC是各种发行套件的一部分。有关最新GCC编译器的版本、文章和补丁请看ftp://ftp.gnu.org/pub/gnu/。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;由C＋＋编写的程序必须由GNU C++编译，GNU C++也是各种发行套件的一部分，在以上网址也有最新版本和补丁。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;编译2.0.x的内核需要2.7.2.x版本的GCC，如用象GCC 2.8.x, EGCS, or PGCC别的编译器编译内核可能产生不可预想的后果。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;怎样移植其它Unix程序到Linux上？ <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;总得来说，Unix上的程序不需要做改动，只要简单的按照提示就可以移植到Linux上，如果安装过程中出现错误信息，而你又不知道怎么处理，你可以猜或略去，不过这样产生的程序往往带有bug。所以最好还是问一下有经验的人。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;如果要从BSD-ish移植程序，试一试在编译时加上-I/usr/include/bsd 和 ？lbsd命令。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;什么是ld.so,从哪可以找到它？ <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;ld.so是动态函数库装载器。过去，使用共享函数库的程序在原代码开头使用约3K的空间来寻找和加载共享函数库，现在，这段代码被加进了一个特殊共享函数库/lib/ld.so，所有的程序都可以使用该共享库，这样就节省了磁盘空间，而且升级方便。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;ld.so可以从以下网址得到tsx-11.mit.edu/pub/linux/packages/GCC/。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;怎样升级库函数而不使系统崩溃？ <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;注意：进行此操作应该养成做备份的习惯，因为这项操作很容易出错。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;如果你升级象libc4这样的老函数库，这个过程会变得非常困难。而且你应该在该系统上让libc4和libc5共存，因为，有些老程序还需要它。升级libc5也一样。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;升级动态库的问题常出现在当你移走老的函数库时，用来升级的程序也运行不了了。有许多方法可以解决这个问题。一个方法就是暂时备份一下运行程序所需函数库，它们一般在/lib/、/usr/lib/、 /usr/local/lib/、或其它的地方，在文件/etc/ld.so.conf中都有详细记录。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;例如，当你升级libc5时，目录/lib/中有如下文件 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;libc.so.5 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;libc.so.5.4.33 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;libm.so.5 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;libm.so.5.0.9 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;这些是C函数库和数学库，拷贝它们到文件/etc/ld.so.conf中含有的其它的目录，如/usr/lib/中： <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;cp -df /lib/libc.so.5* /usr/lib/ <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;cp -df /lib/libm.so.5* /usr/lib/ <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;ldconfig <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;一定要记住运行ldconfig来升级函数库的配置文件。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;文件libc.so.5 和 libm.so.5是实际库文件的链接文件，当你升级的时候，如果老的链接文件存在，新的链接不会产生，除非你使用CP命令的-f选项。CP的-d选项只复制链接文件，不复制原文件。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;如果你需要直接覆盖链接，使用ln命令的选项-f。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;例如，拷贝新的库函数覆盖旧的。先对新的函数库做一个链接，然后把函数库和链接一起拷贝到/lib/中，命令如下： <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;ln -sf ./libm.so.5.0.48 libm.so.5 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;ln -sf ./libc.so.5.0.48 libc.so.5 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;cp -df libm.so.5* /lib <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;cp -df libc.so.5* /lib <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;重申一下，拷贝完别忘记运行ldconfig. <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;如果一切工作顺利的话，你可以删除老的函数库的备份。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;我能否把在486上编译的代码或编译器拿到386上用？ <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;当然，除非你编译的是内核。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;GCC用来在486上编译的选项-m486 只是优化了所编译程序，使其运行快一些。这些编译的程序仍能很好的在386上运行，只是效果差一些。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;然而，从内核1.3.35以后，采用486或Pentium选项编译的内核不能用于386的机器。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;GCC可以针对386和486进行配置。两者区别在于，针对386配置的GCC把-m386作为缺省选项，而针对486配置的GCC把-m486作为缺省选项，仅此而已。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;gcc -O6可以干什么？ <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;目前，它和 -O2 (GCC 2.5) 或 -O3 (GCC 2.6, 2.7)功能一样，所有版本大于它的功能也一样。新版内核的Makefiles使用－O2，所以你也应该用－O2。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;linux/*.h 和asm/*.h在什么地方？ <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;目录 /usr/include/linux/ 和 /usr/include/asm/低下的文件是内核头文件的软链接，内核头文件其实在目录/usr/src/kernel*/低下。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;怎样作一个共享函数库？ <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;对ELF, 命令如下： <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;gcc -fPIC -c *.c <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;gcc -shared -Wl,-soname,libfoo.so.1 -o libfoo.so.1.0 *.o <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;对a.out，从 http://tsx-11.mit.edu/pub/linux/packages/GCC/src/ 下载n.nn.tar.gz，其中包含详细说明。建议你将共享库由a.out升级为ELF。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;为什么我编译的可执行程序非常大？ <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;用ELF编译器，生成可执行程序太大最可能的原因是没有合适的.so库与你使用的库函数链接。对每一个象libc.so.5.2.18的函数库，应该有一个象libc.so的链接。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;用a.out编译器，生成可执行程序太大可能是使用了-g选项，这会生成静态链接库而不是动态链接库。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;从哪可以得到对于Linux的&#8216;lint&#8217;？ <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;大部分&#8216;lint&#8217;的功能已经内置进了GCC，打开GCC的-Wall选项会打开许多有用的外部警告。 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp;还有一个叫`lclint'的软件功能和传统的lint差不多，原代码可以在http://larch.lcs.mit.edu /pub/Larch/lclint/中找到。 <br>&nbsp;&nbsp; &nbsp;做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。 <br>
<img src ="http://www.cppblog.com/true/aggbug/21289.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2007-04-05 10:34 <a href="http://www.cppblog.com/true/archive/2007/04/05/21289.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在Linux中创建静态库和动态库 【转】</title><link>http://www.cppblog.com/true/archive/2007/04/04/21256.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Wed, 04 Apr 2007 10:22:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2007/04/04/21256.html</guid><wfw:comment>http://www.cppblog.com/true/comments/21256.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2007/04/04/21256.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/21256.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/21256.html</trackback:ping><description><![CDATA[<p style="TEXT-INDENT: 20pt">我们通常把一些公用函数制作成函数库，供其它程序使用。函数库分为静态库和动态库两种。静态库在程序编译时会被连接到目标代码中，程序运行时将不再需要该静态库。动态库在程序编译时并不会被连接到目标代码中，而是在程序运行是才被载入，因此在程序运行时还需要动态库存在。本文主要通过举例来说明在Linux中如何创建静态库和动态库，以及使用它们。 </p>
<p style="TEXT-INDENT: 20pt">在创建函数库前，我们先来准备举例用的源程序，并将函数库的源程序编译成.o文件。 </p>
<p style="TEXT-INDENT: 20pt">第1步：编辑得到举例的程序--hello.h、hello.c和main.c； </p>
<p style="TEXT-INDENT: 20pt">hello.c(见程序2)是函数库的源程序，其中包含公用函数hello，该函数将在屏幕上输出"Hello XXX!"。hello.h(见程序1)为该函数库的头文件。main.c(见程序3)为测试库文件的主程序，在主程序中调用了公用函数hello。 </p>
&nbsp;#ifndef HELLO_H<br>&nbsp;#define HELLO_H<br>&nbsp;<br>&nbsp;void hello(const char *name);<br>&nbsp;<br>&nbsp;#endif //HELLO_H<br>&nbsp;程序1: hello.h<br>&nbsp;<br>&nbsp;#include &lt;stdio.h&gt;<br>&nbsp;<br>&nbsp;void hello(const char *name)<br>&nbsp;{<br>&nbsp;&nbsp;printf("Hello %s!\n", name);<br>&nbsp;}<br>&nbsp;程序2: hello.c<br>&nbsp;<br>&nbsp;#include "hello.h"<br>&nbsp;<br>&nbsp;int main()<br>&nbsp;{<br>&nbsp;&nbsp;hello("everyone");<br>&nbsp;&nbsp;return 0;<br>&nbsp;}<br>&nbsp;程序3: main.c<br>
<p style="TEXT-INDENT: 20pt">第2步：将hello.c编译成.o文件； </p>
<p style="TEXT-INDENT: 20pt">无论静态库，还是动态库，都是由.o文件创建的。因此，我们必须将源程序hello.c通过gcc先编译成.o文件。 </p>
<p style="TEXT-INDENT: 20pt">在系统提示符下键入以下命令得到hello.o文件。 </p>
<p style="TEXT-INDENT: 40pt"># gcc -c hello.c </p>
<p style="TEXT-INDENT: 40pt"># </p>
<p style="TEXT-INDENT: 20pt">(注1：本文不介绍各命令使用和其参数功能，若希望详细了解它们，请参考其他文档。) </p>
<p style="TEXT-INDENT: 20pt">(注2：首字符"#"是系统提示符，不需要键入，下文相同。) </p>
<p style="TEXT-INDENT: 20pt">我们运行ls命令看看是否生存了hello.o文件。 </p>
<p style="TEXT-INDENT: 40pt"># ls </p>
<p style="TEXT-INDENT: 40pt">hello.c hello.h hello.o main.c </p>
<p style="TEXT-INDENT: 40pt"># </p>
<p style="TEXT-INDENT: 20pt">(注3：首字符不是"#"为系统运行结果，下文相同。) </p>
<p style="TEXT-INDENT: 20pt">在ls命令结果中，我们看到了hello.o文件，本步操作完成。 </p>
<p style="TEXT-INDENT: 20pt">下面我们先来看看如何创建静态库，以及使用它。 </p>
<p style="TEXT-INDENT: 20pt">第3步：由.o文件创建静态库； </p>
<p style="TEXT-INDENT: 20pt">静态库文件名的命名规范是以lib为前缀，紧接着跟静态库名，扩展名为.a。例如：我们将创建的静态库名为myhello，则静态库文件名就是libmyhello.a。在创建和使用静态库时，需要注意这点。创建静态库用ar命令。 </p>
<p style="TEXT-INDENT: 20pt">在系统提示符下键入以下命令将创建静态库文件libmyhello.a。 </p>
<p style="TEXT-INDENT: 40pt"># ar cr libmyhello.a hello.o </p>
<p style="TEXT-INDENT: 40pt"># </p>
<p style="TEXT-INDENT: 20pt">我们同样运行ls命令查看结果： </p>
<p style="TEXT-INDENT: 40pt"># ls </p>
<p style="TEXT-INDENT: 40pt">hello.c hello.h hello.o libmyhello.a main.c </p>
<p style="TEXT-INDENT: 40pt"># </p>
<p style="TEXT-INDENT: 20pt">ls命令结果中有libmyhello.a。 </p>
<p style="TEXT-INDENT: 20pt">第4步：在程序中使用静态库； </p>
<p style="TEXT-INDENT: 20pt">静态库制作完了，如何使用它内部的函数呢？只需要在使用到这些公用函数的源程序中包含这些公用函数的原型声明，然后在用gcc命令生成目标文件时指明静态库名，gcc将会从静态库中将公用函数连接到目标文件中。注意，gcc会在静态库名前加上前缀lib，然后追加扩展名.a得到的静态库文件名来查找静态库文件。 </p>
<p style="TEXT-INDENT: 20pt">在程序3:main.c中，我们包含了静态库的头文件hello.h，然后在主程序main中直接调用公用函数hello。下面先生成目标程序hello，然后运行hello程序看看结果如何。 </p>
<p style="TEXT-INDENT: 40pt"># gcc -o hello main.c -L. -lmyhello </p>
<p style="TEXT-INDENT: 40pt"># ./hello </p>
<p style="TEXT-INDENT: 40pt">Hello everyone! </p>
<p style="TEXT-INDENT: 40pt"># </p>
<p style="TEXT-INDENT: 20pt">我们删除静态库文件试试公用函数hello是否真的连接到目标文件 hello中了。 </p>
<p style="TEXT-INDENT: 40pt"># rm libmyhello.a </p>
<p style="TEXT-INDENT: 40pt">rm: remove regular file `libmyhello.a'? y </p>
<p style="TEXT-INDENT: 40pt"># ./hello </p>
<p style="TEXT-INDENT: 40pt">Hello everyone! </p>
<p style="TEXT-INDENT: 40pt"># </p>
<p style="TEXT-INDENT: 20pt">程序照常运行，静态库中的公用函数已经连接到目标文件中了。 </p>
<p style="TEXT-INDENT: 20pt">我们继续看看如何在Linux中创建动态库。我们还是从.o文件开始。 </p>
<p style="TEXT-INDENT: 20pt">第5步：由.o文件创建动态库文件； </p>
<p style="TEXT-INDENT: 20pt">动态库文件名命名规范和静态库文件名命名规范类似，也是在动态库名增加前缀lib，但其文件扩展名为.so。例如：我们将创建的动态库名为myhello，则动态库文件名就是libmyhello.so。用gcc来创建动态库。 </p>
<p style="TEXT-INDENT: 20pt">在系统提示符下键入以下命令得到动态库文件libmyhello.so。 </p>
<p style="TEXT-INDENT: 40pt"># gcc -shared -fPCI -o libmyhello.so hello.o </p>
<p style="TEXT-INDENT: 40pt"># </p>
<p style="TEXT-INDENT: 20pt">我们照样使用ls命令看看动态库文件是否生成。 </p>
<p style="TEXT-INDENT: 40pt"># ls </p>
<p style="TEXT-INDENT: 40pt">hello.c hello.h hello.o libmyhello.so main.c </p>
<p style="TEXT-INDENT: 40pt"># </p>
<p style="TEXT-INDENT: 20pt">第6步：在程序中使用动态库； </p>
<p style="TEXT-INDENT: 20pt">在程序中使用动态库和使用静态库完全一样，也是在使用到这些公用函数的源程序中包含这些公用函数的原型声明，然后在用gcc命令生成目标文件时指明动态库名进行编译。我们先运行gcc命令生成目标文件，再运行它看看结果。 </p>
<p style="TEXT-INDENT: 40pt"># gcc -o hello main.c -L. -lmyhello </p>
<p style="TEXT-INDENT: 40pt"># ./hello </p>
<p style="TEXT-INDENT: 40pt">./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory </p>
<p style="TEXT-INDENT: 40pt"># </p>
<p style="TEXT-INDENT: 20pt">哦！出错了。快看看错误提示，原来是找不到动态库文件libmyhello.so。程序在运行时，会在/usr/lib和/lib等目录中查找需要的动态库文件。若找到，则载入动态库，否则将提示类似上述错误而终止程序运行。我们将文件libmyhello.so复制到目录/usr/lib中，再试试。 </p>
<p style="TEXT-INDENT: 40pt"># mv libmyhello.so /usr/lib </p>
<p style="TEXT-INDENT: 40pt"># ./hello </p>
<p style="TEXT-INDENT: 40pt">Hello everyone! </p>
<p style="TEXT-INDENT: 40pt"># </p>
<p style="TEXT-INDENT: 20pt">成功了。这也进一步说明了动态库在程序运行时是需要的。 </p>
<p style="TEXT-INDENT: 20pt">我们回过头看看，发现使用静态库和使用动态库编译成目标程序使用的gcc命令完全一样，那当静态库和动态库同名时，gcc命令会使用哪个库文件呢？抱着对问题必究到底的心情，来试试看。 </p>
<p style="TEXT-INDENT: 20pt">先删除除.c和.h外的所有文件，恢复成我们刚刚编辑完举例程序状态。 </p>
<p style="TEXT-INDENT: 40pt"># rm -f hello hello.o /usr/lib/libmyhello.so </p>
<p style="TEXT-INDENT: 40pt"># ls </p>
<p style="TEXT-INDENT: 40pt">hello.c hello.h main.c </p>
<p style="TEXT-INDENT: 40pt"># </p>
<p style="TEXT-INDENT: 20pt">在来创建静态库文件libmyhello.a和动态库文件libmyhello.so。 </p>
<p style="TEXT-INDENT: 40pt"># gcc -c hello.c </p>
<p style="TEXT-INDENT: 40pt"># ar cr libmyhello.a hello.o </p>
<p style="TEXT-INDENT: 40pt"># gcc -shared -fPCI -o libmyhello.so hello.o </p>
<p style="TEXT-INDENT: 40pt"># ls </p>
<p style="TEXT-INDENT: 40pt">hello.c hello.h hello.o libmyhello.a libmyhello.so main.c </p>
<p style="TEXT-INDENT: 40pt"># </p>
<p style="TEXT-INDENT: 20pt">通过上述最后一条ls命令，可以发现静态库文件libmyhello.a和动态库文件libmyhello.so都已经生成，并都在当前目录中。然后，我们运行gcc命令来使用函数库myhello生成目标文件hello，并运行程序 hello。 </p>
<p style="TEXT-INDENT: 40pt"># gcc -o hello main.c -L. -lmyhello </p>
<p style="TEXT-INDENT: 40pt"># ./hello </p>
<p style="TEXT-INDENT: 40pt">./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory </p>
<p style="TEXT-INDENT: 40pt"># </p>
<p style="TEXT-INDENT: 20pt">从程序hello运行的结果中很容易知道，当静态库和动态库同名时， gcc命令将优先使用动态库。 </p>
<img src ="http://www.cppblog.com/true/aggbug/21256.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2007-04-04 18:22 <a href="http://www.cppblog.com/true/archive/2007/04/04/21256.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>进程和线程编程－linux方面，转</title><link>http://www.cppblog.com/true/archive/2007/03/27/20712.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Tue, 27 Mar 2007 08:28:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2007/03/27/20712.html</guid><wfw:comment>http://www.cppblog.com/true/comments/20712.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2007/03/27/20712.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/20712.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/20712.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 进程和线程编程目 录进程和线程编程原始管道pipe()dup()dup2()popen()和pclose()命名管道创建FIFO操作FIFO阻塞FIFO消息队列msgget()msgsnd()msgrcv()msgctl()信号量semget()semop()semctl()共享内存shmget()shmat()shmctl()shmdt()线程线程同步使用信号量协调程序代码例子newthread...&nbsp;&nbsp;<a href='http://www.cppblog.com/true/archive/2007/03/27/20712.html'>阅读全文</a><img src ="http://www.cppblog.com/true/aggbug/20712.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2007-03-27 16:28 <a href="http://www.cppblog.com/true/archive/2007/03/27/20712.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux环境进程间通信[转]</title><link>http://www.cppblog.com/true/archive/2007/03/27/20711.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Tue, 27 Mar 2007 08:26:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2007/03/27/20711.html</guid><wfw:comment>http://www.cppblog.com/true/comments/20711.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2007/03/27/20711.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/20711.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/20711.html</trackback:ping><description><![CDATA[
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr valign="top">
								<td width="100%">
										<h1>Linux环境进程间通信（三）</h1>
										<p id="subtitle">消息队列</p>
										<img class="display-img" height="6" alt="" src="http://www.ibm.com/i/c.gif" width="1" twffan="done" />
								</td>
								<td class="no-print" width="192">
										<img height="18" alt="developerWorks" src="http://www.ibm.com/developerworks/cn/i/dw.gif" width="192" twffan="done" />
								</td>
						</tr>
				</tbody>
		</table>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr valign="top">
								<td width="10">
										<img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" twffan="done" />
								</td>
								<td width="100%">
										<table class="no-print" cellspacing="0" cellpadding="0" width="160" align="right" border="0">
												<tbody>
														<tr>
																<td width="10">
																		<img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" twffan="done" />
																</td>
																<td>
																		<table cellspacing="0" cellpadding="0" width="150" border="0">
																				<tbody>
																						<tr>
																								<td class="v14-header-1-small">
																								</td>
																						</tr>
																				</tbody>
																		</table>
																		<table class="v14-gray-table-border" cellspacing="0" cellpadding="0" border="0">
																				<tbody>
																						<tr>
																								<td class="no-padding" width="150">
																										<table cellspacing="0" cellpadding="0" width="143" border="0">
																												<img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="8" twffan="done" />
																												<form name="email" action="https://www.ibm.com/developerworks/secure/email-it.jsp">
																														<script language="JavaScript" type="text/javascript">
																																<!--
document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr>');
//-->
																														</script>
 
<tbody><tr valign="top"><td width="8"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="8" twffan="done" /></td><td width="16"></td><td width="122"></td></tr><noscript><tr valign="top"><td width="8"><img alt="" height="1" width="8" src="//www.ibm.com/i/c.gif" /></td><td width="16"><img alt="" width="16" height="16" src="//www.ibm.com/i/c.gif" /></td><td class="small" width="122"><p><span class="ast">未显示需要 JavaScript 的文档选项</span></p></td></tr></noscript></tbody></form>
																										</table>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
										<!--START RESERVED FOR FUTURE USE INCLUDE FILES-->
										<!-- 03/20/06 updated by gretchen -->
										<br />
										<table cellspacing="0" cellpadding="0" width="150" border="0">
												<tbody>
														<tr>
																<td class="v14-header-2-small">
																</td>
														</tr>
												</tbody>
										</table>
										<table class="v14-gray-table-border" cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td class="no-padding" width="150">
																		<table cellspacing="0" cellpadding="0" width="143" border="0">
																				<tbody>
																						<tr valign="top">
																								<td width="8">
																										<img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="8" twffan="done" />
																								</td>
																								<td>
																								</td>
																								<td width="125">
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
										<!--END RESERVED FOR FUTURE USE INCLUDE FILES-->
										<br />
								</td>
						</tr>
				</tbody>
		</table>
		<p>级别: 初级</p>
		<p>
				<a href="http://www.ibm.com/developerworks/cn/linux/l-ipc/part3/#author">
						<font color="#996699">郑彦兴</font>
				</a> (<a href="mailto:mlinux@163.com?subject=Linux环境进程间通信（三）"><font color="#5c81a7">mlinux@163.com</font></a>)国防科大计算机学院<br /></p>
		<p>2003 年 1 月 17 日</p>
		<blockquote>本系列文章中的前两部分，我们探讨管道及信号两种通信机制，本文将深入第三部分，介绍系统 V 消息队列及其相应 API。</blockquote>
		<!--START RESERVED FOR FUTURE USE INCLUDE FILES-->
		<!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters -->
		<!--END RESERVED FOR FUTURE USE INCLUDE FILES-->
		<p>消息队列（也叫做报文队列）能够克服早期unix通信机制的一些缺点。作为早期unix通信机制之一的信号能够传送的信息量有限，后来虽然POSIX 1003.1b在信号的实时性方面作了拓广，使得信号在传递信息量方面有了相当程度的改进，但是信号这种通信方式更像"即时"的通信方式，它要求接受信号的进程在某个时间范围内对信号做出反应，因此该信号最多在接受信号进程的生命周期内才有意义，信号所传递的信息是接近于随进程持续的概念（process-persistent），见 <a href="http://www.ibm.com/developerworks/cn/linux/l-ipc/part3/#listing1"><font color="#996699">附录 1</font></a>；管道及有名管道及有名管道则是典型的随进程持续IPC，并且，只能传送无格式的字节流无疑会给应用程序开发带来不便，另外，它的缓冲区大小也受到限制。 </p>
		<p>消息队列就是一个消息的链表。可以把消息看作一个记录，具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向中按照一定的规则添加新消息；对消息队列有读权限的进程则可以从消息队列中读走消息。消息队列是随内核持续的（参见 <a href="http://www.ibm.com/developerworks/cn/linux/l-ipc/part3/#listing1"><font color="#996699">附录 1</font></a>）。 </p>
		<p>目前主要有两种类型的消息队列：POSIX消息队列以及系统V消息队列，系统V消息队列目前被大量使用。考虑到程序的可移植性，新开发的应用程序应尽量使用POSIX消息队列。</p>
		<p>在本系列专题的序（深刻理解Linux进程间通信（IPC））中，提到对于消息队列、信号灯、以及共享内存区来说，有两个实现版本：POSIX的以及系统V的。Linux内核（内核2.4.18）支持POSIX信号灯、POSIX共享内存区以及POSIX消息队列，但对于主流Linux发行版本之一redhad8.0（内核2.4.18），还没有提供对POSIX进程间通信API的支持，不过应该只是时间上的事。</p>
		<p>因此，本文将主要介绍系统V消息队列及其相应API。 <b>在没有声明的情况下，以下讨论中指的都是系统V消息队列。</b></p>
		<p>
				<a name="N1005A">
						<span class="atitle" twffan="done">一、消息队列基本概念</span>
				</a>
		</p>
		<ol>
				<li>系统V消息队列是随内核持续的，只有在内核重起或者显示删除一个消息队列时，该消息队列才会真正被删除。因此系统中记录消息队列的数据结构（struct ipc_ids msg_ids）位于内核中，系统中的所有消息队列都可以在结构msg_ids中找到访问入口。 
</li>
				<li>消息队列就是一个消息的链表。每个消息队列都有一个队列头，用结构struct msg_queue来描述（参见 <a href="http://www.ibm.com/developerworks/cn/linux/l-ipc/part3/#listing2"><font color="#996699">附录 2</font></a>）。队列头中包含了该消息队列的大量信息，包括消息队列键值、用户ID、组ID、消息队列中消息数目等等，甚至记录了最近对消息队列读写进程的ID。读者可以访问这些信息，也可以设置其中的某些信息。 
</li>
				<li>下图说明了内核与消息队列是怎样建立起联系的： <br />其中：struct ipc_ids msg_ids是内核中记录消息队列的全局数据结构；struct msg_queue是每个消息队列的队列头。 <br /><br /><img height="219" alt="" src="http://www.ibm.com/developerworks/cn/linux/l-ipc/part3/fig1.gif" width="529" border="0" twffan="done" /><br /></li>
		</ol>
		<p>从上图可以看出，全局数据结构 struct ipc_ids msg_ids 可以访问到每个消息队列头的第一个成员：struct kern_ipc_perm；而每个struct kern_ipc_perm能够与具体的消息队列对应起来是因为在该结构中，有一个key_t类型成员key，而key则唯一确定一个消息队列。kern_ipc_perm结构如下：</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">struct kern_ipc_perm{   //内核中记录消息队列的全局数据结构msg_ids能够访问到该结构；
            key_t   key;    //该键值则唯一对应一个消息队列
            uid_t   uid;
            gid_t   gid;
uid_t   cuid;
gid_t   cgid;
mode_t  mode;
unsigned long seq;
}
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" twffan="done" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" twffan="done" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" twffan="done" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" twffan="done" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www.ibm.com/developerworks/cn/linux/l-ipc/part3/#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N1008A">
						<span class="atitle" twffan="done">二、操作消息队列</span>
				</a>
		</p>
		<p>
				<a name="N10090">
						<span class="smalltitle" twffan="done">
								<strong>
										<font face="Arial">对消息队列的操作无非有下面三种类型：</font>
								</strong>
						</span>
				</a>
		</p>
		<p>1、 打开或创建消息队列 <br />消息队列的内核持续性要求每个消息队列都在系统范围内对应唯一的键值，所以，要获得一个消息队列的描述字，只需提供该消息队列的键值即可； </p>
		<p>注：消息队列描述字是由在系统范围内唯一的键值生成的，而键值可以看作对应系统内的一条路经。</p>
		<p>2、 读写操作</p>
		<p>消息读写操作非常简单，对开发人员来说，每个消息都类似如下的数据结构：</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">struct msgbuf{
long mtype;
char mtext[1];
};
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>mtype成员代表消息类型，从消息队列中读取消息的一个重要依据就是消息的类型；mtext是消息内容，当然长度不一定为1。因此，对于发送消息来说，首先预置一个msgbuf缓冲区并写入消息类型和内容，调用相应的发送函数即可；对读取消息来说，首先分配这样一个msgbuf缓冲区，然后把消息读入该缓冲区即可。</p>
		<p>3、 获得或设置消息队列属性：</p>
		<p>消息队列的信息基本上都保存在消息队列头中，因此，可以分配一个类似于消息队列头的结构(struct msqid_ds，见 <a href="http://www.ibm.com/developerworks/cn/linux/l-ipc/part3/#listing2"><font color="#996699">附录 2</font></a>)，来返回消息队列的属性；同样可以设置该数据结构。 </p>
		<br />
		<br />
		<br />
		<img height="219" alt="" src="http://www.ibm.com/developerworks/cn/linux/l-ipc/part3/fig2.gif" width="507" border="0" twffan="done" />
		<br />
		<br />
		<p>
				<a name="N100CA">
						<span class="smalltitle" twffan="done">
								<strong>
										<font face="Arial">消息队列API</font>
								</strong>
						</span>
				</a>
		</p>
		<p>
				<strong>1、文件名到键值</strong>
		</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">#include &lt;sys/types.h&gt;
#include &lt;sys/ipc.h&gt;
key_t ftok (char*pathname, char proj)；
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>它返回与路径pathname相对应的一个键值。该函数不直接对消息队列操作，但在调用ipc(MSGGET,…)或msgget()来获得消息队列描述字前，往往要调用该函数。典型的调用代码是：</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">key=ftok(path_ptr, 'a');
    ipc_id=ipc(MSGGET, (int)key, flags,0,NULL,0);
    …
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<b>2、linux为操作系统V进程间通信的三种方式（消息队列、信号灯、共享内存区）提供了一个统一的用户界面：</b>
				<br />
				<code>
						<span class="boldcode" twffan="done">
								<strong>
										<font face="Lucida Console" size="2">int ipc</font>
								</strong>
						</span>(unsigned int <span class="boldcode" twffan="done"><strong><font face="Lucida Console" size="2">call</font></strong></span>, int <span class="boldcode" twffan="done"><strong><font face="Lucida Console" size="2">first</font></strong></span>, int <span class="boldcode" twffan="done"><strong><font face="Lucida Console" size="2">second</font></strong></span>, int <span class="boldcode" twffan="done"><strong><font face="Lucida Console" size="2">third</font></strong></span>, void * <span class="boldcode" twffan="done"><strong><font face="Lucida Console" size="2">ptr</font></strong></span>, long <span class="boldcode" twffan="done"><strong><font face="Lucida Console" size="2">fifth</font></strong></span>); </code>
		</p>
		<p>第一个参数指明对IPC对象的操作方式，对消息队列而言共有四种操作：MSGSND、MSGRCV、MSGGET以及MSGCTL，分别代表向消息队列发送消息、从消息队列读取消息、打开或创建消息队列、控制消息队列；first参数代表唯一的IPC对象；下面将介绍四种操作。</p>
		<ul>
				<li>
						<b>int ipc</b>( <b>MSGGET, int</b>first, <b>int</b>second, <b>int</b>third, <b>void</b>*ptr, <b>long</b>fifth); <br />与该操作对应的系统V调用为：int msgget( (key_t)first，second)。 
</li>
				<li>
						<b>int ipc</b>( <b>MSGCTL, int</b>first, <b>int</b>second, <b>int</b>third, <b>void</b>*ptr, <b>long</b>fifth) <br />与该操作对应的系统V调用为：int msgctl( first，second, (struct msqid_ds*) ptr)。 
</li>
				<li>
						<b>int ipc</b>( <b>MSGSND, int</b>first, <b>int</b>second, <b>int</b>third, <b>void</b>*ptr, <b>long</b>fifth); <br />与该操作对应的系统V调用为：int msgsnd( first, (struct msgbuf*)ptr, second, third)。 
</li>
				<li>
						<b>int ipc</b>( <b>MSGRCV, int</b>first, <b>int</b>second, <b>int</b>third, <b>void</b>*ptr, <b>long</b>fifth); <br />与该操作对应的系统V调用为：int msgrcv( first，(struct msgbuf*)ptr, second, fifth,third)， </li>
		</ul>
		<br />
		<p>注：本人不主张采用系统调用ipc()，而更倾向于采用系统V或者POSIX进程间通信API。原因如下：</p>
		<ul>
				<li>虽然该系统调用提供了统一的用户界面，但正是由于这个特性，它的参数几乎不能给出特定的实际意义（如以first、second来命名参数），在一定程度上造成开发不便。 
</li>
				<li>正如ipc手册所说的：ipc()是linux所特有的，编写程序时应注意程序的移植性问题； 
</li>
				<li>该系统调用的实现不过是把系统V IPC函数进行了封装，没有任何效率上的优势； 
</li>
				<li>系统V在IPC方面的API数量不多，形式也较简洁。 </li>
		</ul>
		<br />
		<p>
				<b>3.系统V消息队列API</b>
				<br />系统V消息队列API共有四个，使用时需要包括几个头文件： </p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">#include &lt;sys/types.h&gt;
#include &lt;sys/ipc.h&gt;
#include &lt;sys/msg.h&gt;
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<b>1）int msgget(key_t key, int msgflg)</b>
		</p>
		<p>参数key是一个键值，由ftok获得；msgflg参数是一些标志位。该调用返回与健值key相对应的消息队列描述字。</p>
		<p>在以下两种情况下，该调用将创建一个新的消息队列：</p>
		<ul>
				<li>如果没有消息队列与健值key相对应，并且msgflg中包含了IPC_CREAT标志位； 
</li>
				<li>key参数为IPC_PRIVATE； </li>
		</ul>
		<br />
		<p>参数msgflg可以为以下：IPC_CREAT、IPC_EXCL、IPC_NOWAIT或三者的或结果。</p>
		<p>
				<b>调用返回：</b>成功返回消息队列描述字，否则返回-1。 </p>
		<p>注：参数key设置成常数IPC_PRIVATE并不意味着其他进程不能访问该消息队列，只意味着即将创建新的消息队列。</p>
		<p>
				<b>2）int msgrcv(int msqid, struct msgbuf *msgp, int msgsz, long msgtyp, int msgflg);</b>
				<br />该系统调用从msgid代表的消息队列中读取一个消息，并把消息存储在msgp指向的msgbuf结构中。 </p>
		<p>msqid为消息队列描述字；消息返回后存储在msgp指向的地址，msgsz指定msgbuf的mtext成员的长度（即消息内容的长度），msgtyp为请求读取的消息类型；读消息标志msgflg可以为以下几个常值的或：</p>
		<ul>
				<li>IPC_NOWAIT 如果没有满足条件的消息，调用立即返回，此时，errno=ENOMSG 
</li>
				<li>IPC_EXCEPT 与msgtyp&gt;0配合使用，返回队列中第一个类型不为msgtyp的消息 
</li>
				<li>IPC_NOERROR 如果队列中满足条件的消息内容大于所请求的msgsz字节，则把该消息截断，截断部分将丢失。 </li>
		</ul>
		<br />
		<p>msgrcv手册中详细给出了消息类型取不同值时(&gt;0; &lt;0; =0)，调用将返回消息队列中的哪个消息。</p>
		<p>msgrcv()解除阻塞的条件有三个：</p>
		<ol>
				<li>消息队列中有了满足条件的消息； 
</li>
				<li>msqid代表的消息队列被删除； 
</li>
				<li>调用msgrcv（）的进程被信号中断； </li>
		</ol>
		<br />
		<p>
				<b>调用返回：</b>成功返回读出消息的实际字节数，否则返回-1。 </p>
		<p>
				<b>3）int msgsnd(int msqid, struct msgbuf *msgp, int msgsz, int msgflg);</b>
				<br />向msgid代表的消息队列发送一个消息，即将发送的消息存储在msgp指向的msgbuf结构中，消息的大小由msgze指定。 </p>
		<p>对发送消息来说，有意义的msgflg标志为IPC_NOWAIT，指明在消息队列没有足够空间容纳要发送的消息时，msgsnd是否等待。造成msgsnd()等待的条件有两种：</p>
		<ul>
				<li>当前消息的大小与当前消息队列中的字节数之和超过了消息队列的总容量； 
</li>
				<li>当前消息队列的消息数（单位"个"）不小于消息队列的总容量（单位"字节数"），此时，虽然消息队列中的消息数目很多，但基本上都只有一个字节。 </li>
		</ul>
		<br />msgsnd()解除阻塞的条件有三个： 
<ol><li>不满足上述两个条件，即消息队列中有容纳该消息的空间； 
</li><li>msqid代表的消息队列被删除； 
</li><li>调用msgsnd（）的进程被信号中断； </li></ol><br /><p><b>调用返回：</b>成功返回0，否则返回-1。 </p><p><b>4）int msgctl(int msqid, int cmd, struct msqid_ds *buf);</b><br />该系统调用对由msqid标识的消息队列执行cmd操作，共有三种cmd操作：IPC_STAT、IPC_SET 、IPC_RMID。 </p><ol><li>IPC_STAT：该命令用来获取消息队列信息，返回的信息存贮在buf指向的msqid结构中； 
</li><li>IPC_SET：该命令用来设置消息队列的属性，要设置的属性存储在buf指向的msqid结构中；可设置属性包括：msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes，同时，也影响msg_ctime成员。 
</li><li>IPC_RMID：删除msqid标识的消息队列； </li></ol><br /><p><b>调用返回：</b>成功返回0，否则返回-1。 </p><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" twffan="done" /><br /><img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" twffan="done" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" twffan="done" /><br /><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" twffan="done" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/linux/l-ipc/part3/#main"><b><font color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="N10228"><span class="atitle" twffan="done">三、消息队列的限制</span></a></p><p>每个消息队列的容量（所能容纳的字节数）都有限制，该值因系统不同而不同。在后面的应用实例中，输出了redhat 8.0的限制，结果参见 <a href="http://www.ibm.com/developerworks/cn/linux/l-ipc/part3/#listing3"><font color="#996699">附录 3</font></a>。 </p><p>另一个限制是每个消息队列所能容纳的最大消息数：在redhad 8.0中，该限制是受消息队列容量制约的：消息个数要小于消息队列的容量（字节数）。</p><p>注：上述两个限制是针对每个消息队列而言的，系统对消息队列的限制还有系统范围内的最大消息队列个数，以及整个系统范围内的最大消息数。一般来说，实际开发过程中不会超过这个限制。</p><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" twffan="done" /><br /><img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" twffan="done" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" twffan="done" /><br /><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" twffan="done" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/linux/l-ipc/part3/#main"><b><font color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="N1023B"><span class="atitle" twffan="done">四、消息队列应用实例</span></a></p><p>消息队列应用相对较简单，下面实例基本上覆盖了对消息队列的所有操作，同时，程序输出结果有助于加深对前面所讲的某些规则及消息队列限制的理解。</p><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td class="code-outline"><pre class="displaycode">#include &lt;sys/types.h&gt;
#include &lt;sys/msg.h&gt;
#include &lt;unistd.h&gt;
void msg_stat(int,struct msqid_ds );
main()
{
int gflags,sflags,rflags;
key_t key;
int msgid;
int reval;
struct msgsbuf{
        int mtype;
        char mtext[1];
    }msg_sbuf;
struct msgmbuf
    {
    int mtype;
    char mtext[10];
    }msg_rbuf;
struct msqid_ds msg_ginfo,msg_sinfo;
char* msgpath="/unix/msgqueue";
key=ftok(msgpath,'a');
gflags=IPC_CREAT|IPC_EXCL;
msgid=msgget(key,gflags|00666);
if(msgid==-1)
{
    printf("msg create error\n");
    return;
}
//创建一个消息队列后，输出消息队列缺省属性
msg_stat(msgid,msg_ginfo);
sflags=IPC_NOWAIT;
msg_sbuf.mtype=10;
msg_sbuf.mtext[0]='a';
reval=msgsnd(msgid,&amp;msg_sbuf,sizeof(msg_sbuf.mtext),sflags);
if(reval==-1)
{
    printf("message send error\n");
}
//发送一个消息后，输出消息队列属性
msg_stat(msgid,msg_ginfo);
rflags=IPC_NOWAIT|MSG_NOERROR;
reval=msgrcv(msgid,&amp;msg_rbuf,4,10,rflags);
if(reval==-1)
    printf("read msg error\n");
else
    printf("read from msg queue %d bytes\n",reval);
//从消息队列中读出消息后，输出消息队列属性
msg_stat(msgid,msg_ginfo);
msg_sinfo.msg_perm.uid=8;//just a try
msg_sinfo.msg_perm.gid=8;//
msg_sinfo.msg_qbytes=16388;
//此处验证超级用户可以更改消息队列的缺省msg_qbytes
//注意这里设置的值大于缺省值
reval=msgctl(msgid,IPC_SET,&amp;msg_sinfo);
if(reval==-1)
{
    printf("msg set info error\n");
    return;
}
msg_stat(msgid,msg_ginfo);
//验证设置消息队列属性
reval=msgctl(msgid,IPC_RMID,NULL);//删除消息队列
if(reval==-1)
{
    printf("unlink msg queue error\n");
    return;
}
}
void msg_stat(int msgid,struct msqid_ds msg_info)
{
int reval;
sleep(1);//只是为了后面输出时间的方便
reval=msgctl(msgid,IPC_STAT,&amp;msg_info);
if(reval==-1)
{
    printf("get msg info error\n");
    return;
}
printf("\n");
printf("current number of bytes on queue is %d\n",msg_info.msg_cbytes);
printf("number of messages in queue is %d\n",msg_info.msg_qnum);
printf("max number of bytes on queue is %d\n",msg_info.msg_qbytes);
//每个消息队列的容量（字节数）都有限制MSGMNB，值的大小因系统而异。在创建新的消息队列时，//msg_qbytes的缺省值就是MSGMNB
printf("pid of last msgsnd is %d\n",msg_info.msg_lspid);
printf("pid of last msgrcv is %d\n",msg_info.msg_lrpid);
printf("last msgsnd time is %s", ctime(&amp;(msg_info.msg_stime)));
printf("last msgrcv time is %s", ctime(&amp;(msg_info.msg_rtime)));
printf("last change time is %s", ctime(&amp;(msg_info.msg_ctime)));
printf("msg uid is %d\n",msg_info.msg_perm.uid);
printf("msg gid is %d\n",msg_info.msg_perm.gid);
}
</pre></td></tr></tbody></table><br />程序输出结果见 <a href="http://www.ibm.com/developerworks/cn/linux/l-ipc/part3/#listing3"><font color="#996699">附录 3</font></a>。 <br /><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" twffan="done" /><br /><img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" twffan="done" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" twffan="done" /><br /><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" twffan="done" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/linux/l-ipc/part3/#main"><b><font color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="N1024E"><span class="atitle" twffan="done">小结：</span></a></p><p>消息队列与管道以及有名管道相比，具有更大的灵活性，首先，它提供有格式字节流，有利于减少开发人员的工作量；其次，消息具有类型，在实际应用中，可作为优先级使用。这两点是管道以及有名管道所不能比的。同样，消息队列可以在几个进程间复用，而不管这几个进程是否具有亲缘关系，这一点与有名管道很相似；但消息队列是随内核持续的，与有名管道（随进程持续）相比，生命力更强，应用空间更大。</p><p><b><a name="listing1">附录 1</a>： </b>在参考文献[1]中，给出了IPC随进程持续、随内核持续以及随文件系统持续的定义： </p><ol><li>随进程持续：IPC一直存在到打开IPC对象的最后一个进程关闭该对象为止。如管道和有名管道； 
</li><li>随内核持续：IPC一直持续到内核重新自举或者显示删除该对象为止。如消息队列、信号灯以及共享内存等； 
</li><li>随文件系统持续：IPC一直持续到显示删除该对象为止。 </li></ol><br /><p><b><a name="listing2">附录 2</a>： </b><br />结构msg_queue用来描述消息队列头，存在于系统空间： </p><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td class="code-outline"><pre class="displaycode">struct msg_queue {
    struct kern_ipc_perm q_perm;
    time_t q_stime;         /* last msgsnd time */
    time_t q_rtime;         /* last msgrcv time */
    time_t q_ctime;         /* last change time */
    unsigned long q_cbytes;     /* current number of bytes on queue */
    unsigned long q_qnum;       /* number of messages in queue */
    unsigned long q_qbytes;     /* max number of bytes on queue */
    pid_t q_lspid;          /* pid of last msgsnd */
    pid_t q_lrpid;          /* last receive pid */
    struct list_head q_messages;
    struct list_head q_receivers;
    struct list_head q_senders;
};
</pre></td></tr></tbody></table><br /><br /><p>结构msqid_ds用来设置或返回消息队列的信息，存在于用户空间；</p><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td class="code-outline"><pre class="displaycode">struct msqid_ds {
    struct ipc_perm msg_perm;
    struct msg *msg_first;      /* first message on queue,unused  */
    struct msg *msg_last;       /* last message in queue,unused */
    __kernel_time_t msg_stime;  /* last msgsnd time */
    __kernel_time_t msg_rtime;  /* last msgrcv time */
    __kernel_time_t msg_ctime;  /* last change time */
    unsigned long  msg_lcbytes; /* Reuse junk fields for 32 bit */
    unsigned long  msg_lqbytes; /* ditto */
    unsigned short msg_cbytes;  /* current number of bytes on queue */
    unsigned short msg_qnum;    /* number of messages in queue */
    unsigned short msg_qbytes;  /* max number of bytes on queue */
    __kernel_ipc_pid_t msg_lspid;   /* pid of last msgsnd */
    __kernel_ipc_pid_t msg_lrpid;   /* last receive pid */
};
</pre></td></tr></tbody></table><br />//可以看出上述两个结构很相似。 <br /><p><b><a name="listing3">附录 3</a>： </b>消息队列实例输出结果： </p><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td class="code-outline"><pre class="displaycode">current number of bytes on queue is 0
number of messages in queue is 0
max number of bytes on queue is 16384
pid of last msgsnd is 0
pid of last msgrcv is 0
last msgsnd time is Thu Jan  1 08:00:00 1970
last msgrcv time is Thu Jan  1 08:00:00 1970
last change time is Sun Dec 29 18:28:20 2002
msg uid is 0
msg gid is 0
//上面刚刚创建一个新消息队列时的输出
current number of bytes on queue is 1
number of messages in queue is 1
max number of bytes on queue is 16384
pid of last msgsnd is 2510
pid of last msgrcv is 0
last msgsnd time is Sun Dec 29 18:28:21 2002
last msgrcv time is Thu Jan  1 08:00:00 1970
last change time is Sun Dec 29 18:28:20 2002
msg uid is 0
msg gid is 0
read from msg queue 1 bytes
//实际读出的字节数
current number of bytes on queue is 0
number of messages in queue is 0
max number of bytes on queue is 16384   //每个消息队列最大容量（字节数）
pid of last msgsnd is 2510
pid of last msgrcv is 2510
last msgsnd time is Sun Dec 29 18:28:21 2002
last msgrcv time is Sun Dec 29 18:28:22 2002
last change time is Sun Dec 29 18:28:20 2002
msg uid is 0
msg gid is 0
current number of bytes on queue is 0
number of messages in queue is 0
max number of bytes on queue is 16388   //可看出超级用户可修改消息队列最大容量
pid of last msgsnd is 2510
pid of last msgrcv is 2510  //对操作消息队列进程的跟踪
last msgsnd time is Sun Dec 29 18:28:21 2002
last msgrcv time is Sun Dec 29 18:28:22 2002
last change time is Sun Dec 29 18:28:23 2002    //msgctl()调用对msg_ctime有影响
msg uid is 8
msg gid is 8
</pre></td></tr></tbody></table><br /><br /><br /><p><a name="resources"><span class="atitle" twffan="done">参考资料 </span></a></p><ul><li>UNIX网络编程第二卷：进程间通信，作者：W.Richard Stevens，译者：杨继张，清华大学出版社。对POSIX以及系统V消息队列都有阐述，对Linux环境下的程序开发有极大的启发意义。<br /><br /></li><li>linux内核源代码情景分析（上），毛德操、胡希明著，浙江大学出版社，给出了系统V消息队列相关的源代码分析。<br /><br /></li><li><a href="http://www.fanqiang.com/a4/b2/20010508/113315.html" target="_blank"><font color="#996699">http://www.fanqiang.com/a4/b2/20010508/113315.html</font></a>，主要阐述linux下对文件的操作，详细介绍了对文件的存取权限位，对IPC对象的存取权限同样具有很好的借鉴意义。 <br /><br /></li><li>msgget、msgsnd、msgrcv、msgctl手册<br /></li></ul><br /><br /><p><a name="author"><span class="atitle" twffan="done">关于作者</span></a></p><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td colspan="3"><img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="100%" twffan="done" /></td></tr><tr valign="top" align="left"><td><p></p></td><td><img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="4" twffan="done" /></td><td width="100%"><p>郑彦兴，男，现攻读国防科大计算机学院网络方向博士学位。您可以通过电子邮件 <a href="mailto:mlinux@163.com?cc="><font color="#5c81a7">mlinux@163.com</font></a>和他联系。 </p></td></tr></tbody></table><img src ="http://www.cppblog.com/true/aggbug/20711.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2007-03-27 16:26 <a href="http://www.cppblog.com/true/archive/2007/03/27/20711.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ldconfig几个需要注意的地方 zhuan</title><link>http://www.cppblog.com/true/archive/2007/03/27/20667.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Tue, 27 Mar 2007 00:53:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2007/03/27/20667.html</guid><wfw:comment>http://www.cppblog.com/true/comments/20667.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2007/03/27/20667.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/20667.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/20667.html</trackback:ping><description><![CDATA[
		<h4 class="TextColor1" id="subjcns!408d802a2ec73923!734" style="MARGIN-BOTTOM: 0px">ldconfig几个需要注意的地方</h4>
		<div id="msgcns!408d802a2ec73923!734" twffan="done">
				<div twffan="done">1. 往/lib和/usr/lib里面加东西，是不用修改/etc/ld.so.conf的，但是完了之后要调一下ldconfig，不然这个library会找不到</div>
				<div twffan="done">2. 想往上面两个目录以外加东西的时候，一定要修改/etc/ld.so.conf，然后再调用ldconfig，不然也会找不到</div>
				<div twffan="done">比如安装了一个mysql到/usr/local/mysql，mysql有一大堆library在/usr/local/mysql/lib下面，这时就需要在/etc/ld.so.conf下面加一行/usr/local/mysql/lib，保存过后ldconfig一下，新的library才能在程序运行时被找到。</div>
				<div twffan="done">3. 如果想在这两个目录以外放lib，但是又不想在/etc/ld.so.conf中加东西（或者是没有权限加东西）。那也可以，就是export一个全局变量LD_LIBRARY_PATH，然后运行程序的时候就会去这个目录中找library。一般来讲这只是一种临时的解决方案，在没有权限或临时需要的时候使用。</div>
				<div twffan="done">4. ldconfig做的这些东西都与运行程序时有关，跟编译时一点关系都没有。编译的时候还是该加-L就得加，不要混淆了。</div>
				<div twffan="done">5. 总之，就是不管做了什么关于library的变动后，最好都ldconfig一下，不然会出现一些意想不到的结果。不会花太多的时间，但是会省很多的事。</div>
		</div>
<img src ="http://www.cppblog.com/true/aggbug/20667.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2007-03-27 08:53 <a href="http://www.cppblog.com/true/archive/2007/03/27/20667.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>