﻿<?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++博客-网络服务器软件开发-随笔分类-linux</title><link>http://www.cppblog.com/true/category/3902.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 10 Jun 2008 05:06:34 GMT</lastBuildDate><pubDate>Tue, 10 Jun 2008 05:06:34 GMT</pubDate><ttl>60</ttl><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>2</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>0</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"