﻿<?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++博客-flyonok-随笔分类-program</title><link>http://www.cppblog.com/flyonok/category/6247.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 20 May 2008 18:50:03 GMT</lastBuildDate><pubDate>Tue, 20 May 2008 18:50:03 GMT</pubDate><ttl>60</ttl><item><title>boost</title><link>http://www.cppblog.com/flyonok/archive/2008/03/13/44325.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Wed, 12 Mar 2008 16:50:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2008/03/13/44325.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/44325.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2008/03/13/44325.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/44325.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/44325.html</trackback:ping><description><![CDATA[http://www.stlchina.org/twiki/bin/view.pl/Main/BoostChina<br>http://www.boost.org<br><br><img src ="http://www.cppblog.com/flyonok/aggbug/44325.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyonok/" target="_blank">flyonok</a> 2008-03-13 00:50 <a href="http://www.cppblog.com/flyonok/archive/2008/03/13/44325.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>wxWidgets</title><link>http://www.cppblog.com/flyonok/archive/2008/03/11/44219.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Tue, 11 Mar 2008 15:35:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2008/03/11/44219.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/44219.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2008/03/11/44219.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/44219.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/44219.html</trackback:ping><description><![CDATA[wxWidget first:http://www.codeproject.com/KB/library/wxwidgets.aspx<br>wxWidget second:http://www.codeproject.com/KB/cross-platform/Linux.aspx<br>wxWidget makefile http://www.wxwidgets.org/wiki/index.php/Makefile<br>wx-dev-c++&nbsp;&nbsp;&nbsp; http://wxdsgn.sourceforge.net/tutorials/bbcode.php<br>wx-forum&nbsp;&nbsp;&nbsp; http://wxforum.shadonet.com/viewforum.php?f=28 <br>wx-tutorial http://zetcode.com/(wxPython、winapi、java)<br><br>   <img src ="http://www.cppblog.com/flyonok/aggbug/44219.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyonok/" target="_blank">flyonok</a> 2008-03-11 23:35 <a href="http://www.cppblog.com/flyonok/archive/2008/03/11/44219.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>libpcap </title><link>http://www.cppblog.com/flyonok/archive/2008/01/04/40405.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Fri, 04 Jan 2008 08:30:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2008/01/04/40405.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/40405.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2008/01/04/40405.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/40405.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/40405.html</trackback:ping><description><![CDATA[libpcap的英文意思是 Packet Capturelibrary，即数据包捕获函数库。该库提供的C函数接口可用于需要捕获经过网络接口（只要经过该接口，目标地址不一定为本机）数据包的系统开发上。由Berkeley大学Lawrence Berkeley National Laboratory研究院的Van Jacobson、CraigLeres和Steven McCanne编写，目前的最新版本为0.4。该函数库支持Linux、Solaris和*BSD系统平台。<br><br>　　主要接口函数说明如下：<br><br>pcap_t *pcap_open_live(char *device, int snaplen,<br>int promisc, int to_ms, char *ebuf)<br><br>获得用于捕获网络数据包的数据包捕获描述字。device参数为指定打开<br>的网络设备名。snaplen参数定义捕获数据的最大字节数。promisc指定<br>是否将网络接口置于混杂模式。to_ms参数指定超时时间（毫秒）。<br>ebuf参数则仅在pcap_open_live()函数出错返回NULL时用于传递错误消<br>息。<br><br>pcap_t *pcap_open_offline(char *fname, char *ebuf)<br><br>打开以前保存捕获数据包的文件，用于读取。fname参数指定打开的文<br>件名。该文件中的数据格式与tcpdump和tcpslice兼容。"-"为标准输<br>入。ebuf参数则仅在pcap_open_offline()函数出错返回NULL时用于传<br>递错误消息。<br><br>pcap_dumper_t *pcap_dump_open(pcap_t *p, char *fname)<br><br>打开用于保存捕获数据包的文件，用于写入。fname参数为"-"时表示<br>标准输出。出错时返回NULL。p参数为调用pcap_open_offline()或<br>pcap_open_live()函数后返回的pcap结构指针。fname参数指定打开<br>的文件名。如果返回NULL，则可调用pcap_geterr()函数获取错误消<br>息。<br><br>char *pcap_lookupdev(char *errbuf)<br><br>用于返回可被pcap_open_live()或pcap_lookupnet()函数调用的网络<br>设备名指针。如果函数出错，则返回NULL，同时errbuf中存放相关的<br>错误消息。<br><br>int pcap_lookupnet(char *device, bpf_u_int32 *netp,<br>bpf_u_int32 *maskp, char *errbuf)<br><br>获得指定网络设备的网络号和掩码。netp参数和maskp参数都是<br>bpf_u_int32指针。如果函数出错，则返回-1，同时errbuf中存放相<br>关的错误消息。<br><br>int pcap_dispatch(pcap_t *p, int cnt,<br>pcap_handler callback, u_char *user)<br><br>捕获并处理数据包。cnt参数指定函数返回前所处理数据包的最大值。<br>cnt=-1表示在一个缓冲区中处理所有的数据包。cnt=0表示处理所有<br>数据包，直到产生以下错误之一：读取到EOF；超时读取。callback<br>参数指定一个带有三个参数的回调函数，这三个参数为：一个从<br>pcap_dispatch()函数传递过来的u_char指针，一个pcap_pkthdr结构<br>的指针，和一个数据包大小的u_char指针。如果成功则返回读取到的<br>字节数。读取到EOF时则返回零值。出错时则返回-1，此时可调用<br>pcap_perror()或pcap_geterr()函数获取错误消息。<br><br>int pcap_loop(pcap_t *p, int cnt,<br>pcap_handler callback, u_char *user)<br><br>功能基本与pcap_dispatch()函数相同，只不过此函数在cnt个数据包<br>被处理或出现错误时才返回，但读取超时不会返回。而如果为<br>pcap_open_live()函数指定了一个非零值的超时设置，然后调用<br>pcap_dispatch()函数，则当超时发生时pcap_dispatch()函数会返回。<br>cnt参数为负值时pcap_loop()函数将始终循环运行，除非出现错误。<br><br>void pcap_dump(u_char *user, struct pcap_pkthdr *h,<br>u_char *sp)<br><br>向调用pcap_dump_open()函数打开的文件输出一个数据包。该函数可<br>作为pcap_dispatch()函数的回调函数。<br><br>int pcap_compile(pcap_t *p, struct bpf_program *fp,<br>char *str, int optimize, bpf_u_int32 netmask)<br><br>将str参数指定的字符串编译到过滤程序中。fp是一个bpf_program结<br>构的指针，在pcap_compile()函数中被赋值。optimize参数控制结果<br>代码的优化。netmask参数指定本地网络的网络掩码。<br><br>int pcap_setfilter(pcap_t *p, struct bpf_program *fp)<br><br>指定一个过滤程序。fp参数是bpf_program结构指针，通常取自<br>pcap_compile()函数调用。出错时返回-1；成功时返回0。<br><br>u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)<br><br>返回指向下一个数据包的u_char指针。<br><br>int pcap_datalink(pcap_t *p)<br><br>返回数据链路层类型，例如DLT_EN10MB。<br><br>int pcap_snapshot(pcap_t *p)<br><br>返回pcap_open_live被调用后的snapshot参数值。<br><br>int pcap_is_swapped(pcap_t *p)<br><br>返回当前系统主机字节与被打开文件的字节顺序是否不同。<br><br>int pcap_major_version(pcap_t *p)<br><br>返回写入被打开文件所使用的pcap函数的主版本号。<br><br>int pcap_minor_version(pcap_t *p)<br><br>返回写入被打开文件所使用的pcap函数的辅版本号。<br><br>int pcap_stats(pcap_t *p, struct pcap_stat *ps)<br><br>向pcap_stat结构赋值。成功时返回0。这些数值包括了从开始<br>捕获数据以来至今共捕获到的数据包统计。如果出错或不支持<br>数据包统计，则返回-1，且可调用pcap_perror()或<br>pcap_geterr()函数来获取错误消息。<br><br>FILE *pcap_file(pcap_t *p)<br><br>返回被打开文件的文件名。<br><br>int pcap_fileno(pcap_t *p)<br><br>返回被打开文件的文件描述字号码。<br><br>void pcap_perror(pcap_t *p, char *prefix)<br><br>在标准输出设备上显示最后一个pcap库错误消息。以prefix参<br>数指定的字符串为消息头。<br><br>char *pcap_geterr(pcap_t *p)<br><br>返回最后一个pcap库错误消息。<br><br>char *pcap_strerror(int error)<br><br>如果strerror()函数不可用，则可调用pcap_strerror函数替代。<br><br>void pcap_close(pcap_t *p)<br><br>关闭p参数相应的文件，并释放资源。<br><br>void pcap_dump_close(pcap_dumper_t *p)<br><br>关闭相应的被打开文件。<br><br><br><br>
<p><span class="atitle2">网络监控</span><br>绝大多数的现代操作系统都提供了对底层网络数据包捕获的机制，在捕获机制之上可以建立网络监控（NetworkMonitoring）应用软件。网络监控也常简称为sniffer,其最初的目的在于对网络通信情况进行监控，以对网络的一些异常情况进行调试处理。但随着互连网的快速普及和网络攻击行为的频繁出现，保护网络的运行安全也成为监控软件的另一个重要目的。例如，网络监控在路由器，防火墙、入侵检查等方面使用也很广泛。除此而外，它也是一种比较有效的黑客手段，例如，美国政府安全部门的"肉食动物"计划。</p>
<p><span class="atitle2">包捕获机制</span><br>从广义的角度上看，一个包捕获机制包含三个主要部分：最底层是针对特定操作系统的包捕获机制，最高层是针对用户程序的接口，第三部分是包过滤机制。</p>
<p>不同的操作系统实现的底层包捕获机制可能是不一样的，但从形式上看大同小异。数据包常规的传输路径依次为网卡、设备驱动层、数据链路层、IP层、传输层、最后到达应用程序。而包捕获机制是在数据链路层增加一个旁路处理，对发送和接收到的数据包做过滤/缓冲等相关处理，最后直接传递到应用程序。值得注意的是，包捕获机制并不影响操作系统对数据包的网络栈处理。对用户程序而言，包捕获机制提供了一个统一的接口，使用户程序只需要简单的调用若干函数就能获得所期望的数据包。这样一来，针对特定操作系统的捕获机制对用户透明，使用户程序有比较好的可移植性。包过滤机制是对所捕获到的数据包根据用户的要求进行筛选，最终只把满足过滤条件的数据包传递给用户程序。</p>
<p><span class="atitle2">Libpcap 应用程序框架</span><br>Libpcap提供了系统独立的用户级别网络数据包捕获接口，并充分考虑到应用程序的可移植性。Libpcap 可以在绝大多数类 unix 平台下工作，参考资料A 中是对基于 libpcap 的网络应用程序的一个详细列表。在 windows 平台下，一个与libpcap 很类似的函数包 winpcap提供捕获功能，其官方网站是<a href="http://winpcap.polito.it/" target="_blank"><font color="#0000ff">http://winpcap.polito.it/</font></a>。</p>
<p>Libpcap 软件包可从 <a href="http://www.tcpdump.org/" target="_blank"><font color="#0000ff">http://www.tcpdump.org/</font></a> 下载，然后依此执行下列三条命令即可安装，但如果希望 libpcap 能在 linux 上正常工作，则必须使内核支持"packet"协议，也即在编译内核时打开配置选项 CONFIG_PACKET(选项缺省为打开)。</p>
<br>
<table bgcolor="#cccccc" border="1" cellpadding="5" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre><code><br>./configure<br>./make<br>./make install<br></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<p>libpcap 源代码由 20 多个 C 文件构成，但在Linux 系统下并不是所有文件都用到。可以通过查看命令 make 的输出了解实际所用的文件。本文所针对的libpcap 版本号为0.8.3，网络类型为常规以太网。Libpcap 应用程序从形式上看很简单，下面是一个简单的程序框架：</p>
<br>
<table bgcolor="#cccccc" border="1" cellpadding="5" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre><code><br>char * device; /* 用来捕获数据包的网络接口的名称 */<br>pcap_t * p; /* 捕获数据包句柄，最重要的数据结构 */<br>struct bpf_program fcode; /* BPF 过滤代码结构 */<br><br>/* 第一步：查找可以捕获数据包的设备 */<br>device = pcap_lookupdev(errbuf)；<br><br>/* 第二步：创建捕获句柄，准备进行捕获 */<br>p = pcap_open_live(device, 8000, 1, 500, errbuf)；<br><br>/* 第三步：如果用户设置了过滤条件，则编译和安装过滤代码 */<br>pcap_compile(p, &amp;fcode, filter_string, 0, netmask)；<br>pcap_setfilter(p, &amp;fcode)；<br><br>/* 第四步：进入（死）循环，反复捕获数据包 */<br>for( ; ; )<br>{<br>while((ptr = (char *)(pcap_next(p, &amp;hdr))) == NULL);<br><br>/* 第五步：对捕获的数据进行类型转换，转化成以太数据包类型 */<br>eth = (struct libnet_ethernet_hdr *)ptr;<br><br>/* 第六步：对以太头部进行分析，判断所包含的数据包类型，做进一步的处理 */<br>if(eth-&gt;ether_type == ntohs(ETHERTYPE_IP)) <br>&#8230;&#8230;&#8230;&#8230;<br>if(eth-&gt;ether_type == ntohs(ETHERTYPE_ARP)) <br>&#8230;&#8230;&#8230;&#8230;<br>}<br><br>/* 最后一步：关闭捕获句柄,一个简单技巧是在程序初始化时增加信号处理函数，<br>以便在程序退出前执行本条代码 */<br>pcap_close(p)；<br></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<p><span class="atitle2">检查网络设备</span><br>libpcap程序的第一步通常是在系统中找到合适的网络接口设备。网络接口在Linux网络体系中是一个很重要的概念，它是对具体网络硬件设备的一个抽象，在它的下面是具体的网卡驱动程序，而其上则是网络协议层。Linux中最常见的接口设备名 eth0 和 lo。Lo 称为回路设备，是一种逻辑意义上的设备,其主要目的是为了调试网络程序之间的通讯功能。eth0对应了实际的物理网卡，在真实网络环境下，数据包的发送和接收都要通过 eht0。如果<a class="wordstyle" href="http://www.stupai.com/computer" target="_blank"><font color="#0000ff">计算机</font></a>有多个网卡，则还可以有更多的网络接口，如eth1,eth2 等等。调用命令 ifconfig 可以列出当前所有活跃的接口及相关信息，注意对 eth0 的描述中既有物理网卡的 MAC地址，也有网络协议的 IP 地址。查看文件 /proc/net/dev 也可获得接口信息。</p>
<p>Libpcap 中检查网络设备中主要使用到的函数关系如下图：</p>
<p><br><img alt="" src="http://ziliaonet.com/tech/UploadPic/2006-7/200671419125659.gif" onload="return imgzoom(this,550)" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" border="0" height="495" width="382"></p>
<p>libpcap调用 pcap_lookupdev() 函数获得可用网络接口的设备名。首先利用函数 getifaddrs()获得所有网络接口的地址，以及对应的网络掩码、广播地址、目标地址等相关信息，再利用add_addr_to_iflist()、add_or_find_if()、get_instance() 把网络接口的信息增加到结构链表pcap_if 中，最后从链表中提取第一个接口作为捕获设备。其中 get_instanced()的功能是从设备名开始,找第一个是数字的字符,做为接口的实例号。网络接口的设备号越小，则排在链表的越前面，因此，通常函数最后返回的设备名为eth0。虽然 libpcap 可以工作在回路接口上，但显然 libpcap开发者认为捕获本机进程之间的数据包没有多大意义。在检查网络设备操作中，主要用到的数据结构和代码如下：</p>
<br>
<table bgcolor="#cccccc" border="1" cellpadding="5" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre><code><br>/* libpcap 自定义的接口信息链表 [pcap.h] */<br>struct pcap_if <br>{<br>struct pcap_if *next; <br>char *name; /* 接口设备名 */<br>char *description; /* 接口描述 */<br><br>/*接口的 IP 地址, 地址掩码, 广播地址,目的地址 */<br>struct pcap_addr addresses; <br>bpf_u_int32 flags;/* 接口的参数 */<br>};<br><br>char * pcap_lookupdev(register char * errbuf)<br>{<br>pcap_if_t *alldevs;<br>&#8230;&#8230;<br>pcap_findalldevs(&amp;alldevs, errbuf)；<br>&#8230;&#8230;<br>strlcpy(device, alldevs-&gt;name, sizeof(device));<br>}<br></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<p><span class="atitle2">打开网络设备</span><br>当设备找到后，下一步工作就是打开设备以准备捕获数据包。Libpcap 的包捕获是建立在具体的操作系统所提供的捕获机制上，而 Linux 系统随着版本的不同，所支持的捕获机制也有所不同。</p>
<p>2.0及以前的内核版本使用一个特殊的 socket 类型 SOCK_PACKET，调用形式是 socket(PF_INET,SOCK_PACKET, int protocol)，但 Linux 内核开发者明确指出这种方式已过时。Linux 在 2.2及以后的版本中提供了一种新的协议簇 PF_PACKET 来实现捕获机制。PF_PACKET 的调用形式为 socket(PF_PACKET,int socket_type, int protocol)，其中 socket 类型可以是 SOCK_RAW 和SOCK_DGRAM。SOCK_RAW 类型使得数据包从数据链路层取得后，不做任何修改直接传递给用户程序，而 SOCK_DRRAM则要对数据包进行加工(cooked)，把数据包的数据链路层头部去掉，而使用一个通用结构 sockaddr_ll 来保存链路信息。</p>
<p>使用 2.0 版本内核捕获数据包存在多个问题：首先，SOCK_PACKET 方式使用结构 sockaddr_pkt来保存数据链路层信息，但该结构缺乏包类型信息；其次，如果参数 MSG_TRUNC 传递给读包函数recvmsg()、recv()、recvfrom()等，则函数返回的数据包长度是实际读到的包数据长度，而不是数据包真正的长度。Libpcap 的开发者在源代码中明确建议不使用 2.0版本进行捕获。</p>
<p>相对 2.0 版本 SOCK_PACKET 方式，2.2 版本的 PF_PACKET方式则不存在上述两个问题。在实际应用中，用户程序显然希望直接得到"原始"的数据包，因此使用 SOCK_RAW类型最好。但在下面两种情况下，libpcap 不得不使用 SOCK_DGRAM类型，从而也必须为数据包合成一个"伪"链路层头部（sockaddr_ll）。</p>
<ul xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <li>某些类型的设备数据链路层头部不可用：例如 Linux 内核的 PPP 协议实现代码对 PPP 数据包头部的支持不可靠。
    </li>
    <li>在捕获设备为"any"时：所有设备意味着 libpcap 对所有接口进行捕获，为了使包过滤机制能在所有类型的数据包上正常工作,要求所有的数据包有相同的数据链路头部。</li>
</ul>
<p>打开网络设备的主函数是 pcap_open_live()[pcap-linux.c]，其任务就是通过给定的接口设备名，获得一个捕获句柄：结构pcap_t。pcap_t 是大多数 libpcap 函数都要用到的参数，其中最重要的属性则是上面讨论到的三种 socket方式中的某一种。首先我们看看 pcap_t 的具体构成。</p>
<br>
<table bgcolor="#cccccc" border="1" cellpadding="5" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre><code><br>struct pcap [pcap-int.h]<br>{ <br>int fd; /* 文件描述字，实际就是 socket */<br><br>/* 在 socket 上，可以使用 select() 和 poll() 等 I/O 复用类型函数 */<br>int selectable_fd; <br><br>int snapshot; /* 用户期望的捕获数据包最大长度 */<br>int linktype; /* 设备类型 */<br>int tzoff;/* 时区位置，实际上没有被使用 */<br>int offset;/* 边界对齐偏移量 */<br><br>int break_loop; /* 强制从读数据包循环中跳出的标志 */<br><br>struct pcap_sf sf; /* 数据包保存到文件的相关配置数据结构 */<br>struct pcap_md md; /* 具体描述如下 */<br><br>int bufsize; /* 读缓冲区的长度 */<br>u_char buffer; /* 读缓冲区指针 */<br>u_char *bp;<br>int cc;<br>u_char *pkt;<br><br>/* 相关抽象操作的函数指针，最终指向特定操作系统的处理函数 */<br>int(*read_op)(pcap_t *, int cnt, pcap_handler, u_char *);<br>int(*setfilter_op)(pcap_t *, struct bpf_program *);<br>int(*set_datalink_op)(pcap_t *, int);<br>int(*getnonblock_op)(pcap_t *, char *);<br>int(*setnonblock_op)(pcap_t *, int, char *);<br>int(*stats_op)(pcap_t *, struct pcap_stat *);<br>void (*close_op)(pcap_t *);<br><br>/*如果 BPF 过滤代码不能在内核中执行,则将其保存并在用户空间执行 */<br>struct bpf_program fcode; <br><br>/* 函数调用出错信息缓冲区 */<br>char errbuf[PCAP_ERRBUF_SIZE + 1]; <br><br>/* 当前设备支持的、可更改的数据链路类型的个数 */<br>int dlt_count;<br>/* 可更改的数据链路类型号链表，在 linux 下没有使用 */<br>int *dlt_list;<br><br>/* 数据包自定义头部，对数据包捕获时间、捕获长度、真实长度进行描述 [pcap.h] */<br>struct pcap_pkthdr pcap_header;<br>};<br><br>/* 包含了捕获句柄的接口、状态、过滤信息  [pcap-int.h] */<br>struct pcap_md {<br>/* 捕获状态结构  [pcap.h] */<br>struct pcap_stat stat;  <br><br>int use_bpf; /* 如果为1，则代表使用内核过滤*/ <br>u_longTotPkts; <br>u_longTotAccepted; /* 被接收数据包数目 */ <br>u_longTotDrops;/* 被丢弃数据包数目 */ <br>longTotMissed;/* 在过滤进行时被接口丢弃的数据包数目 */<br>longOrigMissed; /*在过滤进行前被接口丢弃的数据包数目*/<br>#ifdef linux<br>intsock_packet; /* 如果为 1，则代表使用 2.0 内核的 SOCK_PACKET 模式 */<br>inttimeout;/* pcap_open_live() 函数超时返回时间*/ <br>intclear_promisc; /* 关闭时设置接口为非混杂模式 */ <br>intcooked;/* 使用 SOCK_DGRAM 类型 */<br>intlo_ifindex;/* 回路设备索引号 */<br>char *device;/* 接口设备名称 */ <br><br>/* 以混杂模式打开 SOCK_PACKET 类型 socket 的 pcap_t 链表*/<br>struct pcap *next;<br>#endif<br>};<br></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<p>函数 pcap_open_live() 的调用形式是pcap_t * pcap_open_live(const char *device, int snaplen, int promisc,int to_ms, char *ebuf)，其中如果 device 为 NULL 或"any"，则对所有接口捕获，snaplen代表用户期望的捕获数据包最大长度，promisc代表设置接口为混杂模式（捕获所有到达接口的数据包，但只有在设备给定的情况下有意义），to_ms代表函数超时返回的时间。本函数的代码比较简单，其执行步骤如下：</p>
<ul xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <li>为结构 pcap_t 分配空间并根据函数入参对其部分属性进行初试化。
    </li>
    <li>分别利用函数 live_open_new() 或 live_open_old() 尝试创建 PF_PACKET 方式或 SOCK_PACKET 方式的 socket，注意函数名中一个为"new"，另一个为"old"。
    </li>
    <li>根据 socket 的方式，设置捕获句柄的读缓冲区长度，并分配空间。
    </li>
    <li>为捕获句柄 pcap_t 设置 linux 系统下的特定函数，其中最重要的是读数据包函数和设置过滤器函数。（注意到这种从抽象模式到具体模式的设计思想在 linux 源代码中也多次出现，如 VFS 文件系统）<br>handle-&gt;read_op = pcap_read_linux；handle-&gt;setfilter_op = pcap_setfilter_linux；</li>
</ul>
<p>下面我们依次分析 2.2 和 2.0 内核版本下的 socket 创建函数。</p>
<br>
<table bgcolor="#cccccc" border="1" cellpadding="5" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre><code><br>static int<br>live_open_new(pcap_t *handle, const char *device, int promisc,<br>   int to_ms, char *ebuf)<br>{<br>/* 如果设备给定,则打开一个 RAW 类型的套接字,否则,打开 DGRAM 类型的套接字 */<br>sock_fd = device ?<br>socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))<br>      : socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL));<br><br>/* 取得回路设备接口的索引 */<br>handle-&gt;md.lo_ifindex = iface_get_id(sock_fd, "lo", ebuf);<br><br>/* 如果设备给定，但接口类型未知或是某些必须工作在加工模式下的特定类型，则使用加工模式 */<br>if (device) {<br>/* 取得接口的硬件类型 */<br>arptype = iface_get_arptype(sock_fd, device, ebuf); <br><br>/* linux 使用 ARPHRD_xxx 标识接口的硬件类型，而 libpcap 使用DLT_xxx<br>来标识。本函数是对上述二者的做映射变换，设置句柄的链路层类型为<br>DLT_xxx，并设置句柄的偏移量为合适的值，使其与链路层头部之和为 4 的倍数，目的是边界对齐 */<br>map_arphrd_to_dlt(handle, arptype, 1);<br><br>/* 如果接口是前面谈到的不支持链路层头部的类型，则退而求其次，使用 SOCK_DGRAM 模式 */<br>if (handle-&gt;linktype == xxx) <br>{<br>close(sock_fd)；<br>sock_fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL));<br>}<br><br>/* 获得给定的设备名的索引 */<br>device_id = iface_get_id(sock_fd, device, ebuf);<br><br>/* 把套接字和给定的设备绑定，意味着只从给定的设备上捕获数据包 */<br>iface_bind(sock_fd, device_id, ebuf)；<br><br>} else { /* 现在是加工模式 */<br>handle-&gt;md.cooked = 1;<br>/* 数据包链路层头部为结构 sockaddr_ll， SLL 大概是结构名称的简写形式 */<br>handle-&gt;linktype = DLT_LINUX_SLL;<br>device_id = -1;<br>}<br><br>/* 设置给定设备为混杂模式 */<br>if (device &amp;&amp; promisc) <br>{<br>memset(&amp;mr, 0, sizeof(mr));<br>mr.mr_ifindex = device_id;<br>mr.mr_type = PACKET_MR_PROMISC;<br>setsockopt(sock_fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, <br>&amp;mr, sizeof(mr))；<br>}<br><br>/* 最后把创建的 socket 保存在句柄 pcap_t 中 */<br>handle-&gt;fd = sock_fd;<br>}<br><br>/* 2.0 内核下函数要简单的多，因为只有唯一的一种 socket 方式 */<br>static int<br>live_open_old(pcap_t *handle, const char *device, int promisc,<br>      int to_ms, char *ebuf)<br>{<br>/* 首先创建一个SOCK_PACKET类型的 socket */<br>handle-&gt;fd = socket(PF_INET, SOCK_PACKET, htons(ETH_P_ALL));<br><br>/* 2.0 内核下，不支持捕获所有接口，设备必须给定 */<br>if (!device) {<br>strncpy(ebuf, "pcap_open_live: The \"any\" device isn't supported on 2.0[.x]-kernel systems", PCAP_ERRBUF_SIZE);<br>break;<br>}<br><br>/* 把 socket 和给定的设备绑定 */<br>iface_bind_old(handle-&gt;fd, device, ebuf)；<br><br>/*以下的处理和 2.2 版本下的相似，有所区别的是如果接口链路层类型未知，则 libpcap 直接退出 */<br> <br>arptype = iface_get_arptype(handle-&gt;fd, device, ebuf);<br>map_arphrd_to_dlt(handle, arptype, 0);<br>if (handle-&gt;linktype == -1) {<br>snprintf(ebuf, PCAP_ERRBUF_SIZE, "unknown arptype %d", arptype);<br>break;<br>}<br><br>/* 设置给定设备为混杂模式 */<br>if (promisc) {<br>memset(&amp;ifr, 0, sizeof(ifr));<br>strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));<br>ioctl(handle-&gt;fd, SIOCGIFFLAGS, &amp;ifr)；<br>ifr.ifr_flags |= IFF_PROMISC;<br>ioctl(handle-&gt;fd, SIOCSIFFLAGS, &amp;ifr)；<br>}<br>}<br></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<p>比较上面两个函数的代码，还有两个细节上的区别。首先是 socket 与接口绑定所使用的结构：老式的绑定使用了结构 sockaddr，而新式的则使用了 2.2 内核中定义的通用链路头部层结构 sockaddr_ll。</p>
<br>
<table bgcolor="#cccccc" border="1" cellpadding="5" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre><code><br>iface_bind_old(int fd, const char *device, char *ebuf)<br>{<br>struct sockaddrsaddr;<br>memset(&amp;saddr, 0, sizeof(saddr));<br>strncpy(saddr.sa_data, device, sizeof(saddr.sa_data));<br>bind(fd, &amp;saddr, sizeof(saddr))；<br>}<br><br>iface_bind(int fd, int ifindex, char *ebuf)<br>{<br>struct sockaddr_llsll;<br>memset(&amp;sll, 0, sizeof(sll));<br>sll.sll_family = AF_PACKET;<br>sll.sll_ifindex = ifindex;<br>sll.sll_protocol= htons(ETH_P_ALL);<br>bind(fd, (struct sockaddr *) &amp;sll, sizeof(sll)；<br>}<br></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<p>第二个是在 2.2 版本中设置设备为混杂模式时，使用了函数setsockopt()，以及新的标志 PACKET_ADD_MEMBERSHIP 和结构packet_mreq。我估计这种方式主要是希望提供一个统一的调用接口，以代替传统的（混乱的）ioctl 调用。</p>
<br>
<table bgcolor="#cccccc" border="1" cellpadding="5" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre><code><br>struct packet_mreq<br>{<br>int             mr_ifindex;    /* 接口索引号 */<br>unsigned short  mr_type;       /* 要执行的操作(号) */<br>unsigned short  mr_alen;       /* 地址长度 */<br>unsigned char   mr_address[8]; /* 物理层地址 */ <br>};<br></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<p><span class="atitle2">用户应用程序接口</span><br>Libpcap 提供的用户程序接口比较简单，通过反复调用函数pcap_next()[pcap.c] 则可获得捕获到的数据包。下面是一些使用到的数据结构：</p>
<br>
<table bgcolor="#cccccc" border="1" cellpadding="5" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre><code><br>/* 单个数据包结构，包含数据包元信息和数据信息 */<br>struct singleton [pcap.c]<br>{<br>struct pcap_pkthdr hdr; /* libpcap 自定义数据包头部 */<br>const u_char * pkt; /* 指向捕获到的网络数据 */<br>};<br><br>/* 自定义头部在把数据包保存到文件中也被使用 */<br>struct pcap_pkthdr <br>{<br>struct timeval ts; /* 捕获时间戳 */ <br>bpf_u_int32 caplen; /* 捕获到数据包的长度 */<br>bpf_u_int32 len; /* 数据包的真正长度 */<br>}<br><br>/* 函数 pcap_next() 实际上是对函数 pcap_dispatch()[pcap.c] 的一个包装 */<br>const u_char * pcap_next(pcap_t *p, struct pcap_pkthdr *h)<br>{<br>struct singleton s;<br>s.hdr = h;<br><br>/*入参"1"代表收到1个数据包就返回；回调函数 pcap_oneshot() 是对结构 singleton 的属性赋值 */<br>if (pcap_dispatch(p, 1, pcap_oneshot, (u_char*)&amp;s) &lt;= 0)<br>return (0);<br>return (s.pkt); /* 返回数据包缓冲区的指针 */<br>}<br></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<p>pcap_dispatch() 简单的调用捕获句柄pcap_t 中定义的特定操作系统的读数据函数：return p-&gt;read_op(p, cnt, callback, user)。在linux 系统下，对应的读函数为 pcap_read_linux()（在创建捕获句柄时已定义[pcap-linux.c]），而pcap_read_linux() 则是直接调用pcap_read_packet()([pcap-linux.c])。</p>
<p>pcap_read_packet() 的中心任务是利用了recvfrom() 从已创建的 socket 上读数据包数据，但是考虑到 socket可能为前面讨论到的三种方式中的某一种，因此对数据缓冲区的结构有相应的处理，主要表现在加工模式下对伪链路层头部的合成。具体代码分析如下：</p>
<br>
<table bgcolor="#cccccc" border="1" cellpadding="5" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre><code><br>static int<br>pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)<br>{<br>/* 数据包缓冲区指针 */<br>u_char * bp;<br><br>/* bp 与捕获句柄 pcap_t 中 handle-&gt;buffer<br>之间的偏移量，其目的是为在加工模式捕获情况下，为合成的伪数据链路层头部留出空间 */<br>int offset;<br><br>/* PACKET_SOCKET 方式下，recvfrom() 返回 scokaddr_ll 类型，而在SOCK_PACKET 方式下，<br>返回 sockaddr 类型 */<br>#ifdef HAVE_PF_PACKET_SOCKETS <br>struct sockaddr_llfrom;<br>struct sll_header* hdrp;<br>#else<br>struct sockaddrfrom;<br>#endif<br><br>socklen_tfromlen;<br>intpacket_len, caplen;<br><br>/* libpcap 自定义的头部 */<br>struct pcap_pkthdrpcap_header;<br><br>#ifdef HAVE_PF_PACKET_SOCKETS<br>/* 如果是加工模式，则为合成的链路层头部留出空间 */<br>if (handle-&gt;md.cooked)<br>offset = SLL_HDR_LEN;<br><br>/* 其它两中方式下，链路层头部不做修改的被返回，不需要留空间 */<br>else<br>offset = 0;<br>#else<br>offset = 0;<br>#endif<br><br>bp = handle-&gt;buffer + handle-&gt;offset;<br><br>/* 从内核中接收一个数据包，注意函数入参中对 bp 的位置进行修正 */<br>packet_len = recvfrom( handle-&gt;fd, bp + offset,<br>handle-&gt;bufsize - offset, MSG_TRUNC,<br>(struct sockaddr *) &amp;from, &amp;fromlen);<br><br>#ifdef HAVE_PF_PACKET_SOCKETS<br><br>/* 如果是回路设备,则只捕获接收的数据包，而拒绝发送的数据包。显然，我们只能在 PF_PACKET<br>方式下这样做,因为 SOCK_PACKET 方式下返回的链路层地址类型为<br>sockaddr_pkt，缺少了判断数据包类型的信息。*/<br>if (!handle-&gt;md.sock_packet &amp;&amp;<br>from.sll_ifindex == handle-&gt;md.lo_ifindex &amp;&amp;<br>from.sll_pkttype == PACKET_OUTGOING)<br>return 0;<br>#endif<br><br>#ifdef HAVE_PF_PACKET_SOCKETS<br>/* 如果是加工模式，则合成伪链路层头部 */<br>if (handle-&gt;md.cooked) {<br>/* 首先修正捕包数据的长度，加上链路层头部的长度 */<br>packet_len += SLL_HDR_LEN;<br>hdrp = (struct sll_header *)bp;<br><br>/* 以下的代码分别对伪链路层头部的数据赋值 */<br>hdrp-&gt;sll_pkttype = xxx;<br>hdrp-&gt;sll_hatype = htons(from.sll_hatype);<br>hdrp-&gt;sll_halen = htons(from.sll_halen);<br>memcpy(hdrp-&gt;sll_addr, from.sll_addr, <br>(from.sll_halen &gt; SLL_ADDRLEN) ? <br>SLL_ADDRLEN : from.sll_halen);<br>hdrp-&gt;sll_protocol = from.sll_protocol;<br>}<br>#endif<br><br>/* 修正捕获的数据包的长度，根据前面的讨论，SOCK_PACKET 方式下长度可能是不准确的 */<br>caplen = packet_len;<br>if (caplen &gt; handle-&gt;snapshot)<br>caplen = handle-&gt;snapshot;<br><br>/* 如果没有使用内核级的包过滤,则在用户空间进行过滤*/<br>if (!handle-&gt;md.use_bpf &amp;&amp; handle-&gt;fcode.bf_insns) {<br>if (bpf_filter(handle-&gt;fcode.bf_insns, bp,<br>packet_len, caplen) == 0)<br>{<br>/* 没有通过过滤，数据包被丢弃 */<br>return 0;<br>}<br>}<br><br>/* 填充 libpcap 自定义数据包头部数据：捕获时间,捕获的长度,真实的长度 */<br>ioctl(handle-&gt;fd, SIOCGSTAMP, &amp;pcap_header.ts)；<br>pcap_header.caplen= caplen;<br>pcap_header.len= packet_len;<br><br>/* 累加捕获数据包数目，注意到在不同内核/捕获方式情况下数目可能不准确 */<br>handle-&gt;md.stat.ps_recv++;<br><br>/* 调用用户定义的回调函数 */<br>callback(userdata, &amp;pcap_header, bp);<br>}<br></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<p><span class="atitle2">数据包过滤机制</span><br>大量的网络监控程序目的不同，期望的数据包类型也不同，但绝大多数情况都都只需要所有数据包的一（小）部分。例如：对邮件系统进行监控可能只需要端口号为25（smtp）和 110（pop3) 的 TCP 数据包，对 DNS 系统进行监控就只需要端口号为 53 的 UDP数据包。包过滤机制的引入就是为了解决上述问题，用户程序只需简单的设置一系列过滤条件，最终便能获得满足条件的数据包。包过滤操作可以在用户空间执行，也可以在内核空间执行，但必须注意到数据包从内核空间拷贝到用户空间的开销很大，所以如果能在内核空间进行过滤，会极大的提高捕获的效率。内核过滤的优势在低速网络下表现不明显，但在高速网络下是非常突出的。在理论研究和实际应用中，包捕获和包过滤从语意上并没有严格的区分，关键在于认识到捕获数据包必然有过滤操作。基本上可以认为，包过滤机制在包捕获机制中占中心地位。</p>
<p>包过滤机制实际上是针对数据包的布尔值操作函数，如果函数最终返回true，则通过过滤，反之则被丢弃。形式上包过滤由一个或多个谓词判断的并操作（AND）和或操作（OR）构成，每一个谓词判断基本上对应了数据包的协议类型或某个特定值,例如：只需要 TCP 类型且端口为 110 的数据包或 ARP类型的数据包。包过滤机制在具体的实现上与数据包的协议类型并无多少关系，它只是把数据包简单的看成一个字节数组，而谓词判断会根据具体的协议映射到数组特定位置的值。如判断ARP类型数据包，只需要判断数组中第 13、14 个字节（以太头中的数据包类型）是否为0X0806。从理论研究的意思上看，包过滤机制是一个数学问题，或者说是一个算法问题，其中心任务是如何使用最少的判断操作、最少的时间完成过滤处理，提高过滤效率。</p>
<p><span class="atitle2">BPF</span><br>Libpcap重点使用 BPF（BSD Packet Filter）包过滤机制，BPF 于 1992年被设计出来，其设计目的主要是解决当时已存在的过滤机制效率低下的问题。BPF的工作步骤如下：当一个数据包到达网络接口时，数据链路层的驱动会把它向系统的协议栈传送。但如果 BPF 监听接口，驱动首先调用 BPF。BPF首先进行过滤操作，然后把数据包存放在过滤器相关的缓冲区中，最后设备驱动再次获得控制。注意到BPF是先对数据包过滤再缓冲，避免了类似 sun 的NIT 过滤机制先缓冲每个数据包直到用户读数据时再过滤所造成的效率问题。参考资料D是关于 BPF 设计思想最重要的文献。</p>
<p>BPF的设计思想和当时的<a class="wordstyle" href="http://www.stupai.com/computer" target="_blank"><font color="#0000ff">计算机</font></a>硬件的发展有很大联系，相对老式的过滤方式CSPF（CMU/Stanford PacketFilter）它有两大特点。1：基于寄存器的过滤机制，而不是早期内存堆栈过滤机制，2：直接使用独立的、非共享的内存缓冲区。同时，BPF在过滤算法是也有很大进步，它使用无环控制流图（CFG control flow graph）,而不是老式的布尔表达式树（booleanexpression tree）。布尔表达式树理解上比较直观，它的每一个叶子节点即是一个谓词判断，而非叶子节点则为 AND 操作或OR操作。CSPF有三个主要的缺点。1：过滤操作使用的栈在内存中被模拟，维护栈指针需要使用若干的加/减等操作，而内存操作是现代<a class="wordstyle" href="http://www.stupai.com/computer" target="_blank"><font color="#0000ff">计算机</font></a>架构的主要瓶颈。2：布尔表达式树造成了不需要的重复计算。3：不能分析数据包的变长头部。BPF 使用的CFG算法实际上是一种特殊的状态机，每一节点代表了一个谓词判断，而左右边分别对应了判断失败和成功后的跳转，跳转后又是谓词判断，这样反复操作，直到到达成功或失败的终点。CFG 算法的优点在于把对数据包的分析信息直接建立在图中，从而不需要重复计算。直观的看，CFG是一种"快速的、一直向前"的算法。</p>
<p><span class="atitle2">过滤代码的编译</span><br>BPF对 CFG 算法的代码实现非常复杂，它使用伪机器方式。BPF 伪机器是一个轻量级的，高效的状态机，对 BPF 过滤代码进行解释处理。BPF过滤代码形式为"opcode jt jf k"，分别代表了操作码和寻址方式、判断正确的跳转、判断失败的跳转、操作使用的通用数据域。BPF过滤代码从逻辑上看很类似于汇编语言，但它实际上是机器语言，注意到上述 4 个域的数据类型都是 int 和 char型。显然，由用户来写过滤代码太过复杂，因此 libpcap 允许用户书写高层的、容易理解的过滤字符串，然后将其编译为BPF代码。</p>
<p>Libpcap使用了 4 个源程序gencode.c、optimize.c、grammar.c、scanner.c完成编译操作，其中前两个实现了对过滤字符串的编译和优化，后两个主要是为编译提供从协议相关过滤条件到协议无关(的字符数组)位置信息的映射，并且它们由词汇分析器生成器 flex 和 bison 生成。参考资料C 有对此两个工具的讲解。</p>
<br>
<table bgcolor="#cccccc" border="1" cellpadding="5" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre><code><br>flex -Ppcap_ -t scanner.l &gt; $$.scanner.c; mv $$.scanner.c scanner.c<br>bison -y -p pcap_ -d grammar.y<br>mv y.tab.c grammar.c<br>mv y.tab.h tokdefs.h<br></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<p>编译过滤字符串调用了函数 pcap_compile()[getcode.c]，形式为：</p>
<br>
<table bgcolor="#cccccc" border="1" cellpadding="5" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre><code><br>int pcap_compile(pcap_t *p, struct bpf_program *program,<br>     char *buf, int optimize, bpf_u_int32 mask)<br></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<p>其中 buf 指向用户过滤字符串，编译后的 BPF 代码存在在结构 bpf_program中，标志 optimize 指示是否对 BPF 代码进行优化。</p>
<br>
<table bgcolor="#cccccc" border="1" cellpadding="5" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre><code><br>/* [pcap-bpf.h] */<br>struct bpf_program {<br>u_int bf_len; /* BPF 代码中谓词判断指令的数目 */<br>struct bpf_insn *bf_insns; /* 第一个谓词判断指令 */<br>};<br><br>/* 谓词判断指令结构，含意在前面已描述 [pcap-bpf.h] */<br>struct bpf_insn {<br>u_shortcode;<br>u_char jt;<br>u_char jf;<br>bpf_int32 k;<br>};<br></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<p><span class="atitle2">过滤代码的安装</span><br>前面我们曾经提到，在内核空间过滤数据包对整个捕获机制的效率是至关重要的。早期使用 SOCK_PACKET 方式的 Linux不支持内核过滤，因此过滤操作只能在用户空间执行（请参阅函数 pcap_read_packet() 代码）,在《UNIX网络编程(第一卷)》（参考资料 B）的第 26 章中对此有明确的描述。不过现在看起来情况已经发生改变，linux 在 PF_PACKET类型的 socket 上支持内核过滤。Linux 内核允许我们把一个名为 LPF(Linux Packet Filter) 的过滤器直接放到PF_PACKET 类型 socket 的处理过程中，过滤器在网卡接收中断执行后立即执行。LSF 基于 BPF机制，但两者在实现上有略微的不同。实际代码如下：</p>
<br>
<table bgcolor="#cccccc" border="1" cellpadding="5" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre><code><br>/* 在包捕获设备上附加 BPF 代码 [pcap-linux.c]*/<br>static int<br>pcap_setfilter_linux(pcap_t *handle, struct bpf_program *filter)<br>{<br>#ifdef SO_ATTACH_FILTER<br>struct sock_fprogfcode;<br>int can_filter_in_kernel;<br>int err = 0;<br>#endif<br><br>/* 检查句柄和过滤器结构的正确性 */<br>if (!handle)<br>return -1;<br>if (!filter) {<br>strncpy(handle-&gt;errbuf, "setfilter: No filter specified",<br>sizeof(handle-&gt;errbuf));<br>return -1;<br>}<br><br>/* 具体描述如下 */ <br>if (install_bpf_program(handle, filter) &lt; 0)<br>return -1;<br><br>/* 缺省情况下在用户空间运行过滤器,但如果在内核安装成功,则值为 1 */<br>handle-&gt;md.use_bpf = 0;<br><br><br>/* 尝试在内核安装过滤器 */<br>#ifdef SO_ATTACH_FILTER<br>#ifdef USHRT_MAX<br>if (handle-&gt;fcode.bf_len &gt; USHRT_MAX) {<br>/*过滤器代码太长，内核不支持 */<br>fprintf(stderr, "Warning: Filter too complex for kernel\n");<br>fcode.filter = NULL;<br>can_filter_in_kernel = 0;<br>} else<br>#endif /* USHRT_MAX */<br>{<br>/* linux 内核设置过滤器时使用的数据结构是 sock_fprog，而不是 BPF 的结构 bpf_program ,因此应做结构之间的转换 */<br>switch (fix_program(handle, &amp;fcode)) {<br><br>/* 严重错误，直接退出 */<br>case -1:<br>default: <br>return -1;<br><br>/* 通过检查，但不能工作在内核中 */<br>case 0: <br>can_filter_in_kernel = 0;<br>break;<br><br>/* BPF 可以在内核中工作 */<br>case 1: <br>can_filter_in_kernel = 1;<br>break;<br>}<br>}<br><br>/* 如果可以在内核中过滤，则安装过滤器到内核中 */<br>if (can_filter_in_kernel) {<br>if ((err = set_kernel_filter(handle, &amp;fcode)) == 0)<br>{<br>/* 安装成功 !!! */<br>handle-&gt;md.use_bpf = 1;<br>}<br>else if (err == -1)/* 出现非致命性错误 */<br>{<br>if (errno != ENOPROTOOPT &amp;&amp; errno != EOPNOTSUPP) {<br>fprintf(stderr, "Warning: Kernel filter failed:<br> %s\n",pcap_strerror(errno));<br>}<br>}<br>}<br><br>/* 如果不能在内核中使用过滤器，则去掉曾经可能在此 socket<br>上安装的内核过滤器。主要目的是为了避免存在的过滤器对数据包过滤的干扰 */<br>if (!handle-&gt;md.use_bpf)<br>reset_kernel_filter(handle);[pcap-linux.c]<br>#endif <br>}<br><br><br>/* 把 BPF 代码拷贝到 pcap_t 数据结构的 fcode 上 */<br>int install_bpf_program(pcap_t *p, struct bpf_program *fp)<br>{<br>size_t prog_size;<br><br>/* 首先释放可能已存在的 BPF 代码 */ <br>pcap_freecode(&amp;p-&gt;fcode);<br><br>/* 计算过滤代码的长度，分配内存空间 */<br>prog_size = sizeof(*fp-&gt;bf_insns) * fp-&gt;bf_len;<br>p-&gt;fcode.bf_len = fp-&gt;bf_len;<br>p-&gt;fcode.bf_insns = (struct bpf_insn *)malloc(prog_size);<br>if (p-&gt;fcode.bf_insns == NULL) {<br>snprintf(p-&gt;errbuf, sizeof(p-&gt;errbuf),<br>"malloc: %s", pcap_strerror(errno));<br>return (-1);<br>}<br><br>/* 把过滤代码保存在捕获句柄中 */<br>memcpy(p-&gt;fcode.bf_insns, fp-&gt;bf_insns, prog_size);<br><br>return (0);<br>}<br><br>/* 在内核中安装过滤器 */<br>static int set_kernel_filter(pcap_t *handle, struct sock_fprog *fcode)<br>{<br>int total_filter_on = 0;<br>int save_mode;<br>int ret;<br>int save_errno;<br><br>/*在设置过滤器前，socket 的数据包接收队列中可能已存在若干数据包。当设置过滤器后，<br>这些数据包极有可能不满足过滤条件，但它们不被过滤器丢弃。<br>这意味着，传递到用户空间的头几个数据包不满足过滤条件。<br>注意到在用户空间过滤这不是问题，因为用户空间的过滤器是在包进入队列后执行的。<br>Libpcap 解决这个问题的方法是在设置过滤器之前，<br>首先读完接收队列中所有的数据包。具体步骤如下。*/<br> <br>/*为了避免无限循环的情况发生（反复的读数据包并丢弃，但新的数据包不停的到达），首先设置一个过滤器，阻止所有的包进入 */<br> <br>setsockopt(handle-&gt;fd, SOL_SOCKET, SO_ATTACH_FILTER,<br>&amp;total_fcode, sizeof(total_fcode)；<br><br>/* 保存 socket 当前的属性 */<br>save_mode = fcntl(handle-&gt;fd, F_GETFL, 0);<br><br>/* 设置 socket 它为非阻塞模式 */<br>fcntl(handle-&gt;fd, F_SETFL, save_mode | O_NONBLOCK)；<br><br>/* 反复读队列中的数据包，直到没有数据包可读。这意味着接收队列已被清空 */<br>while (recv(handle-&gt;fd, &amp;drain, sizeof drain, MSG_TRUNC) &gt;= 0)；<br><br>/* 恢复曾保存的 socket 属性 */<br>fcntl(handle-&gt;fd, F_SETFL, save_mode);<br><br>/* 现在安装新的过滤器 */<br>setsockopt(handle-&gt;fd, SOL_SOCKET, SO_ATTACH_FILTER,<br>fcode, sizeof(*fcode));<br>}<br><br>/* 释放 socket 上可能有的内核过滤器 */<br>static int reset_kernel_filter(pcap_t *handle)<br>{<br>int dummy;<br>return setsockopt(handle-&gt;fd, SOL_SOCKET, SO_DETACH_FILTER,<br>&amp;dummy, sizeof(dummy));<br>}<br></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<p>linux 在安装和卸载过滤器时都使用了函数setsockopt()，其中标志SOL_SOCKET 代表了对 socket 进行设置，而 SO_ATTACH_FILTER 和SO_DETACH_FILTER 则分别对应了安装和卸载。下面是 linux 2.4.29 版本中的相关代码：</p>
<br>
<table bgcolor="#cccccc" border="1" cellpadding="5" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td>
            <pre><code><br>[net/core/sock.c]<br>#ifdef CONFIG_FILTER<br>case SO_ATTACH_FILTER:<br>&#8230;&#8230;<br>/* 把过滤条件结构从用户空间拷贝到内核空间 */<br>if (copy_from_user(&amp;fprog, optval, sizeof(fprog)))<br>break;<br>/* 在 socket 上安装过滤器 */<br>ret = sk_attach_filter(&amp;fprog, sk);<br>&#8230;&#8230;<br><br>case SO_DETACH_FILTER:<br>/* 使用自旋锁锁住 socket */<br>spin_lock_bh(&amp;sk-&gt;lock.slock);<br><br>filter = sk-&gt;filter;<br>/* 如果在 socket 上有过滤器，则简单设置为空，并释放过滤器内存 */<br>if (filter) {<br>sk-&gt;filter = NULL;<br>spin_unlock_bh(&amp;sk-&gt;lock.slock);<br>sk_filter_release(sk, filter);<br>break;<br>}<br>spin_unlock_bh(&amp;sk-&gt;lock.slock);<br>ret = -ENONET;<br>break;<br>#endif<br></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<p>上面出现的 sk_attach_filter() 定义在 net/core/filter.c，它把结构sock_fprog 转换为结构 sk_filter, 最后把此结构设置为 socket 的过滤器：sk-&gt;filter = fp。</p>
<p><span class="atitle2">其他代码</span><br>libpcap 还提供了其它若干函数，但基本上是提供辅助或扩展功能，重要性相对弱一点。我个人认为，函数 pcap_dump_open() 和 pcap_open_offline() 可能比较有用，使用它们能把在线的数据包写入文件并事后进行分析处理。</p>
<p><span class="atitle2">总结</span><br>1994年 libpcap 的第一个版本被发布，到现在已有 11 年的历史，如今libpcap 被广泛的应用在各种网络监控软件中。Libpcap最主要的优点在于平台无关性，用户程序几乎不需做任何改动就可移植到其它 unix平台上；其次，libpcap也能适应各种过滤机制，特别对BPF的支持最好。分析它的源代码，可以学习开发者优秀的设计思想和实现技巧，也能了解到（linux）操作系统的网络内核实现，对个人能力的提高有很大帮助。</p>
<p><span class="atitle2">参考资料</span><br>A：《Libpcap, winpcap, libdnet, and libnet applications and resources》</p>
<p>B：《UNIX网络编程(第一卷)》 W.Richard Stevens</p>
<p>C：《使用 lex 和 yacc 编译代码》 Peter Seebach</p>
<p>D：《The BSD Packet Filter: A New Architecture for User-level Packet Capture》 Steven McCanne and Van Jacobson</p>
<p>E：linux 联机帮助手册：socket(2)、socket(7)、packet等</p>
<p>F：《xPF Packet Filtering for Low-Cost Network Monitoring》</p>
<p>G：《Plab a packet capture and analysis architecture》</p>
<p>H：《A compiler for Packet Filters》</p>
<div class="Message" id="Message"><span style="font-weight: bold; font-size: 16px; color: #000000;">libpcap使用总结！</span></div>
<div class="Message"><span style="font-weight: bold; font-size: 16px; color: #000000;"><font face="宋体">libpcap 是一个开发sniffer的工具包。pcap：packet capture! </font>
<p>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; libpcap使用总结！<br>libpcap 是一个开发sniffer的工具包。pcap：packet capture!<br><br>libpcap的数据类型定义：<br><br>struct pcap_addr:网卡地址描述<br>{<br>&nbsp;&nbsp; &nbsp;pcap_addr * next;<br>&nbsp;&nbsp; &nbsp;sockaddr * addr;<br>&nbsp;&nbsp; &nbsp;sockaddr * netmask;<br>&nbsp;&nbsp; &nbsp;sockaddr *broadaddr;<br>&nbsp;&nbsp; &nbsp;sockaddr *dstaddr;<br>};<br>pcap_addr * next; <br>&nbsp;&nbsp; &nbsp;如果非空，指向链表中一个元素的指针；空表示链表中的最后一个元素。<br>sockaddr * addr; <br>&nbsp;&nbsp; &nbsp;指向包含一个地址的sockaddr的结构的指针。<br>sockaddr * netmask;<br>&nbsp;&nbsp; &nbsp;如果非空，指向包含相对于addr指向的地址的一个网络掩码的结构。<br>sockaddr * broadaddr;<br>&nbsp;&nbsp; &nbsp;如果非空，指向包含相对于addr指向的地址的一个广播地址，如果网络不支持广播可能为空。<br>sockaddr * dstaddr;<br>&nbsp;&nbsp; &nbsp;如果非空，指向一个相对于addr指向的源地址的目的地址，如果网络不支持点对点通讯，则为空。<br><br>struct pcap_file_header {<br>&nbsp;&nbsp; &nbsp;bpf_u_int32 magic;<br>&nbsp;&nbsp; &nbsp;u_short version_major;<br>&nbsp;&nbsp; &nbsp;u_short version_minor;<br>&nbsp;&nbsp; &nbsp;bpf_int32 thiszone;&nbsp;&nbsp; &nbsp;/* gmt to local correction */<br>&nbsp;&nbsp; &nbsp;bpf_u_int32 sigfigs;&nbsp;&nbsp; &nbsp;/* accuracy of timestamps */<br>&nbsp;&nbsp; &nbsp;bpf_u_int32 snaplen;&nbsp;&nbsp; &nbsp;/* max length saved portion of each pkt */<br>&nbsp;&nbsp; &nbsp;bpf_u_int32 linktype;&nbsp;&nbsp; &nbsp;/* data link type (LINKTYPE_*) */<br>};<br><br>bpf_u_int32 magic;<br>&nbsp;&nbsp; &nbsp;????????????????????????????????<br>u_short version_major;<br>&nbsp;&nbsp; &nbsp;Libpcap的主版本号。<br>u_shart version_minor;<br>&nbsp;&nbsp; &nbsp;Libpcap的从版本号。<br>bpf_u_int32 sigfigs;<br>&nbsp;&nbsp; &nbsp;时间戳描述。<br>bpf_u_int32 snaplen;<br>&nbsp;&nbsp; &nbsp;保存的每个pkt的分片号的最大值。<br>bpf_u_int32 linktype;<br>&nbsp;&nbsp; &nbsp;数据链的类型。<br>细节说明：<br>&nbsp;&nbsp; &nbsp;libpcap dump文件头；<br>&nbsp;&nbsp; &nbsp;libpcap dump文件中的第一个记录包含了一些标志的保存值，这些标志在打印阶段用到。这儿的很多域都是32位的int，所以compilers不用进行转化；这些文件需要具有跨层次的可交换性。<br>&nbsp;&nbsp; &nbsp;无论如何不要改变结构的层次（包括仅仅改变这个结构中域的长度）；<br><br><br>struct pcap_if { &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;/*网卡数据链的一个元素*/<br>&nbsp;&nbsp; &nbsp;struct pcap_if *next;<br>&nbsp;&nbsp; &nbsp;char *name;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;/* name to hand to "pcap_open_live()" */<br>&nbsp;&nbsp; &nbsp;char *description;&nbsp;&nbsp; &nbsp;/* textual description of interface, or NULL */<br>&nbsp;&nbsp; &nbsp;struct pcap_addr *addresses;<br>&nbsp;&nbsp; &nbsp;u_int flags;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;/* PCAP_IF_ interface flags */<br>};<br><br>pcap_if *next; <br>&nbsp;&nbsp; &nbsp;如果非空，指向链的下一个元素。如果为空是链的最后一个元素。<br>char * name;<br>&nbsp;&nbsp; &nbsp;指向一个字符串，该字符串是传给pcap_open_live()函数的设备名；<br>char * description;<br>&nbsp;&nbsp; &nbsp;如果非空，指向一个对设备的人性化的描述字符串。<br>pcap_addr *addresses;<br>&nbsp;&nbsp; &nbsp;指向网卡地址链中的第一个元素。<br>u_int flags;<br>&nbsp;&nbsp; &nbsp;PCAP_IF_ 网卡的标志。现在唯一可用的标识是PCAP_IF_LOOKBACK,它被用来标识网卡是不是lookback网卡。<br><br>struct pcap_pkthdr { &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;/*dump 文件中的数据包头*/<br>&nbsp;&nbsp; &nbsp;struct timeval ts;&nbsp;&nbsp; &nbsp;/* time stamp */<br>&nbsp;&nbsp; &nbsp;bpf_u_int32 caplen;&nbsp;&nbsp; &nbsp;/* length of portion present */<br>&nbsp;&nbsp; &nbsp;bpf_u_int32 len;&nbsp;&nbsp; &nbsp;/* length this packet (off wire) */<br>};<br>timeval ts;<br>&nbsp;&nbsp; &nbsp;数据报时间戳；<br>bpf_u_int32 caplen;<br>&nbsp;&nbsp; &nbsp;当前分片的长度；<br>dpf_u_int32 len;<br>&nbsp;&nbsp; &nbsp;这个数据报的长度；<br>细节描述：<br>&nbsp;&nbsp; &nbsp;在dump文件中的每个数据报都有这样一个报头。它用来处理不同数据报网卡的不同报头问题。<br><br>struct pcap_stat {&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;/*用来保存网卡静态变量的结构*/<br>&nbsp;&nbsp; &nbsp;u_int ps_recv;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;/* number of packets received */<br>&nbsp;&nbsp; &nbsp;u_int ps_drop;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;/* number of packets dropped */<br>&nbsp;&nbsp; &nbsp;u_int ps_ifdrop;&nbsp;&nbsp; &nbsp;/* drops by interface XXX not yet supported */<br>};<br>u_int ps_recv;<br>&nbsp;&nbsp; &nbsp;接受数据报的数目；<br>u_int ps_drop;<br>&nbsp;&nbsp; &nbsp;被驱动程序丢弃的数据报的数目；<br>u_int ps_ifdrop;<br>&nbsp;&nbsp; &nbsp;被网卡丢弃的数据报的数目；<br><br><br>struct pcap_sf {&nbsp;&nbsp; &nbsp;//pacap的savefile结构 定义<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FILE *rfile;&nbsp;&nbsp; &nbsp;//该指针指向savefile文件<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int swapped;&nbsp;&nbsp; &nbsp;//？<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int hdrsize;&nbsp;&nbsp; &nbsp;//头大小吗？<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int version_major;//主版本号<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int version_minor;//从版本号<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; u_char *base;//？<br>};<br>struct pcap_md { //？<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct pcap_stat stat;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*XXX*/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int use_bpf;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* using kernel filter */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; u_long&nbsp; TotPkts;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* can't oflow for 79 hrs on ether */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; u_long&nbsp; TotAccepted;&nbsp;&nbsp;&nbsp; /* count accepted by filter */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; u_long&nbsp; TotDrops;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* count of dropped packets */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; long&nbsp;&nbsp;&nbsp; TotMissed;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* missed by i/f during this run */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; long&nbsp;&nbsp;&nbsp; OrigMissed;&nbsp;&nbsp;&nbsp;&nbsp; /* missed by i/f before this run */<br>#ifdef linux<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp; sock_packet;&nbsp;&nbsp;&nbsp; /* using Linux 2.0 compatible interface */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp; timeout;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* timeout specified to pcap_open_live */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp; clear_promisc;&nbsp; /* must clear promiscuous mode when we close */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp; cooked;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* using SOCK_DGRAM rather than SOCK_RAW */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp; lo_ifindex;&nbsp;&nbsp;&nbsp;&nbsp; /* interface index of the loopback device */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char&nbsp;&nbsp;&nbsp; *device;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* device name */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct pcap *next;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* list of open promiscuous sock_packet pcaps */<br>#endif<br>};<br><br>struct pcap {&nbsp;&nbsp; &nbsp;//这个结构很重要<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int fd;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int snapshot;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int linktype;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int tzoff;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* timezone offset */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int offset;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* offset for proper alignment */<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct pcap_sf sf;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct pcap_md md;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * Read buffer.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int bufsize;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; u_char *buffer;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; u_char *bp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int cc;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * Place holder for pcap_next().<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; u_char *pkt;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * Placeholder for filter code if bpf not in kernel.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct bpf_program fcode;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char errbuf[PCAP_ERRBUF_SIZE];<br>};<br><br>lipcap的声明：<br>#define PCAP_VERSION_MAJOR 2<br>&nbsp;&nbsp;&nbsp; libpcap dump文件的主版本号；<br>#define PCAP_VERSION_MINOR&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; libpcap dump文件的从版本号；<br>#define PCAP_ERRBUF_SIZE&nbsp;&nbsp; 256<br>&nbsp;&nbsp;&nbsp; 用来存放libpcap出错信息的缓冲区的大小；<br>#define PCAP_IF_LOOPBACK&nbsp;&nbsp; 0x00000001<br>&nbsp;&nbsp;&nbsp; 网卡是回环网卡；<br>#define MODE_CAPT&nbsp;&nbsp; 0<br>&nbsp;&nbsp;&nbsp; 抓报模式，在调用pcap_setmode()时使用；<br>#define MODE_STAT&nbsp;&nbsp; 1<br>&nbsp;&nbsp;&nbsp; 静态模式，在调用pcap_setmode()时使用；<br><br>libpcap的类型定义：<br>typedef int bpf_int32&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; 32bit 的整形；<br>typedef&nbsp;&nbsp;&nbsp; u_int bpf_u_int32<br>&nbsp;&nbsp;&nbsp; 32bit 的无类型整形；<br>typedef pcap pcap_t<br>&nbsp;&nbsp;&nbsp; Descriptor of an open capture instance（一个打开的捕获实例的描述符？）这个结构对用户是不透明的。<br>typedef pcap_dumper pcap_dumper_t<br>&nbsp;&nbsp;&nbsp; libpcap保存文件的描述符。<br>typedef pcap_if pcap_if_t<br>&nbsp;&nbsp;&nbsp; 网卡链表的一个元素；<br>typedef pcap_addr pcap_addr_t<br>&nbsp;&nbsp;&nbsp; 网卡地址的表示；<br><br>libpcap函数描述：<br><br><br>char *pcap_lookupdev(char * errbuf);<br>&nbsp;&nbsp;&nbsp; 描述：&nbsp;&nbsp;&nbsp; 这个函数用于获取一个合适的网卡描述，以供pcap_open_liver函数和pcap_lookupnet函数使用。如果找不到网卡或者所有网卡为off，则返回null。如果一个系统中有多个网卡,那么该函数返回找到的第一个on的网卡。最后才是回环接口。回环网卡一直被忽略；<br>&nbsp;&nbsp;&nbsp; 参数：<br>&nbsp;&nbsp;&nbsp; char * errbuf &nbsp;&nbsp;&nbsp; 存放pcap_lookupdev函数的出错信息，只有在pcap_lookup失败是才有值。<br>&nbsp;&nbsp;&nbsp; 返回值：&nbsp;&nbsp;&nbsp; 如果函数执行成功，则返回一个用于描述系统上的一个网卡的描述符的指针。如果失败，返回null,errbuf中存放出错信息。<br><br><br>int pcap_lookupnet(char * device, bpf_u_int32 * netp, bpf_u_int32 * maskp,char * errbuf);<br>&nbsp;&nbsp;&nbsp; 描述：该函数用于监测网卡所在网络的网络地址和子网掩码。<br>&nbsp;&nbsp;&nbsp; 参数：<br>&nbsp;&nbsp;&nbsp; char *devic:网卡的描述符指针，由pcap_looupdev函数获取；<br>&nbsp;&nbsp;&nbsp; bpf_u_int32 *netp:存放网络地址；<br>&nbsp;&nbsp;&nbsp; bpf_u_int32 *maskp：存放子网掩码；<br>&nbsp;&nbsp;&nbsp; char * errbuf: 存放出错信息；<br>&nbsp;&nbsp;&nbsp; 返回值：如果函数执行成功，则返回值为0,否则返回值为-1,并在errbuf中存放出错信息。<br><br><br>pcap_t *pcap_open_live(char * device, int&nbsp; snaplen,int&nbsp; promisc, int&nbsp; to_ms, char * ebuf);<br>&nbsp;&nbsp;&nbsp; 描述：该函数用于打开网卡用于捕获数据报。单词live的意思就是表示一个运行的网卡（相对于offline而言）被打开了，如同一个保存有被抓数据报的文件被打开一样。在捕获数据报之前这个函数必须被执行。所有的其他的用于处理数据报捕获的函数用到的捕获数据报的描述符由该函数产生。查看pcap_open_offlin（）函数的定义，了解如何打开一个预先保存的包含数据报的文件的细节。<br>&nbsp;&nbsp;&nbsp; 参数：<br>&nbsp;&nbsp;&nbsp; char *device:网卡的描述符指针，由pcap_looupdev函数获取；<br>&nbsp;&nbsp;&nbsp; int snaplen:规定捕获的每个数据报的最大字节数；<br>&nbsp;&nbsp;&nbsp; int promisc:1为混杂模式;0为非混杂模式；<br>&nbsp;&nbsp;&nbsp; int to_ms:规定读超时的微秒（milliseconds）数；<br>&nbsp;&nbsp;&nbsp; char *ebuf:存放错误信息，只有在pcap_open_live失败时才被设置；<br>&nbsp;&nbsp;&nbsp; 返回值：如果函数成功执行，则返回一个指向数据报捕获的指针；如果错误，返回null,ebuf存放出错信息；<br><br>&nbsp;&nbsp;&nbsp; <br>int pcap_compile(pcap_t * p, struct bpf_ program *fp, char * str,int&nbsp; optimize, bpf_u_int32&nbsp; netmask);<br>&nbsp;&nbsp;&nbsp; 描述：该函数用于将str指定的规则整合到fp过滤程序中去，并生成过滤程序入口地址，用于过滤选择期望的数据报；<br>&nbsp;&nbsp;&nbsp; 参数：<br>&nbsp;&nbsp;&nbsp; pcap_t *p：pcap_open_live返回的数据报捕获的指针；<br>&nbsp;&nbsp;&nbsp; struct bpf_program *fp:指向一个子函数用于过滤，在pcap_compile()函数中被赋值；<br>&nbsp;&nbsp;&nbsp; char *str:该字符串规定过滤规则；<br>&nbsp;&nbsp;&nbsp; int optimize:规定了在结果代码上的选择是否被执行；<br>&nbsp;&nbsp;&nbsp; bpf_u_int32 netmask:该网卡的子网掩码，可以通过pcap_lookupnet()获取；<br>&nbsp;&nbsp;&nbsp; 返回值：<br>&nbsp;&nbsp;&nbsp; 如果成功执行，返回0,否则返回-1；<br><br>int pcap_loop(pcap_t * p, int&nbsp; cnt, pcap_handler&nbsp; callback,u_char * user);<br>&nbsp;&nbsp;&nbsp; 描述：<br>&nbsp;&nbsp;&nbsp; 该函数用于读取和处理数据报。既可以用来处理事先捕获的保存在文件中的数据报，也可以用来处理实时捕获的数据报；<br>&nbsp;&nbsp;&nbsp; 这个函数类似于pcap_dispatch函数，除了它继续读取数据报直至完成cnt个报的处理，或者文件处理完（在offline情况下），或者有错误发生为止。它不会在实时读超时时返回（而如果为pcap_open_live()函数指定了一个非零值的超时设置，然后调用<br>pcap_dispatch()函数，则当超时发生时pcap_dispatch()函数会返回。）<br>&nbsp;&nbsp;&nbsp; 注意第三个参数，callback是pcap_handler类型的变量。这是一个用户提供的有着三个参数的子函数。定义为：<br>void user_routine(u_char *user, struct pcap_pkthdr *phrd, u_char *pdata)<br>这三个参数中，user,是传递给pcap_dispatch（）的那个参数；phdr,是个pcap_pkthdr类型的指针，是savefile中的数据报的头指针，pdata，指向数据报数据；这个函数允许用户定义子集的数据报过滤程序；<br>&nbsp;&nbsp;&nbsp; 参数：<br>&nbsp;&nbsp;&nbsp; pcap_t * p:pcap_open_live返回的数据报捕获的指针；<br>&nbsp;&nbsp;&nbsp; int cnt:规定了函数返回前应处理的数据报数目；<br>&nbsp;&nbsp;&nbsp; pcap_handler callback:指向一个用户自定义的函数，在处理每个报后自动调用该函数进行再处理；<br>&nbsp;&nbsp;&nbsp; u_char *user:该指针用于传递给callback.(不知道有什么用？）<br>&nbsp;&nbsp;&nbsp; 返回值：<br>&nbsp;&nbsp;&nbsp; 如果函数成功执行（包括读文件时读到EOF)，则返回0.否则返回-1,那么错误信息将由函数pcap_geterr或pcap_perror给出；<br>&nbsp;&nbsp;&nbsp; 补充：callback函数：<br>&nbsp;&nbsp;&nbsp; The concept behind a callback function is fairly simple.&nbsp; Suppose I have a program that is waiting for an event of some sort.&nbsp; For the purpose of this example, lets pretend that my program wants a user to press a key on the keyboard.&nbsp; Every time they press a key, I want to call a function which then will determine that to do.&nbsp; The function I am utilizing is a callback function. <br>pcap_open_dead()<br>&nbsp;&nbsp;&nbsp; &nbsp;is used for creating a pcap_t structure to use when calling the other functions in libpcap. It is typically used when just using libpcap for compiling BPF code.<br>pcap_dump_open()<br>&nbsp;&nbsp;&nbsp; &nbsp;is called to open a ``savefile'' for writing. The name "-" in a synonym for stdout. NULL is returned on failure. p is a pcap struct as returned by pcap_open_offline() or pcap_open_live(). fname specifies the name of the file to open. If NULL is returned, pcap_geterr() can be used to get the error text.<br>pcap_setnonblock()<br>&nbsp;&nbsp;&nbsp; &nbsp;puts a capture descriptor, opened with pcap_open_live(), into ``non-blocking'' mode, or takes it out of ``non-blocking'' mode, depending on whether the nonblock argument is non-zero or zero. It has no effect on ``savefiles''. If there is an error, -1 is returned and errbuf is filled in with an appropriate error message; otherwise, 0 is returned. In ``non-blocking'' mode, an attempt to read from the capture descriptor with pcap_dispatch() will, if no packets are currently available to be read, return 0 immediately rather than blocking waiting for packets to arrive. pcap_loop() and pcap_next() will not work in ``non-blocking'' mode.<br>&nbsp;&nbsp;&nbsp; libpcap文件选读：<br>&nbsp;&nbsp;&nbsp; ethernet.c:定义了三个内联函数:<br>&nbsp;&nbsp;&nbsp; static inline int xdtoi(int):该函数将十六进值数转化为整数；<br>&nbsp;&nbsp;&nbsp; static inline int skip_space(FILE *):该函数定义了在一个文件中含有空格时，返回第一个不是'\n'的字符。<br>&nbsp;&nbsp;&nbsp; static inline int skip_line(FILE *)：<br>&nbsp;&nbsp;&nbsp; struct pcap_etherent {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; u_char addr[6];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; char name[122];<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; };<br>&nbsp;&nbsp;&nbsp; ethertype.h定义了各个协议的值。如#define ETHERTYPE_IP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x0800&nbsp; /* IP protocol */<br></p>
</span></div><img src ="http://www.cppblog.com/flyonok/aggbug/40405.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyonok/" target="_blank">flyonok</a> 2008-01-04 16:30 <a href="http://www.cppblog.com/flyonok/archive/2008/01/04/40405.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>tips of ACE</title><link>http://www.cppblog.com/flyonok/archive/2007/12/31/40030.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Sun, 30 Dec 2007 16:36:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2007/12/31/40030.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/40030.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2007/12/31/40030.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/40030.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/40030.html</trackback:ping><description><![CDATA[1、打开ACE的Trace开关<br>make CFLAGS=-DACE_NTRACE=0 <br>2、在Fedora 6,gcc4.1环境下，在platform_macros.GNU中先定义如下语句<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #000000;">no_hidden_visibility</span><span style="color: #000000;">=</span><span style="color: #000000;">1</span><span style="color: #000000;"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span></div>
<br>3、makefile example of ACE:
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">BIN&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;testlog<br>FILES&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;testlog<br>SRC</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;$(addsuffix&nbsp;.cpp,$(FILES))<br>OBJ</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;$(addsuffix&nbsp;.o,$(FILES))<br>BUILD&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;$(VBIN)<br>LIBS&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;#</span><span style="color: #000000;">-</span><span style="color: #000000;">lACE<br>LDFLAGS&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;#</span><span style="color: #000000;">-</span><span style="color: #000000;">L$(ACE_ROOT)</span><span style="color: #000000;">/</span><span style="color: #000000;">lib<br>CFLAGS</span><span style="color: #000000;">+=-</span><span style="color: #000000;">DACE_NTRACE</span><span style="color: #000000;">=</span><span style="color: #000000;">0</span><span style="color: #000000;"><br>CXXFLAGS</span><span style="color: #000000;">+=</span><span style="color: #000000;"><br>#</span><span style="color: #000000;">---------------------------------------------------</span><span style="color: #000000;"><br>#Include&nbsp;macros&nbsp;</span><span style="color: #0000ff;">and</span><span style="color: #000000;">&nbsp;targets<br>#</span><span style="color: #000000;">---------------------------------------------------</span><span style="color: #000000;"><br>include&nbsp;$(ACE_ROOT)</span><span style="color: #000000;">/</span><span style="color: #000000;">include</span><span style="color: #000000;">/</span><span style="color: #000000;">makeinclude</span><span style="color: #000000;">/</span><span style="color: #000000;">wrapper_macros.GNU<br>include&nbsp;$(ACE_ROOT)</span><span style="color: #000000;">/</span><span style="color: #000000;">include</span><span style="color: #000000;">/</span><span style="color: #000000;">makeinclude</span><span style="color: #000000;">/</span><span style="color: #000000;">macros.GNU<br>include&nbsp;$(ACE_ROOT)</span><span style="color: #000000;">/</span><span style="color: #000000;">include</span><span style="color: #000000;">/</span><span style="color: #000000;">makeinclude</span><span style="color: #000000;">/</span><span style="color: #000000;">rules.common.GNU<br>include&nbsp;$(ACE_ROOT)</span><span style="color: #000000;">/</span><span style="color: #000000;">include</span><span style="color: #000000;">/</span><span style="color: #000000;">makeinclude</span><span style="color: #000000;">/</span><span style="color: #000000;">rules.nonested.GNU<br>include&nbsp;$(ACE_ROOT)</span><span style="color: #000000;">/</span><span style="color: #000000;">include</span><span style="color: #000000;">/</span><span style="color: #000000;">makeinclude</span><span style="color: #000000;">/</span><span style="color: #000000;">rules.bin.GNU<br>include&nbsp;$(ACE_ROOT)</span><span style="color: #000000;">/</span><span style="color: #000000;">include</span><span style="color: #000000;">/</span><span style="color: #000000;">makeinclude</span><span style="color: #000000;">/</span><span style="color: #000000;">rules.local.GNU<br></span></div>
note: must set ACE_ROOT environment to the location of ACE_wrappers<br>the makefile automatic link the ACE lib libACE.so<br><br>4、compile ACEXML library,your config.h must similiar this:<br> #define ACE_GCC_HAS_TEMPLATE_INSTANTIATION_VISIBILITY_ATTRS 1<br>#incluede "ace/config-...h"<br>  <img src ="http://www.cppblog.com/flyonok/aggbug/40030.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyonok/" target="_blank">flyonok</a> 2007-12-31 00:36 <a href="http://www.cppblog.com/flyonok/archive/2007/12/31/40030.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>autoconf and automake</title><link>http://www.cppblog.com/flyonok/archive/2007/12/29/39927.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Sat, 29 Dec 2007 06:54:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2007/12/29/39927.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/39927.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2007/12/29/39927.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/39927.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/39927.html</trackback:ping><description><![CDATA[<p><a href="http://www.ibm.com/developerworks/cn/linux/l-makefile/">http://www.ibm.com/developerworks/cn/linux/l-makefile/</a></p>
<p>&nbsp;</p>
<blockquote>本文介绍了在 linux 系统中，通过 Gnu autoconf 和 automake 生成 Makefile 的方法。主要探讨了生成 Makefile 的来龙去脉及其机理，接着详细介绍了配置 Configure.in 的方法及其规则。</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><a name="N10060"><span class="atitle">引子</span></a></p>
<p>无论是在Linux还是在Unix环境中，make都是一个非常重要的编译命令。不管是自己进行项目开发还是安装应用软件，我们都经常要用到make或 make install。利用make工具，我们可以将大型的开发项目分解成为多个更易于管理的模块，对于一个包括几百个源文件的应用程序，使用make和 makefile工具就可以轻而易举的理顺各个源文件之间纷繁复杂的相互关系。</p>
<p>但是如果通过查阅make的帮助文档来手工编写Makefile,对任何程序员都是一场挑战。幸而有GNU 提供的Autoconf及Automake这两套工具使得编写makefile不再是一个难题。</p>
<p>本文将介绍如何利用 GNU Autoconf 及 Automake 这两套工具来协助我们自动产生 Makefile文件，并且让开发出来的软件可以像大多数源码包那样，只需"./configure", "make","make install" 就可以把程序安装到系统中。</p>
<br>
<p><a name="N1006F"><span class="atitle">模拟需求</span></a></p>
<p>假设源文件按如下目录存放，如图1所示，运用autoconf和automake生成makefile文件。</p>
<br><a name="N1007A"><strong>图 1文件目录结构</strong></a><br><img alt="图 1文件目录结构" src="http://www.ibm.com/developerworks/cn/linux/l-makefile/images/image001.jpg" border="0" height="190" width="217">
<p>假设src是我们源文件目录，include目录存放其他库的头文件，lib目录存放用到的库文件，然后开始按模块存放，每个模块都有一个对应的目录，模块下再分子模块，如apple、orange。每个子目录下又分core，include，shell三个目录，其中core和shell目录存放.c文件，include的存放.h文件，其他类似。</p>
<p>样例程序功能：基于多线程的数据读写保护（联系作者获取整个autoconf和automake生成的Makefile工程和源码，E-mail：<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#110;&#111;&#114;&#109;&#97;&#108;&#110;&#111;&#116;&#101;&#98;&#111;&#111;&#107;&#64;&#49;&#50;&#54;&#46;&#99;&#111;&#109;"><font color="#5c81a7">normalnotebook@126.com</font></a>）。</p>
<p><a name="N10094"><span class="atitle">工具简介</span></a></p>
<p>所必须的软件：autoconf/automake/m4/perl/libtool（其中libtool非必须）。</p>
<p>autoconf是一个用于生成可以自动地配置软件源码包，用以适应多种UNIX类系统的shell脚本工具，其中autoconf需要用到 m4，便于生成脚本。automake是一个从Makefile.am文件自动生成Makefile.in的工具。为了生成Makefile.in，automake还需用到perl，由于automake创建的发布完全遵循GNU标准，所以在创建中不需要perl。libtool是一款方便生成各种程序库的工具。</p>
<p>目前automake支持三种目录层次：flat、shallow和deep。</p>
<p>1) flat指的是所有文件都位于同一个目录中。</p>
<p>就是所有源文件、头文件以及其他库文件都位于当前目录中，且没有子目录。Termutils就是这一类。</p>
<p>2) shallow指的是主要的源代码都储存在顶层目录，其他各个部分则储存在子目录中。</p>
<p>就是主要源文件在当前目录中，而其它一些实现各部分功能的源文件位于各自不同的目录。automake本身就是这一类。</p>
<p>3) deep指的是所有源代码都被储存在子目录中；顶层目录主要包含配置信息。</p>
<p>就是所有源文件及自己写的头文件位于当前目录的一个子目录中，而当前目录里没有任何源文件。 GNU cpio和GNU tar就是这一类。</p>
<p>flat类型是最简单的，deep类型是最复杂的。不难看出，我们的模拟需求正是基于第三类deep型，也就是说我们要做挑战性的事情：)。注：我们的测试程序是基于多线程的简单程序。</p>
<p><a name="N100B8"><span class="atitle">生成 Makefile 的来龙去脉</span></a></p>
<p>首先进入 project 目录，在该目录下运行一系列命令，创建和修改几个文件，就可以生成符合该平台的Makefile文件，操作过程如下：</p>
<p>1) 运行autoscan命令</p>
<p>2) 将configure.scan 文件重命名为configure.in，并修改configure.in文件</p>
<p>3) 在project目录下新建Makefile.am文件，并在core和shell目录下也新建makefile.am文件</p>
<p>4) 在project目录下新建NEWS、 README、 ChangeLog 、AUTHORS文件</p>
<p>5) 将/usr/share/automake-1.X/目录下的depcomp和complie文件拷贝到本目录下</p>
<p>6) 运行aclocal命令</p>
<p>7) 运行autoconf命令</p>
<p>8) 运行automake -a命令</p>
<p>9) 运行./confiugre脚本</p>
<p>可以通过图2看出产生Makefile的流程，如图所示：</p>
<br><a name="N100E1"><strong>图 2生成Makefile流程图</strong></a><br><img alt="图 2生成makefile流程图" src="http://www.ibm.com/developerworks/cn/linux/l-makefile/images/image002.gif" border="0" height="338" width="468"> <br><br>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td><img alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" width="100%"><br><img alt="" src="http://www.ibm.com/i/c.gif" border="0" height="6" width="8"></td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name="N100F1"><span class="atitle">Configure.in的八股文</span></a></p>
<p>当我们利用autoscan工具生成confiugre.scan文件时，我们需要将confiugre.scan重命名为confiugre.in文件。confiugre.in调用一系列autoconf宏来测试程序需要的或用到的特性是否存在，以及这些特性的功能。</p>
<p>下面我们就来目睹一下confiugre.scan的庐山真面目：</p>
<br>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode"># Process this file with autoconf to produce a configure script.
            AC_PREREQ(2.59)
            AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
            AC_CONFIG_SRCDIR([config.h.in])
            AC_CONFIG_HEADER([config.h])
            # Checks for programs.
            AC_PROG_CC
            # Checks for libraries.
            # FIXME: Replace `main' with a function in `-lpthread':
            AC_CHECK_LIB([pthread], [main])
            # Checks for header files.
            # Checks for typedefs, structures, and compiler characteristics.
            # Checks for library functions.
            AC_OUTPUT
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>每个configure.scan文件都是以AC_INIT开头，以AC_OUTPUT结束。我们不难从文件中看出confiugre.in文件的一般布局：</p>
<br>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">AC_INIT
            测试程序
            测试函数库
            测试头文件
            测试类型定义
            测试结构
            测试编译器特性
            测试库函数
            测试系统调用
            AC_OUTPUT
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>上面的调用次序只是建议性质的，但我们还是强烈建议不要随意改变对宏调用的次序。</p>
<p>现在就开始修改该文件：</p>
<br>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">$mv configure.scan configure.in
            $vim configure.in
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>修改后的结果如下：</p>
<br>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">            #                                -*- Autoconf -*-
            # Process this file with autoconf to produce a configure script.
            AC_PREREQ(2.59)
            AC_INIT(test, 1.0, normalnotebook@126.com)
            AC_CONFIG_SRCDIR([src/ModuleA/apple/core/test.c])
            AM_CONFIG_HEADER(config.h)
            AM_INIT_AUTOMAKE(test,1.0)
            # Checks for programs.
            AC_PROG_CC
            # Checks for libraries.
            # FIXME: Replace `main' with a function in `-lpthread':
            AC_CHECK_LIB([pthread], [pthread_rwlock_init])
            AC_PROG_RANLIB
            # Checks for header files.
            # Checks for typedefs, structures, and compiler characteristics.
            # Checks for library functions.
            AC_OUTPUT([Makefile
            src/lib/Makefile
            src/ModuleA/apple/core/Makefile
            src/ModuleA/apple/shell/Makefile
            ])
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>其中要将AC_CONFIG_HEADER([config.h])修改为：AM_CONFIG_HEADER(config.h), 并加入AM_INIT_AUTOMAKE(test,1.0)。由于我们的测试程序是基于多线程的程序，所以要加入AC_PROG_RANLIB，不然运行automake命令时会出错。在AC_OUTPUT输入要创建的Makefile文件名。</p>
<p>由于我们在程序中使用了读写锁，所以需要对库文件进行检查，即AC_CHECK_LIB([pthread], [main])，该宏的含义如下：</p>
<br><img alt="" src="http://www.ibm.com/developerworks/cn/linux/l-makefile/images/table1.gif" border="0" height="166" width="537"> <br>
<p>其中，LIBS是link的一个选项，详细请参看后续的Makefile文件。由于我们在程序中使用了读写锁，所以我们测试pthread库中是否存在pthread_rwlock_init函数。</p>
<p>由于我们是基于deep类型来创建makefile文件，所以我们需要在四处创建Makefile文件。即：project目录下，lib目录下，core和shell目录下。 </p>
<p>Autoconf提供了很多内置宏来做相关的检测，限于篇幅关系，我们在这里对其他宏不做详细的解释，具体请参看参考文献1和参考文献2，也可参看autoconf信息页。</p>
<br>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td><img alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" width="100%"><br><img alt="" src="http://www.ibm.com/i/c.gif" border="0" height="6" width="8"></td>
        </tr>
    </tbody>
</table>
<table class="no-print" align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img alt="" src="http://www.ibm.com/i/c.gif" height="4" width="100%"><br></td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name="N1014D"><span class="atitle">实战Makefile.am</span></a></p>
<p>Makefile.am是一种比Makefile更高层次的规则。只需指定要生成什么目标，它由什么源文件生成，要安装到什么目录等构成。</p>
<p>表一列出了可执行文件、静态库、头文件和数据文件，四种书写Makefile.am文件个一般格式。</p>
<br><a name="N1015B"><strong>表 1Makefile.am一般格式</strong></a><br><img alt="表 1makefile.am一般格式" src="http://www.ibm.com/developerworks/cn/linux/l-makefile/images/table2.gif" border="0" height="351" width="522"> <br>
<p>对于可执行文件和静态库类型，如果只想编译，不想安装到系统中，可以用noinst_PROGRAMS代替bin_PROGRAMS，noinst_LIBRARIES代替lib_LIBRARIES。</p>
<p>Makefile.am还提供了一些全局变量供所有的目标体使用：</p>
<br><a name="N10173"><strong>表 2 Makefile.am中可用的全局变量</strong></a><br><img alt="表 2 makefile.am中可用的全局变量" src="http://www.ibm.com/developerworks/cn/linux/l-makefile/images/table3.gif" border="0" height="221" width="523"> <br>
<p>在Makefile.am中尽量使用相对路径，系统预定义了两个基本路径：</p>
<br><a name="N10188"><strong>表 3Makefile.am中可用的路径变量</strong></a><br><img alt="表 3makefile.am中可用的路径变量" src="http://www.ibm.com/developerworks/cn/linux/l-makefile/images/table4.gif" border="0" height="112" width="522"> <br>
<p>在上文中我们提到过安装路径，automake设置了默认的安装路径：</p>
<p>1) 标准安装路径</p>
<p>默认安装路径为：$(prefix) = /usr/local，可以通过./configure --prefix=&lt;new_path&gt;的方法来覆盖。</p>
<p>其它的预定义目录还包括：bindir = $(prefix)/bin, libdir = $(prefix)/lib, datadir = $(prefix)/share, sysconfdir = $(prefix)/etc等等。</p>
<p>2) 定义一个新的安装路径</p>
<p>比如test, 可定义testdir = $(prefix)/test, 然后test_DATA =test1 test2，则test1，test2会作为数据文件安装到$(prefix)/ /test目录下。</p>
<p>我们首先需要在工程顶层目录下（即project/）创建一个Makefile.am来指明包含的子目录：</p>
<br>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">SUBDIRS=src/lib src/ModuleA/apple/shell src/ModuleA/apple/core
            CURRENTPATH=$(shell /bin/pwd)
            INCLUDES=-I$(CURRENTPATH)/src/include -I$(CURRENTPATH)/src/ModuleA/apple/include
            export INCLUDES
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>由于每个源文件都会用到相同的头文件，所以我们在最顶层的Makefile.am中包含了编译源文件时所用到的头文件，并导出，见蓝色部分代码。</p>
<p>我们将lib目录下的swap.c文件编译成libswap.a文件，被apple/shell/apple.c文件调用，那么lib目录下的Makefile.am如下所示：</p>
<br>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">noinst_LIBRARIES=libswap.a
            libswap_a_SOURCES=swap.c
            INCLUDES=-I$(top_srcdir)/src/includ
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>细心的读者可能就会问：怎么表1中给出的是bin_LIBRARIES，而这里是noinst_LIBRARIES？这是因为如果只想编译，而不想安装到系统中，就用noinst_LIBRARIES代替bin_LIBRARIES，对于可执行文件就用noinst_PROGRAMS代替bin_PROGRAMS。对于安装的情况，库将会安装到$(prefix)/lib目录下，可执行文件将会安装到${prefix}/bin。如果想安装该库，则Makefile.am示例如下：</p>
<br>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">bin_LIBRARIES=libswap.a
            libswap_a_SOURCES=swap.c
            INCLUDES=-I$(top_srcdir)/src/include
            swapincludedir=$(includedir)/swap
            swapinclude_HEADERS=$(top_srcdir)/src/include/swap.h
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>最后两行的意思是将swap.h安装到${prefix}/include /swap目录下。</p>
<p>接下来，对于可执行文件类型的情况，我们将讨论如何写Makefile.am？对于编译apple/core目录下的文件，我们写成的Makefile.am如下所示：</p>
<br>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">noinst_PROGRAMS=test
            test_SOURCES=test.c
            test_LDADD=$(top_srcdir)/src/ModuleA/apple/shell/apple.o $(top_srcdir)/src/lib/libswap.a
            test_LDFLAGS=-D_GNU_SOURCE
            DEFS+=-D_GNU_SOURCE
            #LIBS=-lpthread
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>由于我们的test.c文件在链接时，需要apple.o和libswap.a文件，所以我们需要在test_LDADD中包含这两个文件。对于Linux下的信号量/读写锁文件进行编译，需要在编译选项中指明-D_GNU_SOURCE。所以在test_LDFLAGS中指明。而test_LDFLAGS只是链接时的选项，编译时同样需要指明该选项，所以需要DEFS来指明编译选项，由于DEFS已经有初始值，所以这里用+=的形式指明。从这里可以看出，Makefile.am中的语法与Makefile的语法一致，也可以采用条件表达式。如果你的程序还包含其他的库，除了用AC_CHECK_LIB宏来指明外，还可以用LIBS来指明。</p>
<p>如果你只想编译某一个文件，那么Makefile.am如何写呢？这个文件也很简单，写法跟可执行文件的差不多，如下例所示：</p>
<br>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">noinst_PROGRAMS=apple
            apple_SOURCES=apple.c
            DEFS+=-D_GNU_SOURCE
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>我们这里只是欺骗automake，假装要生成apple文件，让它为我们生成依赖关系和执行命令。所以当你运行完automake命令后，然后修改apple/shell/下的Makefile.in文件，直接将LINK语句删除，即：</p>
<br>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">&#8230;&#8230;.
            clean-noinstPROGRAMS:
            -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
            apple$(EXEEXT): $(apple_OBJECTS) $(apple_DEPENDENCIES)
            @rm -f apple$(EXEEXT)
            #$(LINK) $(apple_LDFLAGS) $(apple_OBJECTS) $(apple_LDADD) $(LIBS)
            &#8230;&#8230;.
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>通过上述处理，就可以达到我们的目的。从图1中不难看出为什么要修改Makefile.in的原因，而不是修改其他的文件。</p><img src ="http://www.cppblog.com/flyonok/aggbug/39927.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyonok/" target="_blank">flyonok</a> 2007-12-29 14:54 <a href="http://www.cppblog.com/flyonok/archive/2007/12/29/39927.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Qt install on ubuntu</title><link>http://www.cppblog.com/flyonok/archive/2007/12/27/39731.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Thu, 27 Dec 2007 06:21:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2007/12/27/39731.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/39731.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2007/12/27/39731.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/39731.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/39731.html</trackback:ping><description><![CDATA[<p>I&#8217;ve received a few queries about how to compile Qt programs in Ubuntu, so I wrote a short tutorial on compiling such a program in Qt.<br>Qt Logo</p>
<p>First make sure you remove all the qt3 dev tools so that your program isn&#8217;t compiled using qt3. It is safe and even recommended to leave the qt3 libraries installed since they may be required for other packages.</p>
<p>$ sudo apt-get remove qt3-dev-tools</p>
<p>Install the qt4 libraries and tools:</p>
<p><br>$ sudo apt-get install libqt4-core libqt4-gui libqt4-sql<br>$ sudo apt-get install libqt4-qt3support libqt4-dev qt4-dev-tools</p>
<p>If you&#8217;re a developer, you&#8217;ll want the gui tools that make development much easier.</p>
<p>$ sudo apt-get install qt4-designer qt4-qtconfig qt4-doc</p>
<p>This will install designer, assistant, and qtconfig.</p>
<p>Now that all the packages are installed, you should be able to compile programs. Many times the program you want to download will reside in a repository (usually CVS or Subversion) so you&#8217;ll need to install these packages.</p>
<p>$ sudo apt-get install subversion cvs</p>
<p>Download a program and try it out (or use the program you were having trouble with that caused you to come here).</p>
<p>$ svn co <a href="http://svn.jasonandshawnda.com/CulinaryManagement">http://svn.jasonandshawnda.com/CulinaryManagement</a></p>
<p>Compile the program:</p>
<p><br>$ cd CulinaryManagement<br>$ qmake<br>$ make</p>
<p>&nbsp;</p><img src ="http://www.cppblog.com/flyonok/aggbug/39731.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyonok/" target="_blank">flyonok</a> 2007-12-27 14:21 <a href="http://www.cppblog.com/flyonok/archive/2007/12/27/39731.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Qt--help</title><link>http://www.cppblog.com/flyonok/archive/2007/12/26/39701.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Wed, 26 Dec 2007 09:54:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2007/12/26/39701.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/39701.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2007/12/26/39701.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/39701.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/39701.html</trackback:ping><description><![CDATA[<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #008000;">
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #008000;">#</span><span style="color: #008000;">include&nbsp;&lt;QApplication&gt;</span><span style="color: #008000;"><br>#</span><span style="color: #008000;">include&nbsp;&lt;QFont&gt;</span><span style="color: #008000;"><br>#</span><span style="color: #008000;">include&nbsp;&lt;QPushButton&gt;</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br>&nbsp;int&nbsp;main(int&nbsp;argc</span><span style="color: #000000;">,</span><span style="color: #000000;">&nbsp;char&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">argv[])<br>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;QApplication&nbsp;app(argc</span><span style="color: #000000;">,</span><span style="color: #000000;">&nbsp;argv);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;QPushButton&nbsp;quit(</span><span style="color: #000000;">"</span><span style="color: #000000;">Quit</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;quit</span><span style="color: #000000;">.</span><span style="color: #000000;">resize(</span><span style="color: #000000;">75</span><span style="color: #000000;">,</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">30</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;quit</span><span style="color: #000000;">.</span><span style="color: #000000;">setFont(QFont(</span><span style="color: #000000;">"</span><span style="color: #000000;">Times</span><span style="color: #000000;">"</span><span style="color: #000000;">,</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">18</span><span style="color: #000000;">,</span><span style="color: #000000;">&nbsp;QFont</span><span style="color: #000000;">::</span><span style="color: #000000;">Bold));<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;QObject</span><span style="color: #000000;">::</span><span style="color: #000000;">connect(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">quit</span><span style="color: #000000;">,</span><span style="color: #000000;">&nbsp;SIGNAL(clicked())</span><span style="color: #000000;">,</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">app</span><span style="color: #000000;">,</span><span style="color: #000000;">&nbsp;SLOT(quit()));<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;quit</span><span style="color: #000000;">.</span><span style="color: #000000;">show();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;app</span><span style="color: #000000;">.</span><span style="color: #008080;">exec</span><span style="color: #000000;">();<br>&nbsp;}</span></div>
<br>1、</span>QObject::connect(&amp;quit, SIGNAL(clicked()), &amp;app, SLOT(quit()));<br>
<p><a href="file:///F:/Qt/qt_src-4.2.2/doc/html/qobject.html#connect">QObject::connect</a>() is perhaps <em>the</em> most central feature of Qt. Note that <a href="file:///F:/Qt/qt_src-4.2.2/doc/html/qobject.html#connect">connect()</a> is a static function in <a href="file:///F:/Qt/qt_src-4.2.2/doc/html/qobject.html">QObject</a>. Do not confuse it with the <tt>connect()</tt> function in the Berkeley socket library.</p>
<p>This <a href="file:///F:/Qt/qt_src-4.2.2/doc/html/qobject.html#connect">connect()</a> call establishes a one-way connection between two Qt objects (objects that inherit <a href="file:///F:/Qt/qt_src-4.2.2/doc/html/qobject.html">QObject</a>, directly or indirectly). Every Qt object can have both <tt>signals</tt> (to send messages) and <tt>slots</tt> (to receive messages). All widgets are Qt objects, since they inherit <a href="file:///F:/Qt/qt_src-4.2.2/doc/html/qwidget.html">QWidget</a>, which in turn inherits <a href="file:///F:/Qt/qt_src-4.2.2/doc/html/qobject.html">QObject</a>.</p>
<br><span style="color: #000000;"></span></div>
<br> <img src ="http://www.cppblog.com/flyonok/aggbug/39701.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyonok/" target="_blank">flyonok</a> 2007-12-26 17:54 <a href="http://www.cppblog.com/flyonok/archive/2007/12/26/39701.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C/C++ 通用 Makefile</title><link>http://www.cppblog.com/flyonok/archive/2007/12/24/39504.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Mon, 24 Dec 2007 05:52:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2007/12/24/39504.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/39504.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2007/12/24/39504.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/39504.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/39504.html</trackback:ping><description><![CDATA[[size=5]C/C++&nbsp;通用&nbsp;Makefile[/size] <br>[color=green]Generic&nbsp;Makefile&nbsp;for&nbsp;C/C++&nbsp;Program[/color] <br><br>================================================== <br>Keywords:&nbsp;Makefile,&nbsp;make,&nbsp;Generic,&nbsp;C/C++ <br>Author:&nbsp;&nbsp;&nbsp;whyglinux&nbsp;(whyglinux&nbsp;AT&nbsp;hotmail&nbsp;DOT&nbsp;com) <br>Date:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2006-03-04 <br>================================================== <br><br>本文提供了一个用于对&nbsp;C/C++&nbsp;程序进行编译和连接以产生可执行程序的通用&nbsp;Makefile。 <br><br>在使用&nbsp;Makefile&nbsp;之前，只需对它进行一些简单的设置即可；而且一经设置，即使以后对源程序文件有所增减一般也不再需要改动&nbsp;Makefile。因此，即便是一个没有学习过&nbsp;Makefile&nbsp;书写规则的人，也可以为自己的&nbsp;C/C++&nbsp;程序快速建立一个可工作的&nbsp;Makefile。 <br><br>这个&nbsp;Makefile&nbsp;可以在&nbsp;GNU&nbsp;Make&nbsp;和&nbsp;GCC&nbsp;编译器下正常工作。但是不能保证对于其它版本的&nbsp;Make&nbsp;和编译器也能正常工作。 <br><br>如果你发现了本文中的错误，或者对本文有什么感想或建议，可通过&nbsp;whyglinux&nbsp;AT&nbsp;hotmail&nbsp;DOT&nbsp;com&nbsp;邮箱和作者联系。&nbsp; <br><br>此&nbsp;Makefile&nbsp;的使用方法如下： <br>[list=1][*]程序目录的组织 <br>尽量将自己的源程序集中在一个目录中，并且把&nbsp;Makefile&nbsp;和源程序放在一起，这样用起来比较方便。当然，也可以将源程序分类存放在不同的目录中。 <br><br>在程序目录中创建一个名为&nbsp;Makefile&nbsp;的文本文件，将后面列出的&nbsp;Makefile&nbsp;的内容复制到这个文件中。（注意：在复制的过程中，Makfile&nbsp;中各命令前面的&nbsp;Tab&nbsp;字符有可能被转换成若干个空格。这种情况下需要把&nbsp;Makefile&nbsp;命令前面的这些空格替换为一个&nbsp;Tab。） <br><br>将当前工作目录切换到&nbsp;Makefile&nbsp;所在的目录。目前，这个&nbsp;Makefile&nbsp;只支持在当前目录中的调用，不支持当前目录和&nbsp;Makefile&nbsp;所在的路径不是同一目录的情况。 <br><br>[*]指定可执行文件 <br>程序编译和连接成功后产生的可执行文件在&nbsp;Makefile&nbsp;中的&nbsp;PROGRAM&nbsp;变量中设定。这一项不能为空。为自己程序的可执行文件起一个有意义的名子吧。 <br><br>[*]指定源程序 <br>要编译的源程序由其所在的路径和文件的扩展名两项来确定。由于头文件是通过包含来使用的，所以在这里说的源程序不应包含头文件。 <br><br>程序所在的路径在&nbsp;SRCDIRS&nbsp;中设定。如果源程序分布在不同的目录中，那么需要在&nbsp;SRCDIRS&nbsp;中一一指定，并且路径名之间用空格分隔。 <br><br>在&nbsp;SRCEXTS&nbsp;中指定程序中使用的文件类型。C/C++&nbsp;程序的扩展名一般有比较固定的几种形式：.c、.C、.cc、.cpp、.CPP、.c++、.cp、或者.cxx（参见&nbsp;man&nbsp;gcc）。扩展名决定了程序是&nbsp;C&nbsp;还是&nbsp;C++&nbsp;程序：.c&nbsp;是&nbsp;C&nbsp;程序，其它扩展名表示&nbsp;C++&nbsp;程序。一般固定使用其中的一种扩展名即可。但是也有可能需要使用多种扩展名，这可以在&nbsp;SOURCE_EXT&nbsp;中一一指定，各个扩展名之间用空格分隔。 <br><br>虽然并不常用，但是&nbsp;C&nbsp;程序也可以被作为&nbsp;C++&nbsp;程序编译。这可以通过在&nbsp;Makefile&nbsp;中设置&nbsp;CC&nbsp;=&nbsp;$(CXX)&nbsp;和&nbsp;CFLAGS&nbsp;=&nbsp;$(CXXFLAGS)&nbsp;两项即可实现。 <br><br>这个&nbsp;Makefile&nbsp;支持&nbsp;C、C++&nbsp;以及&nbsp;C/C++&nbsp;混合三种编译方式： <br>[list][*]如果只指定&nbsp;.c&nbsp;扩展名，那么这是一个&nbsp;C&nbsp;程序，用&nbsp;$(CC)&nbsp;表示的编译命令进行编译和连接。 <br>[*]如果指定的是除&nbsp;.c&nbsp;之外的其它扩展名（如&nbsp;.cc、.cpp、.cxx&nbsp;等），那么这是一个&nbsp;C++&nbsp;程序，用&nbsp;$(CXX)&nbsp;进行编译和连接。 <br>[*]如果既指定了&nbsp;.c，又指定了其它&nbsp;C++&nbsp;扩展名，那么这是&nbsp;C/C++&nbsp;混合程序，将用&nbsp;$(CC)&nbsp;编译其中的&nbsp;C&nbsp;程序，用&nbsp;$(CXX)&nbsp;编译其中的&nbsp;C++&nbsp;程序，最后再用&nbsp;$(CXX)&nbsp;连接程序。 <br>[/list] <br>这些工作都是&nbsp;make&nbsp;根据在&nbsp;Makefile&nbsp;中提供的程序文件类型（扩展名）自动判断进行的，不需要用户干预。 <br><br>[*]指定编译选项 <br>编译选项由三部分组成：预处理选项、编译选项以及连接选项，分别由&nbsp;CPPFLAGS、CFLAGS与CXXFLAGS、LDFLAGS&nbsp;指定。 <br><br>CPPFLAGS&nbsp;选项可参考&nbsp;C&nbsp;预处理命令&nbsp;cpp&nbsp;的说明，但是注意不能包含&nbsp;-M&nbsp;以及和&nbsp;-M&nbsp;有关的选项。如果是&nbsp;C/C++&nbsp;混合编程，也可以在这里设置&nbsp;C/C++&nbsp;的一些共同的编译选项。 <br><br>CFLAGS&nbsp;和&nbsp;CXXFLAGS&nbsp;两个变量通常用来指定编译选项。前者仅仅用于指定&nbsp;C&nbsp;程序的编译选项，后者仅仅用于指定&nbsp;C++&nbsp;程序的编译选项。其实也可以在两个变量中指定一些预处理选项（即一些本来应该放在&nbsp;CPPFLAGS&nbsp;中的选项），和&nbsp;CPPFLAGS&nbsp;并没有明确的界限。 <br><br>连接选项在&nbsp;LDFLAGS&nbsp;中指定。如果只使用&nbsp;C/C++&nbsp;标准库，一般没有必要设置。如果使用了非标准库，应该在这里指定连接需要的选项，如库所在的路径、库名以及其它联接选项。 <br><br>现在的库一般都提供了一个相应的&nbsp;.pc&nbsp;文件来记录使用库所需要的预编译选项、编译选项和连接选项等信息，通过&nbsp;pkg-config&nbsp;可以动态提取这些选项。与由用户显式指定各个选项相比，使用&nbsp;pkg-config&nbsp;来访问库提供的选项更方便、更具通用性。在后面可以看到一个&nbsp;GTK+&nbsp;程序的例子，其编译和连接选项的指定就是用&nbsp;pkg-config&nbsp;实现的。 <br><br>[*]编译和连接 <br>上面的各项设置好之后保存&nbsp;Makefile&nbsp;文件。执行&nbsp;make&nbsp;命令，程序就开始编译了。 <br><br>命令&nbsp;make&nbsp;会根据&nbsp;Makefile&nbsp;中设置好的路径和文件类型搜索源程序文件，然后根据文件的类型调用相应的编译命令、使用相应的编译选项对程序进行编译。 <br><br>编译成功之后程序的连接会自动进行。如果没有错误的话最终会产生程序的可执行文件。 <br><br>注意：在对程序编译之后，会产生和源程序文件一一对应的&nbsp;.d&nbsp;文件。这是表示依赖关系的文件，通过它们&nbsp;make&nbsp;决定在源程序文件变动之后要进行哪些更新。为每一个源程序文件建立相应的&nbsp;.d&nbsp;文件这也是&nbsp;GNU&nbsp;Make&nbsp;推荐的方式。 <br><br>[*]Makefile&nbsp;目标（Targets） <br>下面是关于这个&nbsp;Makefile&nbsp;提供的目标以及它所完成的功能： <br>[list][*]make <br>编译和连接程序。相当于&nbsp;make&nbsp;all。 <br>[*]make&nbsp;objs <br>仅仅编译程序产生&nbsp;.o&nbsp;目标文件，不进行连接（一般很少单独使用）。 <br>[*]make&nbsp;clean <br>删除编译产生的目标文件和依赖文件。 <br>[*]make&nbsp;cleanall <br>删除目标文件、依赖文件以及可执行文件。 <br>[*]make&nbsp;rebuild <br>重新编译和连接程序。相当于&nbsp;make&nbsp;clean&nbsp;&amp;&amp;&nbsp;make&nbsp;all。 <br>[/list][/list] <br>关于这个&nbsp;Makefile&nbsp;的实现原理不准备详细解释了。如果有兴趣的话，可参考文末列出的&#8220;参考资料&#8221;。 <br><br>Makefile&nbsp;的内容如下： <br>
<pre>###############################################################################
<br>#
<br>#&nbsp;Generic&nbsp;Makefile&nbsp;for&nbsp;C/C++&nbsp;Program
<br>#
<br>#&nbsp;Author:&nbsp;whyglinux&nbsp;(whyglinux&nbsp;AT&nbsp;hotmail&nbsp;DOT&nbsp;com)
<br>#&nbsp;Date:&nbsp;&nbsp;&nbsp;2006/03/04
<br>
<br>#&nbsp;Description:
<br>#&nbsp;The&nbsp;makefile&nbsp;searches&nbsp;in&nbsp;&lt;SRCDIRS&gt;&nbsp;directories&nbsp;for&nbsp;the&nbsp;source&nbsp;files
<br>#&nbsp;with&nbsp;extensions&nbsp;specified&nbsp;in&nbsp;&lt;SOURCE_EXT&gt;,&nbsp;then&nbsp;compiles&nbsp;the&nbsp;sources
<br>#&nbsp;and&nbsp;finally&nbsp;produces&nbsp;the&nbsp;&lt;PROGRAM&gt;,&nbsp;the&nbsp;executable&nbsp;file,&nbsp;by&nbsp;linking
<br>#&nbsp;the&nbsp;objectives.
<br>
<br>#&nbsp;Usage:
<br>#&nbsp;&nbsp;&nbsp;$&nbsp;make&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;compile&nbsp;and&nbsp;link&nbsp;the&nbsp;program.
<br>#&nbsp;&nbsp;&nbsp;$&nbsp;make&nbsp;objs&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;compile&nbsp;only&nbsp;(no&nbsp;linking.&nbsp;Rarely&nbsp;used).
<br>#&nbsp;&nbsp;&nbsp;$&nbsp;make&nbsp;clean&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;clean&nbsp;the&nbsp;objectives&nbsp;and&nbsp;dependencies.
<br>#&nbsp;&nbsp;&nbsp;$&nbsp;make&nbsp;cleanall&nbsp;&nbsp;clean&nbsp;the&nbsp;objectives,&nbsp;dependencies&nbsp;and&nbsp;executable.
<br>#&nbsp;&nbsp;&nbsp;$&nbsp;make&nbsp;rebuild&nbsp;&nbsp;&nbsp;rebuild&nbsp;the&nbsp;program.&nbsp;The&nbsp;same&nbsp;as&nbsp;make&nbsp;clean&nbsp;&amp;&amp;&nbsp;make&nbsp;all.
<br>#==============================================================================
<br>
<br>##&nbsp;Customizing&nbsp;Section:&nbsp;adjust&nbsp;the&nbsp;following&nbsp;if&nbsp;necessary.
<br>##=============================================================================
<br>
<br>#&nbsp;The&nbsp;executable&nbsp;file&nbsp;name.
<br>#&nbsp;It&nbsp;must&nbsp;be&nbsp;specified.
<br>#&nbsp;PROGRAM&nbsp;&nbsp;&nbsp;:=&nbsp;a.out&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;the&nbsp;executable&nbsp;name
<br>PROGRAM&nbsp;&nbsp;&nbsp;:=
<br>
<br>#&nbsp;The&nbsp;directories&nbsp;in&nbsp;which&nbsp;source&nbsp;files&nbsp;reside.
<br>#&nbsp;At&nbsp;least&nbsp;one&nbsp;path&nbsp;should&nbsp;be&nbsp;specified.
<br>#&nbsp;SRCDIRS&nbsp;&nbsp;&nbsp;:=&nbsp;.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;current&nbsp;directory
<br>SRCDIRS&nbsp;&nbsp;&nbsp;:=
<br>
<br>#&nbsp;The&nbsp;source&nbsp;file&nbsp;types&nbsp;(headers&nbsp;excluded).
<br>#&nbsp;At&nbsp;least&nbsp;one&nbsp;type&nbsp;should&nbsp;be&nbsp;specified.
<br>#&nbsp;The&nbsp;valid&nbsp;suffixes&nbsp;are&nbsp;among&nbsp;of&nbsp;.c,&nbsp;.C,&nbsp;.cc,&nbsp;.cpp,&nbsp;.CPP,&nbsp;.c++,&nbsp;.cp,&nbsp;or&nbsp;.cxx.
<br>#&nbsp;SRCEXTS&nbsp;&nbsp;&nbsp;:=&nbsp;.c&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;C&nbsp;program
<br>#&nbsp;SRCEXTS&nbsp;&nbsp;&nbsp;:=&nbsp;.cpp&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;C++&nbsp;program
<br>#&nbsp;SRCEXTS&nbsp;&nbsp;&nbsp;:=&nbsp;.c&nbsp;.cpp&nbsp;#&nbsp;C/C++&nbsp;program
<br>SRCEXTS&nbsp;&nbsp;&nbsp;:=
<br>
<br>#&nbsp;The&nbsp;flags&nbsp;used&nbsp;by&nbsp;the&nbsp;cpp&nbsp;(man&nbsp;cpp&nbsp;for&nbsp;more).
<br>#&nbsp;CPPFLAGS&nbsp;&nbsp;:=&nbsp;-Wall&nbsp;-Werror&nbsp;#&nbsp;show&nbsp;all&nbsp;warnings&nbsp;and&nbsp;take&nbsp;them&nbsp;as&nbsp;errors
<br>CPPFLAGS&nbsp;&nbsp;:=
<br>
<br>#&nbsp;The&nbsp;compiling&nbsp;flags&nbsp;used&nbsp;only&nbsp;for&nbsp;C.
<br>#&nbsp;If&nbsp;it&nbsp;is&nbsp;a&nbsp;C++&nbsp;program,&nbsp;no&nbsp;need&nbsp;to&nbsp;set&nbsp;these&nbsp;flags.
<br>#&nbsp;If&nbsp;it&nbsp;is&nbsp;a&nbsp;C&nbsp;and&nbsp;C++&nbsp;merging&nbsp;program,&nbsp;set&nbsp;these&nbsp;flags&nbsp;for&nbsp;the&nbsp;C&nbsp;parts.
<br>CFLAGS&nbsp;&nbsp;&nbsp;&nbsp;:=
<br>CFLAGS&nbsp;&nbsp;&nbsp;&nbsp;+=
<br>
<br>#&nbsp;The&nbsp;compiling&nbsp;flags&nbsp;used&nbsp;only&nbsp;for&nbsp;C++.
<br>#&nbsp;If&nbsp;it&nbsp;is&nbsp;a&nbsp;C&nbsp;program,&nbsp;no&nbsp;need&nbsp;to&nbsp;set&nbsp;these&nbsp;flags.
<br>#&nbsp;If&nbsp;it&nbsp;is&nbsp;a&nbsp;C&nbsp;and&nbsp;C++&nbsp;merging&nbsp;program,&nbsp;set&nbsp;these&nbsp;flags&nbsp;for&nbsp;the&nbsp;C++&nbsp;parts.
<br>CXXFLAGS&nbsp;&nbsp;:=
<br>CXXFLAGS&nbsp;&nbsp;+=
<br>
<br>#&nbsp;The&nbsp;library&nbsp;and&nbsp;the&nbsp;link&nbsp;options&nbsp;(&nbsp;C&nbsp;and&nbsp;C++&nbsp;common).
<br>LDFLAGS&nbsp;&nbsp;&nbsp;:=
<br>LDFLAGS&nbsp;&nbsp;&nbsp;+=
<br>
<br>##&nbsp;Implict&nbsp;Section:&nbsp;change&nbsp;the&nbsp;following&nbsp;only&nbsp;when&nbsp;necessary.
<br>##=============================================================================
<br>#&nbsp;The&nbsp;C&nbsp;program&nbsp;compiler.&nbsp;Uncomment&nbsp;it&nbsp;to&nbsp;specify&nbsp;yours&nbsp;explicitly.
<br>#CC&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;gcc
<br>
<br>#&nbsp;The&nbsp;C++&nbsp;program&nbsp;compiler.&nbsp;Uncomment&nbsp;it&nbsp;to&nbsp;specify&nbsp;yours&nbsp;explicitly.
<br>#CXX&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;g++
<br>
<br>#&nbsp;Uncomment&nbsp;the&nbsp;2&nbsp;lines&nbsp;to&nbsp;compile&nbsp;C&nbsp;programs&nbsp;as&nbsp;C++&nbsp;ones.
<br>#CC&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;$(CXX)
<br>#CFLAGS&nbsp;&nbsp;=&nbsp;$(CXXFLAGS)
<br>
<br>#&nbsp;The&nbsp;command&nbsp;used&nbsp;to&nbsp;delete&nbsp;file.
<br>#RM&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;rm&nbsp;-f
<br>
<br>##&nbsp;Stable&nbsp;Section:&nbsp;usually&nbsp;no&nbsp;need&nbsp;to&nbsp;be&nbsp;changed.&nbsp;But&nbsp;you&nbsp;can&nbsp;add&nbsp;more.
<br>##=============================================================================
<br>SHELL&nbsp;&nbsp;&nbsp;=&nbsp;/bin/sh
<br>SOURCES&nbsp;=&nbsp;$(foreach&nbsp;d,$(SRCDIRS),$(wildcard&nbsp;$(addprefix&nbsp;$(d)/*,$(SRCEXTS))))
<br>OBJS&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;$(foreach&nbsp;x,$(SRCEXTS),&nbsp;\
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$(patsubst&nbsp;%$(x),%.o,$(filter&nbsp;%$(x),$(SOURCES))))
<br>DEPS&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;$(patsubst&nbsp;%.o,%.d,$(OBJS))
<br>
<br>.PHONY&nbsp;:&nbsp;all&nbsp;objs&nbsp;clean&nbsp;cleanall&nbsp;rebuild
<br>
<br>all&nbsp;:&nbsp;$(PROGRAM)
<br>
<br>#&nbsp;Rules&nbsp;for&nbsp;creating&nbsp;the&nbsp;dependency&nbsp;files&nbsp;(.d).
<br>#---------------------------------------------------
<br>%.d&nbsp;:&nbsp;%.c
<br>	@$(CC)&nbsp;-MM&nbsp;-MD&nbsp;$(CFLAGS)&nbsp;$&lt;
<br>
<br>%.d&nbsp;:&nbsp;%.C
<br>	@$(CC)&nbsp;-MM&nbsp;-MD&nbsp;$(CXXFLAGS)&nbsp;$&lt;
<br>
<br>%.d&nbsp;:&nbsp;%.cc
<br>	@$(CC)&nbsp;-MM&nbsp;-MD&nbsp;$(CXXFLAGS)&nbsp;$&lt;
<br>
<br>%.d&nbsp;:&nbsp;%.cpp
<br>	@$(CC)&nbsp;-MM&nbsp;-MD&nbsp;$(CXXFLAGS)&nbsp;$&lt;
<br>
<br>%.d&nbsp;:&nbsp;%.CPP
<br>	@$(CC)&nbsp;-MM&nbsp;-MD&nbsp;$(CXXFLAGS)&nbsp;$&lt;
<br>
<br>%.d&nbsp;:&nbsp;%.c++
<br>	@$(CC)&nbsp;-MM&nbsp;-MD&nbsp;$(CXXFLAGS)&nbsp;$&lt;
<br>
<br>%.d&nbsp;:&nbsp;%.cp
<br>	@$(CC)&nbsp;-MM&nbsp;-MD&nbsp;$(CXXFLAGS)&nbsp;$&lt;
<br>
<br>%.d&nbsp;:&nbsp;%.cxx
<br>	@$(CC)&nbsp;-MM&nbsp;-MD&nbsp;$(CXXFLAGS)&nbsp;$&lt;
<br>
<br>#&nbsp;Rules&nbsp;for&nbsp;producing&nbsp;the&nbsp;objects.
<br>#---------------------------------------------------
<br>objs&nbsp;:&nbsp;$(OBJS)
<br>
<br>%.o&nbsp;:&nbsp;%.c
<br>	$(CC)&nbsp;-c&nbsp;$(CPPFLAGS)&nbsp;$(CFLAGS)&nbsp;$&lt;
<br>
<br>%.o&nbsp;:&nbsp;%.C
<br>	$(CXX)&nbsp;-c&nbsp;$(CPPFLAGS)&nbsp;$(CXXFLAGS)&nbsp;$&lt;
<br>
<br>%.o&nbsp;:&nbsp;%.cc
<br>	$(CXX)&nbsp;-c&nbsp;$(CPPFLAGS)&nbsp;$(CXXFLAGS)&nbsp;$&lt;
<br>
<br>%.o&nbsp;:&nbsp;%.cpp
<br>	$(CXX)&nbsp;-c&nbsp;$(CPPFLAGS)&nbsp;$(CXXFLAGS)&nbsp;$&lt;
<br>
<br>%.o&nbsp;:&nbsp;%.CPP
<br>	$(CXX)&nbsp;-c&nbsp;$(CPPFLAGS)&nbsp;$(CXXFLAGS)&nbsp;$&lt;
<br>
<br>%.o&nbsp;:&nbsp;%.c++
<br>	$(CXX&nbsp;-c&nbsp;$(CPPFLAGS)&nbsp;$(CXXFLAGS)&nbsp;$&lt;
<br>
<br>%.o&nbsp;:&nbsp;%.cp
<br>	$(CXX)&nbsp;-c&nbsp;$(CPPFLAGS)&nbsp;$(CXXFLAGS)&nbsp;$&lt;
<br>
<br>%.o&nbsp;:&nbsp;%.cxx
<br>	$(CXX)&nbsp;-c&nbsp;$(CPPFLAGS)&nbsp;$(CXXFLAGS)&nbsp;$&lt;
<br>
<br>#&nbsp;Rules&nbsp;for&nbsp;producing&nbsp;the&nbsp;executable.
<br>#----------------------------------------------
<br>$(PROGRAM)&nbsp;:&nbsp;$(OBJS)
<br>ifeq&nbsp;($(strip&nbsp;$(SRCEXTS)),&nbsp;.c)&nbsp;&nbsp;#&nbsp;C&nbsp;file
<br>	$(CC)&nbsp;-o&nbsp;$(PROGRAM)&nbsp;$(OBJS)&nbsp;$(LDFLAGS)
<br>else&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;C++&nbsp;file
<br>	$(CXX)&nbsp;-o&nbsp;$(PROGRAM)&nbsp;$(OBJS)&nbsp;$(LDFLAGS)
<br>endif
<br>
<br>-include&nbsp;$(DEPS)
<br>
<br>rebuild:&nbsp;clean&nbsp;all
<br>
<br>clean&nbsp;:
<br>	@$(RM)&nbsp;*.o&nbsp;*.d
<br>
<br>cleanall:&nbsp;clean
<br>	@$(RM)&nbsp;$(PROGRAM)&nbsp;$(PROGRAM).exe
<br>
<br>###&nbsp;End&nbsp;of&nbsp;the&nbsp;Makefile&nbsp;##&nbsp;&nbsp;Suggestions&nbsp;are&nbsp;welcome&nbsp;&nbsp;##&nbsp;All&nbsp;rights&nbsp;reserved&nbsp;###
<br>###############################################################################
<br></pre>
<br>下面提供两个例子来具体说明上面&nbsp;Makefile&nbsp;的用法。 <br><br>[color=darkred]例一　Hello&nbsp;World&nbsp;程序[/color] <br><br>这个程序的功能是输出&nbsp;Hello,&nbsp;world!&nbsp;这样一行文字。由&nbsp;hello.h、hello.c、main.cxx&nbsp;三个文件组成。前两个文件是&nbsp;C&nbsp;程序，后一个是&nbsp;C++&nbsp;程序，因此这是一个&nbsp;C&nbsp;和&nbsp;C++&nbsp;混编程序。 <br>
<pre>/*&nbsp;File&nbsp;name:&nbsp;hello.h
<br>&nbsp;*&nbsp;C&nbsp;header&nbsp;file
<br>&nbsp;*/
<br>
<br>#ifndef&nbsp;HELLO_H
<br>#define&nbsp;HELLO_H
<br>
<br>#ifdef&nbsp;__cplusplus
<br>extern&nbsp;"C"&nbsp;{
<br>#endif
<br>
<br>&nbsp;&nbsp;void&nbsp;print_hello();
<br>
<br>#ifdef&nbsp;__cplusplus
<br>}
<br>#endif
<br>
<br>#endif
<br></pre>
<br>
<pre>/*&nbsp;File&nbsp;name:&nbsp;hello.c
<br>&nbsp;*&nbsp;C&nbsp;source&nbsp;file.
<br>&nbsp;*/
<br>#include&nbsp;"hello.h"
<br>#include&nbsp;&lt;stdio.h&gt;
<br>
<br>void&nbsp;print_hello()
<br>{
<br>&nbsp;&nbsp;puts(&nbsp;"Hello,&nbsp;world!"&nbsp;);
<br>}
<br></pre>
<br>
<pre>/*&nbsp;File&nbsp;name:&nbsp;main.cxx
<br>&nbsp;*&nbsp;C++&nbsp;source&nbsp;file.
<br>&nbsp;*/
<br>#include&nbsp;"hello.h"
<br>
<br>int&nbsp;main()
<br>{
<br>&nbsp;&nbsp;print_hello();
<br>
<br>&nbsp;&nbsp;return&nbsp;0;
<br>}
<br></pre>
<br>建立一个新的目录，然后把这三个文件拷贝到目录中，也把&nbsp;Makefile&nbsp;文件拷贝到目录中。之后，对&nbsp;Makefile&nbsp;的相关项目进行如下设置： <br>
<pre>PROGRAM&nbsp;&nbsp;&nbsp;:=&nbsp;hello&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;设置运行程序名
<br>SRCDIRS&nbsp;&nbsp;&nbsp;:=&nbsp;.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;源程序位于当前目录下
<br>SRCEXTS&nbsp;&nbsp;&nbsp;:=&nbsp;.c&nbsp;.cxx&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;源程序文件有&nbsp;.c&nbsp;和&nbsp;.cxx&nbsp;两种类型
<br>CFLAGS&nbsp;&nbsp;&nbsp;&nbsp;:=&nbsp;-g&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;为&nbsp;C&nbsp;目标程序包含&nbsp;GDB&nbsp;可用的调试信息
<br>CXXFLAGS&nbsp;&nbsp;:=&nbsp;-g&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;为&nbsp;C++&nbsp;目标程序包含&nbsp;GDB&nbsp;可用的调试信息
<br></pre>
<br>由于这个简单的程序只使用了&nbsp;C&nbsp;标准库的函数（puts），所以对于&nbsp;CFLAGS&nbsp;和&nbsp;CXXFLAGS&nbsp;没有过多的要求，LDFLAGS&nbsp;和&nbsp;CPPFLAGS&nbsp;选项也无需设置。 <br><br>经过上面的设置之后，执行&nbsp;make&nbsp;命令就可以编译程序了。如果没有错误出现的话，./hello&nbsp;&nbsp;就可以运行程序了。 <br><br>如果修改了源程序的话，可以看到只有和修改有关的源文件被编译。也可以再为程序添加新的源文件，只要它们的扩展名是已经在&nbsp;Makefile&nbsp;中设置过的，那么就没有必要修改　Makefile。 <br><br>[color=darkred]例二　GTK+&nbsp;版&nbsp;Hello&nbsp;World&nbsp;程序[/color] <br><br>这个&nbsp;GTK+&nbsp;2.0&nbsp;版的&nbsp;Hello&nbsp;World&nbsp;程序可以从下面的网址上得到：http://www.gtk.org/tutorial/c58.html#SEC-HELLOWORLD。当然，要编译&nbsp;GTK+&nbsp;程序，还需要你的系统上已经安装好了&nbsp;GTK+。 <br><br>跟第一个例子一样，单独创建一个新的目录，把上面网页中提供的程序保存为&nbsp;main.c&nbsp;文件。对&nbsp;Makefile&nbsp;做如下设置： <br>
<pre>PROGRAM&nbsp;&nbsp;&nbsp;:=&nbsp;hello&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;设置运行程序名
<br>SRCDIRS&nbsp;&nbsp;&nbsp;:=&nbsp;.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;源程序位于当前目录下
<br>SRCEXTS&nbsp;&nbsp;&nbsp;:=&nbsp;.c&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;源程序文件只有&nbsp;.c&nbsp;一种类型
<br>CFLAGS&nbsp;&nbsp;&nbsp;&nbsp;:=&nbsp;`pkg-config&nbsp;--cflags&nbsp;gtk+-2.0`&nbsp;&nbsp;#&nbsp;CFLAGS
<br>LDFLAGS&nbsp;&nbsp;&nbsp;:=&nbsp;`pkg-config&nbsp;--libs&nbsp;gtk+-2.0`&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;LDFLAGS
<br></pre>
<br>这是一个&nbsp;C&nbsp;程序，所以&nbsp;CXXFLAGS&nbsp;没有必要设置——即使被设置了也不会被使用。 <br><br>编译和连接&nbsp;GTK+&nbsp;库所需要的&nbsp;CFLAGS&nbsp;和&nbsp;LDFLAGS&nbsp;由&nbsp;pkg-config&nbsp;程序自动产生。 <br><br>现在就可以运行&nbsp;make&nbsp;命令编译、./hello&nbsp;执行这个&nbsp;GTK+&nbsp;程序了。 <br><br>参考资料： <br>[list=1][*]Multi-file&nbsp;projects&nbsp;and&nbsp;the&nbsp;GNU&nbsp;Make&nbsp;utility <br>Author:&nbsp;George&nbsp;Foot <br>http://www.elitecoders.de/mags/cscene/CS2/CS2-10.html <br><br>[*]GNU&nbsp;Make&nbsp;Manual <br>http://www.gnu.org/software/make/manual/ <br>[/list]<br>
<p>&nbsp;</p>
<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>
    <li><strong>":="</strong>赋值，和"="不同的是，":="在赋值的同时，会将赋值语句中所有的变量就地展开，也就是说，A:=$(B)后，B的值的改变不再影响A
    </li>
    <li><strong>隐含规则</strong>。GUN Make在不特别指定的情况下会使用诸如以下编译命令：$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $&lt; -o $@，这也是为什么这个Makefile最后一个命令没有添加$(CPPFLAGS)的原因，因为缺省是包含这个变量的
    </li>
    <li><strong>函数</strong>和变量很相似："$ (函数名，空格，一列由逗号分隔的参数)"
    </li>
    <li>SOURCES = $(wildcard *.cpp) 列出工作目录下文件名满足"*.cpp"条件的文件，以空格分隔，并将列表赋给SOURCE变量
    </li>
    <li><strong>patsubst</strong>函数：3个参数。功能是将第三个参数中的每一项（由空格分隔）符合第一个参数描述的部分替换成第二个参数制定的值
    </li>
    <li><strong>addprefix</strong>函数：2个参数。将源串（第2个参数，由空格分隔）中的每一项添加前缀（第1个参数）
    </li>
    <li><strong>filter-out</strong>函数：2个参数。从第二串中过滤掉包含在第一个串中的项
    </li>
    <li>$(CC) -MM -MMD $(SOURCE) : 对每个源文件生成依赖(dependence，Make通过依赖规则来判断是否需要重新编译某个文件)，"D"生成".d"文件，-MM表示去掉 depends里面的系统的头文件(使用&lt;&gt;包含的头文件)（若使用-M则全部包含，事实上，系统头文件被修改的可能性极小，不需要执行依赖检查）
    </li>
    <li><strong>.PHONY</strong>，不检查后面制定各项是否存在同名文件
    </li>
    <li><strong>ifneg...else...endif</strong>，Makefile中的条件语句
    </li>
    <li><strong>-include</strong> $(DEPS) : 将DEPS中的文件包含进来，"-"表示忽略文件不存在的错误
    </li>
    <li><strong>@</strong>$(RM) *.o : 开头的"@"表示在Make的时候，不显示这条命令（GNU Make缺省是显示的)
    </li>
    <li>all : 作为第一个出现的目标项目，Make会将它作为主要和缺省项目("make"就表示"make all")
    </li>
    <li>deps : 只生成依赖文件(.d文件)
    </li>
    <li>objs : 为每一个源码程序生成或更新 '.d' 文件和'.o'文件
    </li>
    <li>clean : 删除所有'.d','.o'和可执行文件
    </li>
    <li>rebuild : clean然后重建
    </li>
    <li>内部变量<strong>$@, $&lt; $^</strong> : 分别表示目标名(:前面的部分，比如all)，依靠列表（:后面的部分）中的第一个依靠文件，所有依靠文件 </li>
</ul><img src ="http://www.cppblog.com/flyonok/aggbug/39504.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyonok/" target="_blank">flyonok</a> 2007-12-24 13:52 <a href="http://www.cppblog.com/flyonok/archive/2007/12/24/39504.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>gcc</title><link>http://www.cppblog.com/flyonok/archive/2007/12/23/39359.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Sun, 23 Dec 2007 05:32:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2007/12/23/39359.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/39359.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2007/12/23/39359.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/39359.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/39359.html</trackback:ping><description><![CDATA[<p>简单介绍开源项目里面的gcc的基础知识以及使用方法<br>&nbsp;&nbsp;&nbsp;
在为Linux开发应用程序时，绝大多数情况下使用的都是C语言，因此几乎每一位Linux程序员面临的首要问题都是如何灵活运用C编译器。目前
Linux下最常用的C语言编译器是GCC（GNU Compiler Collection），它是GNU项目中符合ANSI
C标准的编译系统，能够编译用C、C++和Object
C等语言编写的程序。GCC不仅功能非常强大，结构也异常灵活。最值得称道的一点就是它可以通过不同的前端模块来支持各种语言，如Java、
Fortran、Pascal、Modula-3和Ada等。<br><br><font color="#ff0000">开放、自由和灵活是Linux的魅力所在，而这一点在GCC上的体现就是程序员通过它能够更好地控制整个编译过程。在使用GCC编译程序时，编译过程可以被细分为四个阶段</font>： <br><br>◆ 预处理（Pre-Processing） <br>◆ 编译（Compiling） <br><br>◆ 汇编（Assembling） <br><br>◆ 链接（Linking） <br><br>Linux
程序员可以根据自己的需要让GCC在编译的任何阶段结束，以便检查或使用编译器在该阶段的输出信息，或者对最后生成的二进制文件进行控制，以便通过加入不
同数量和种类的调试代码来为今后的调试做好准备。和其它常用的编译器一样，GCC也提供了灵活而强大的代码优化功能，利用它可以生成执行效率更高的代码。
<br><br>GCC提供了30多条警告信息和三个警告级别，使用它们有助于增强程序的稳定性和可移植性。此外，GCC还对标准的C和C++语言进行了大量的扩展，提高程序的执行效率，有助于编译器进行代码优化，能够减轻编程的工作量。 <br><br>GCC起步 <br><br>在学习使用GCC之前，下面的这个例子能够帮助用户迅速理解GCC的工作原理，并将其立即运用到实际的项目开发中去。首先用熟悉的编辑器输入清单1所示的代码： <br><br>清单1：hello.c <br><br>#include &lt;stdio.h&gt;<br>int main(void)<br>{<br>printf ("Hello world, Linux programming!\n");<br>return 0;<br>}<br><br><br><br>然后执行下面的命令编译和运行这段程序： <br><br># <font color="#0000ff">gcc hello.c -o hello<br></font># ./hello<br>Hello world, Linux programming!<br><br><br><br><br>从
程序员的角度看，只需简单地执行一条GCC命令就可以了，但从编译器的角度来看，却需要完成一系列非常繁杂的工作。首先，GCC需要调用预处理程序
cpp，由它负责展开在源文件中定义的宏，并向其中插入"#include"语句所包含的内容；接着，GCC会调用ccl和as将处理后的源代码编译成目
标代码；最后，GCC会调用链接程序ld，把生成的目标代码链接成一个可执行程序。 <br><br>为了更好地理解GCC的工作过程，可以把上述编译过程分成几个步骤单独进行，并观察每步的运行结果。第一步是进行预编译，<font color="#ff0000">使用-E参数可以让GCC在预处理结束后停止编译过程</font>： <br><br># gcc -E hello.c -o hello.i<br><br><br><br>此时若查看hello.i文件中的内容，会发现stdio.h的内容确实都插到文件里去了，而其它应当被预处理的宏定义也都做了相应的处理。下一步是将hello.i编译为目标代码，<font color="#ff0000">这可以通过使用-c参数来完成</font>： <br><br># gcc -c hello.i -o hello.o<br><br><br><br>GCC默认将.i文件看成是预处理后的C语言源代码，因此上述命令将自动跳过预处理步骤而开始执行编译过程，也可<font color="#ff0000">以使用-x参数让GCC从指定的步骤开始编译</font>。最后一步是将生成的目标文件链接成可执行文件： <br><br># gcc hello.o -o hello<br><br><br><br>在
采用模块化的设计思想进行软件开发时，通常整个程序是由多个源文件组成的，相应地也就形成了多个编译单元，使用GCC能够很好地管理这些编译单元。假设有
一个由foo1.c和foo2.c两个源文件组成的程序，为了对它们进行编译，并最终生成可执行程序foo，可以使用下面这条命令： <br><br># gcc foo1.c foo2.c -o foo<br><br><br><br>如果同时处理的文件不止一个，GCC仍然会按照预处理、编译和链接的过程依次进行。如果深究起来，上面这条命令大致相当于依次执行如下三条命令： <br><br># gcc -c foo1.c -o foo1.o<br># gcc -c foo2.c -o foo2.o<br># gcc foo1.o foo2.o -o foo<br><br><br><br>在
编译一个包含许多源文件的工程时，若只用一条GCC命令来完成编译是非常浪费时间的。假设项目中有100个源文件需要编译，并且每个源文件中都包含
10000行代码，如果像上面那样仅用一条GCC命令来完成编译工作，那么GCC需要将每个源文件都重新编译一遍，然后再全部连接起来。很显然，这样浪费
的时间相当多，尤其是当用户只是修改了其中某一个文件的时候，完全没有必要将每个文件都重新编译一遍，因为很多已经生成的目标文件是不会改变的。要解决这
个问题，关键是要灵活运用GCC，同时还要借助像Make这样的工具。 <br><br><font color="#ff0000">警告提示功能 <br></font><br>GCC包含完整的出错检查和警告提示功能，它们可以帮助Linux程序员写出更加专业和优美的代码。先来读读清单2所示的程序，这段代码写得很糟糕，仔细检查一下不难挑出很多毛病： <br><br>◆main函数的返回值被声明为void，但实际上应该是int； <br><br>◆使用了GNU语法扩展，即使用long long来声明64位整数，不符合ANSI/ISO C语言标准； <br><br>◆main函数在终止前没有调用return语句。 <br><br>清单2：illcode.c <br><br>#include &lt;stdio.h&gt;<br>void main(void)<br>{<br>long long int var = 1;<br>printf("It is not standard C code!\n");<br>}<br><br><br><br>下面来看看GCC是如何帮助程序员来发现这些错误的。<font color="#ff0000">当GCC在编译不符合ANSI/ISO C语言标准的源代码时，如果加上了-pedantic选项，那么使用了扩展语法的地方将产生相应的警告信息</font>： <br><br># gcc -pedantic illcode.c -o illcode<br>illcode.c: In function `main':<br>illcode.c:9: ISO C89 does not support `long long'<br>illcode.c:8: return type of `main' is not `int'<br><br><br><br>需 要注意的是<font color="#ff0000">，-pedantic编译选项并不能保证被编译程序与ANSI/ISO C标准的完全兼容，它</font>仅
仅只能用来帮助Linux程序员离这个目标越来越近。或者换句话说，-pedantic选项能够帮助程序员发现一些不符合 ANSI/ISO
C标准的代码，但不是全部，事实上只有ANSI/ISO C语言标准中要求进行编译器诊断的那些情况，才有可能被GCC发现并提出警告。 <br><br>除了-pedantic之外，<font color="#ff0000">GCC还有一些其它编译选项也能够产生有用的警告信息。这些选项大多以-W开头，其中最有价值的当数-Wall了，使用它能够使GCC产生尽可能多的警告信息：</font> <br><br># gcc -Wall illcode.c -o illcode<br>illcode.c:8: warning: return type of `main' is not `int'<br>illcode.c: In function `main':<br>illcode.c:9: warning: unused variable `var'<br><br><br><br>GCC给出的警告信息虽然从严格意义上说不能算作是错误，但却很可能成为错误的栖身之所。一个优秀的Linux程序员应该尽量避免产生警告信息，使自己的代码始终保持简洁、优美和健壮的特性。 <br><br>在 处理警告方面，<font color="#ff0000">另一个常用的编译选项是-Werror，它要求GCC将所有的警告当成错误进行处理</font>，这在使用自动编译工具（如Make等）时非常有用。如 果编译时带上-Werror选项，那么GCC会在所有产生警告的地方停止编译，迫使程序员对自己的代码进行修改。只有当相应的警告信息消除时，才可能将编 译过程继续朝前推进。执行情况如下： <br><br># gcc -Wall -Werror illcode.c -o illcode<br>cc1: warnings being treated as errors<br>illcode.c:8: warning: return type of `main' is not `int'<br>illcode.c: In function `main':<br>illcode.c:9: warning: unused variable `var'<br><br><br><br>对Linux程序员来讲，<font color="#ff0000">GCC给出的警告信息是很有价值的</font>，它们不仅可以帮助程序员写出更加健壮的程序，而且还是跟踪和调试程序的有力工具。建议在用<font color="#ff0000">GCC编译源代码时始终带上-Wall选项，并把它逐渐培养成为一种习惯，这对找出常见的隐式编程错误很有帮助</font>。 <br><br>库依赖 <br><br>在Linux 下开发软件时，完全不使用第三方函数库的情况是比较少见的，通常来讲都需要借助一个或多个函数库的支持才能够完成相应的功能。从程序员的角度看，函数库实 际上就是一<font color="#ff0000">些头文件（.h）和库文件（.so或者.a）的集合</font>。虽然Linux下的大多数函数都默认将<font color="#ff0000"><font style="background-color: #ffffff;">头文件放到/usr/include/目录</font>下</font>，而库 文件则放到<font color="#ff0000">/usr/lib/目</font>录下，但并不是所有的情况都是这样。正因如此，GCC在编译时必须有自己的办法来查找所需要的头文件和库文件。 <br><br>GCC采用搜索目录的办法来查找所需要的文件，-I选项可以向GCC的头文件搜索路径中添加新的目录。例如，如果在/home/xiaowp/include/目录下有编译时所需要的头文件，为了让GCC能够顺利地找到它们，就<font color="#ff0000">可以使用-I选项：</font> <br><br># gcc foo.c <font color="#ff0000">-I</font> /home/xiaowp/include -o foo<br><br><br><br>同样，如果使用了不在标准位置的库文件，那么可以通过-<font color="#ff0000">L选项向GCC的库文件搜索路径中添加新的目录</font>。例如，如果在/home/xiaowp/lib/目录下有链接时所需要的库文件libfoo.so，为了让GCC能够顺利地找到它，可以使用下面的命令： <br><br># gcc foo.c<font color="#ff0000"> -L</font> /home/xiaowp/lib -lfoo -o foo<br><br><br><br>值
得好好解释一下的是-l选项，它指示GCC去连接库文件libfoo.so。Linux下的库文件在命名时有一个约定，那就是应该以lib三个字母开头，
由于所有的库文件都遵循了同样的规范，因此在用-l选项指定链接的库文件名时可以省去lib三个字母，也就是说GCC在对-lfoo进行处理时，会自动去
链接名为libfoo.so的文件。 <br><br>Linux下的库文件分为两大类分别<font color="#ff0000">是动态链接库（通常以.so结尾）和静态链接库（通常以</font><font color="#ff0000">.a 结尾</font>），两者的差别仅在程序执行时所需的代码是在运行时动态加载的，还是在编译时静态加载的。默认情况下，GCC在链接时优先使用动态链接库，只有当动态 链接库不存在时才考虑使用静态链接库，如果需要的话可以在编译时加<font color="#ff0000">上-static选</font>项，强制使用静态链接库。例如，如果在 /home/xiaowp/lib/目录下有链接时所需要的库文件libfoo.so和libfoo.a，为了让GCC在链接时只用到静态链接库，可以使 用下面的命令： <br><br># gcc foo.c -L /home/xiaowp/lib -static -lfoo -o foo<br><br>代码优化 <br><br>代 码优化指的是编译器通过分析源代码，找出其中尚未达到最优的部分，然后对其重新进行组合，目的是改善程序的执行性能。GCC提供的代码优化功能非常强大， 它通过编译选<font color="#ff0000">项-On来控制优化代码的</font>生成，<font color="#ff0000">其中n是一个代表优化级别的整数</font>。对于不同版本的GCC来讲，n的取值范围及其对应的优化效果可能并不完全相 同，比较典型的范围是从<font color="#ff0000">0变化到2或3</font>。 <br><br>编
译时使用选项-O可以告诉GCC同时减小代码的长度和执行时间，其效果等价于-O1。在这
一级别上能够进行的优化类型虽然取决于目标处理器，但一般都会包括线程跳转（Thread Jump）和延迟退栈（Deferred Stack
Pops）两种优化。选项-O2告诉GCC除了完成所有-O1级别的优化之外，同时还要进行一些额外的调整工作，如处理器指令调度等。选项-O3则除了完
成所有-O2级别的优化之外，还包括循环展开和其它一些与处理器特性相关的优化工作。通常来说，数字越大优化的等级越高，同时也就意味着程序的运行速度越
快。许多Linux程序员都喜欢使<font color="#ff0000">用-O2选项，因为它在优化长度、编译时间和代码大小之间，取得了一个比较理想的平衡点</font>。 <br>下面通过具体实例来感受一下GCC的代码优化功能，所用程序如清单3所示。 <br><br>清单3：optimize.c <br><br>#include &lt;stdio.h&gt; <br>int main(void)<br>{<br>double counter;<br>double result;<br>double temp;<br>for (counter = 0; <br>counter &lt; 2000.0 * 2000.0 * 2000.0 / 20.0 + 2020; <br>counter += (5 - 1) / 4) {<br>temp = counter / 1979;<br>result = counter; <br>}<br>printf("Result is %lf\n", result);<br>return 0;<br>}<br><br><br><br>首先不加任何优化选项进行编译： <br><br># gcc -Wall optimize.c -o optimize<br><br><br><br>借助Linux提供的time命令，可以大致统计出该程序在运行时所需要的时间： <br><br># time ./optimize<br>Result is 400002019.000000<br>real 0m14.942s<br>user 0m14.940s<br>sys 0m0.000s<br><br><br><br>接下去使用优化选项来对代码进行优化处理： <br><br># gcc -Wall -O optimize.c -o optimize<br><br><br><br>在同样的条件下再次测试一下运行时间： <br><br># time ./optimize<br>Result is 400002019.000000<br>real 0m3.256s<br>user 0m3.240s<br>sys 0m0.000s<br><br><br><br>对
比两次执行的输出结果不难看出，程序的性能的确得到了很大幅度的改善，由原来的14秒缩短到了3秒。这个例子是专门针对GCC的优化功能而设计的，因此优
化前后程序的执行速度发生了很大的改变。尽管GCC的代码优化功能非常强大，但作为一名优秀的Linux程序员，首先还是要力求能够手工编写出高质量的代
码。如果编写的代码简短，并且逻辑性强，编译器就不会做更多的工作，甚至根本用不着优化。 <br><br>优化虽然能够给程序带来更好的执行性能，但在如下一些场合中应该避免优化代码： <br><br>◆ <font color="#ff0000">程序开发的时候 优化等级越高，消耗在编译上的时间就越长，因此在开发的时候最好不要使用优化选项，只有到软件发行或开发结束的时候，才考虑对最终生成的代码进行优化</font>。 <br><br>◆ 资源受限的时候 一些优化选项会增加可执行代码的体积，如果程序在运行时能够申请到的内存资源非常紧张（如一些实时嵌入式设备），那就不要对代码进行优化，因为由这带来的负面影响可能会产生非常严重的后果。 <br><br>◆ 跟踪调试的时候 在对代码进行优化的时候，某些<font color="#ff0000">代码可能会被删除或改写</font>，或者为了取得更佳的性能而进行重组，从而使跟踪和调试变得异常困难。 <br><br>调试 <br><br>一个功能强大的调试器不仅为程序员提供了跟踪程序执行的手段，而且还可以帮助程序员找到解决问题的方法。对于Linux程序员来讲，<font color="#ff0000">GDB（GNU Debugger）通过与GCC的配合使用</font>，为基于Linux的软件开发提供了一个完善的调试环境。 <br><br>默 认情况下，GCC在编译时不会将调试符号插入到生成的二进制代码中，因为这样会增加可执行文件的大小。如果需要在编译时生成调试符号信息，可以使用<font color="#ff0000">GCC 的-g或者-ggdb选项</font>。GCC在产生调试符号时，同样采用了分级的思路，开发人员可以通过在<font color="#800000">-g选项后附加数字1、2或3来指定在代码中加入调试信息 的多少</font>。
默认的级别是2（-g2），此时产生的调试信息包括扩展的符号表、行号、局部或外部变量信息。级别3（-g3）包含级别2中的所有调试信息，以及
源代码中定义的宏。级别1（-g1）不包含局部变量和与行号有关的调试信息，因此只能够用于回溯跟踪和堆栈转储之用。回溯跟踪指的是监视程序在运行过程中
的函数调用历史，堆栈转储则是一种以原始的十六进制格式保存程序执行环境的方法，两者都是经常用到的调试手段。 <br><br>GCC产生的调试符号 具有普遍的适应性，可以被许多调试器加以利用，但如果使用的是GDB，<font color="#ff0000">那么还可以通过-ggdb选项在生成的二进制代码中包含GDB专用的调试信息</font>。这种 做法的优点是可以方便GDB的调试工作，但缺点是可能导致其它调试器（如DBX）无法进行正常的调试。选项-ggdb能够接受的调试级别和-g是完全一样 的，它们对输出的调试符号有着相同的影响。 <br><br>需要注意的是，使用任何一个调试选项都会使最终生成的二进制文件的大小急剧增加，同时增加程序在执行时的开销，因此调试选项通常仅在软件的开发和调试阶段使用。调试选项对生成代码大小的影响从下面的对比过程中可以看出来： <br><br># gcc optimize.c -o optimize<br># ls optimize -l<br>-rwxrwxr-x 1 xiaowp xiaowp 11649 Nov 20 08:53 optimize (未加调试选项)<br># gcc -g optimize.c -o optimize<br># ls optimize -l<br>-rwxrwxr-x 1 xiaowp xiaowp 15889 Nov 20 08:54 optimize (加入调试选项)<br><br><br><br>虽然调试选项会增加文件的大小，但事实上Linux中的许多软件在测试版本甚至最终发行版本中仍然使用了调试选项来进行编译，这样做的目的是鼓励用户在发现问题时自己动手解决，是Linux的一个显著特色。 <br><br>下面还是通过一个具体的实例说明如何利用调试符号来分析错误，所用程序见清单4所示。 <br><br>清单4：crash.c <br><br>#include &lt;stdio.h&gt; <br>int main(void)<br>{<br>int input =0;<br>printf("Input an integer:");<br>scanf("%d", input);<br>printf("The integer you input is %d\n", input);<br>return 0;<br>}<br><br><br><br>编译并运行上述代码，会产生一个严重的段错误（Segmentation fault）如下： <br><br># gcc -g crash.c -o crash<br># ./crash<br>Input an integer:10<br>Segmentation fault<br><br><br><br>为了更快速地发现错误所在，可以使用GDB进行跟踪调试，方法如下： <br><br># gdb crash<br>GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)<br>......<br>(gdb)<br><br><br><br>当GDB提示符出现的时候，表明GDB已经做好准备进行调试了，现在可以通过run命令让程序开始在GDB的监控下运行： <br><br>(gdb) run<br>Starting program: /home/xiaowp/thesis/gcc/code/crash<br>Input an integer:10<br><br>Program received signal SIGSEGV, Segmentation fault.<br>0x4008576b in _IO_vfscanf_internal () from /lib/libc.so.6<br><br><br><br>仔 细分析一下GDB给出的输出结果不难看出，程序是由于段错误而导致异常中止的，说明内存操作出了问题，具体发生问题的地方是在调用 _IO_vfscanf_internal ( )的时候。为了得到更加有价值的信息，可以使用GDB提供的回<font color="#ff0000">溯跟踪命令backtrace</font>，执行结果如下： <br><br>(gdb) backtrace<br>#0 0x4008576b in _IO_vfscanf_internal () from /lib/libc.so.6<br>#1 0xbffff0c0 in ?? ()<br>#2 0x4008e0ba in scanf () from /lib/libc.so.6<br>#3 0x08048393 in main () at crash.c:11<br>#4 0x40042917 in __libc_start_main () from /lib/libc.so.6<br><br><br><br>跳过输出结果中的前面三行，从输出结果的第四行中不难看出，GDB已经将错误定位到crash.c中的第11行了。现在仔细检查一下： <br><br>(gdb) frame 3<br>#3 0x08048393 in main () at crash.c:11<br>11 scanf("%d", input);<br><br><br><br>使用GDB提供的frame命令可以定位到发生错误的代码段，该命令后面跟着的数值可以在backtrace命令输出结果中的行首找到。现在已经发现错误所在了，应该将 <br><br>scanf("%d", input);<br>改为<br>scanf("%d", &amp;input);<br><br><br><br>完成后就可以退出GDB了，命令如下： <br><br>(gdb) quit<br><br><br><br>GDB的功能远远不止如此，它还可以<font color="#ff0000">单步跟踪程序、检查内存变量和设置断点</font>等。 <br><br>调 试时可能会需要用到编译器产生的中间结果，这时可以使用<font color="#ff0000">-save-temps选项</font>，让GCC将预处理代码、汇编代码和目标代码都作为文件保存起来。如果 想检查生成的代码是否能够通过手工调整的办法来提高执行性能，在编译过程中生成的中间文件将会很有帮助，具体情况如下： <br><br># gcc -save-temps foo.c -o foo<br># ls foo*<br>foo foo.c foo.i foo.s<br><br><br><br>GCC 支持的其它调试选项还包<font color="#ff0000">括-p和-pg</font>，它们会将剖析（Profiling）信息加入到最终生成的二进制代码中。剖析信息对于找出程序的性能瓶颈很有帮 助，是协助Linux程序员开发出高性能程序的有力工具。<font color="#ff0000">在编译时加入-p选项会在生成的代码中加入通用剖析工具（Prof）能够识别的统计信息，而- pg选项则生成只有GNU剖析工具（Gprof）才能识别的统计信息。 <br></font><br>最
后提醒一点，虽然GCC允许在优化的同时加入调试符号信息，
但优化后的代码对于调试本身而言将是一个很大的挑战。代码在经过优化之后，在源程序中声明和使用的变量很可能不再使用，控制流也可能会突然跳转到意外的地
方，循环语句有可能因为循环展开而变得到处都有，所有这些对调试来讲都将是一场噩梦。建议在调试的时候最好不使用任何优化选项，只有当程序在最终发行的时
候才考虑对其进行优化。</p>
<p>&nbsp;</p>
<p>&nbsp;<span id="ContentLabel" style="padding: 0px 10px; display: block;">上次的培训园地中介绍了GCC的编译过程、警告提示功能、库依赖、代码优化和程序调试六个方面的内容。这期是最后的一部分内容。 <br><br><strong>加速</strong> <br><br>在将源代码变成可执行文件的过程中，需要经过许多中间步骤，包含预处理、编译、汇编和连接。这些过程实际上是由不同的程序负责完成的。大多数情况下GCC可以为Linux程序员完成所有的后台工作，自动调用相应程序进行处理。 <br><br>这
样做有一个很明显的缺点，就是GCC在处理每一个源文件时，最终都需要生成好几个临时文件才能完成相应的工作，从而无形中导致处理速度变慢。例如，GCC
在处理一个源文件时，可能需要一个临时文件来保存预处理的输出、一个临时文件来保存编译器的输出、一个临时文件来保存汇编器的输出，而读写这些临时文件显
然需要耗费一定的时间。当软件项目变得非常庞大的时候，花费在这上面的代价可能会变得很沉重。 <br><br>解决的办法是，使用Linux提供的一种更加高效的通信方式—管道。它可以用来同时连接两个程序，其中一个程序的输出将被直接作为另一个程序的输入，这样就可以避免使用临时文件，但编译时却需要消耗更多的内存。 <br><br>在编译过程中使用管道是由GCC的-pipe选项决定的。下面的这条命令就是借助GCC的管道功能来提高编译速度的： <br><br><ccid_nobr></ccid_nobr>
<table bordercolordark="#ffffff" bordercolorlight="#000000" align="center" border="1" cellpadding="2" cellspacing="0" width="400">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6">
            <pre><ccid_code></ccid_code># gcc -pipe foo.c -o foo</pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br>在编译小型工程时使用管道，编译时间上的差异可能还不是很明显，但在源代码非常多的大型工程中，差异将变得非常明显。 <br><br><strong>文件扩展名</strong> <br><br>在使用GCC的过程中，用户对一些常用的扩展名一定要熟悉，并知道其含义。为了方便大家学习使用GCC，在此将这些扩展名罗列如下： <br><br>.c C原始程序； <br><br>.C C++原始程序； <br><br>.cc C++原始程序； <br><br>.cxx C++原始程序； <br><br>.m Objective-C原始程序； <br><br>.i 已经过预处理的C原始程序； <br><br>.ii 已经过预处理之C++原始程序； <br><br>.s 组合语言原始程序； <br><br>.S 组合语言原始程序； <br><br>.h 预处理文件(标头文件)； <br><br>.o 目标文件； <br><br>.a 存档文件。 <br><br><strong>GCC常用选项</strong> <br><br>GCC作为Linux下C/C++重要的编译环境，功能强大，编译选项繁多。为了方便大家日后编译方便，在此将常用的选项及说明罗列出来如下： <br><br>-c 通知GCC取消链接步骤，即编译源码并在最后生成目标文件； <br><br>-Dmacro 定义指定的宏，使它能够通过源码中的#ifdef进行检验； <br><br>-E 不经过编译预处理程序的输出而输送至标准输出； <br><br>-g3 获得有关调试程序的详细信息，它不能与-o选项联合使用； <br><br>-Idirectory 在包含文件搜索路径的起点处添加指定目录； <br><br>-llibrary 提示链接程序在创建最终可执行文件时包含指定的库； <br><br>-O、-O2、-O3 将优化状态打开，该选项不能与-g选项联合使用； <br><br>-S 要求编译程序生成来自源代码的汇编程序输出； <br><br>-v 启动所有警报； <br><br>-Wall 在发生警报时取消编译操作，即将警报看作是错误； <br><br>-Werror 在发生警报时取消编译操作，即把报警当作是错误； <br><br>-w 禁止所有的报警。 <br><br><strong>小结</strong> <br><br>GCC
是在Linux下开发程序时必须掌握的工具之一。本文对GCC做了一个简要的介绍，主要讲述了如何使用GCC编译程序、产生警告信息、调试程序和加快
GCC的编译速度。对所有希望早日跨入Linux开发者行列的人来说，GCC就是成为一名优秀的Linux程序员的起跑线。 <br></span></p>
<p>&nbsp;</p>
<p><br>3. 关于sqrt,sin和cos函数<br>就算是引入math.h头文件，也不会找到sqrt,sin和cos函数。应该用链接库来解决。<br><font color="#ff0000">有时候我们使用了某个函数,但是我们不知道库的名字,这个时候怎么办呢?很</font>抱
歉,对于这个问题我也不知道答案,我只有一个傻办法.首先,
我到标准库路径下面去找看看有没有和我用的函数相关的库,我就这样找到了线程(thread)函数的库文件(libpthread.a).
当然,如果找不到,只有一个笨方法.比如我要找sin这个函数所在的库. 就只好用 nm -o /lib/*.so|grep
sin&gt;~/sin 命令,然后看~/sin文件,到那里面去找了.
在sin文件当中,我会找到这样的一行libm-2.1.2.so:00009fa0 W sin 这样我就知道了sin在
libm-2.1.2.so库里面,我用 -lm选项就可以了(去掉前面的lib和后面的版本标志,就剩下m了所以是 -lm).&nbsp; <br></p><img src ="http://www.cppblog.com/flyonok/aggbug/39359.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyonok/" target="_blank">flyonok</a> 2007-12-23 13:32 <a href="http://www.cppblog.com/flyonok/archive/2007/12/23/39359.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>makefile 范本</title><link>http://www.cppblog.com/flyonok/archive/2007/12/16/38611.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Sun, 16 Dec 2007 05:47:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2007/12/16/38611.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/38611.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2007/12/16/38611.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/38611.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/38611.html</trackback:ping><description><![CDATA[<p>PROGRAM = SystemExersize.exe #定义程序名<br>OBJECT = main.o sysinfo.o #定义目标文件名集合<br>CFLAG = -D SECURITY_WIN32=1 -D _WIN32_WINNT=0x500 <br>#定义宏，相当于#define<br>LIBS = -lsecur32 #-lkernel32 #声明库文件<br>$(PROGRAM) : $(OBJECT)<br>&nbsp;gcc -o $(PROGRAM) $(OBJECT) $(LIBS)<br>main.o : ../main.c ../sysinfo.h<br>&nbsp;gcc -c -g $(CFLAG) ../main.c</p>
<p>sysinfo.o : ../sysinfo.c ../sysinfo.h<br>&nbsp;gcc -c -g $(CFLAG) ../sysinfo.c</p>
<p>clean:<br>&nbsp;del $(PROGRAM) $(OBJECT)<br><br>以上是mingw在windows下的一个范本。<br></p><img src ="http://www.cppblog.com/flyonok/aggbug/38611.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyonok/" target="_blank">flyonok</a> 2007-12-16 13:47 <a href="http://www.cppblog.com/flyonok/archive/2007/12/16/38611.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Kinds of Insert Iterators</title><link>http://www.cppblog.com/flyonok/archive/2007/12/14/38527.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Fri, 14 Dec 2007 08:47:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2007/12/14/38527.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/38527.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2007/12/14/38527.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/38527.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/38527.html</trackback:ping><description><![CDATA[<table border="1" cellpadding="1" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <th valign="top"><font size="2"><strong>Name</strong> </font></th>
            <th valign="top"><font size="2"><strong>Class</strong> </font></th>
            <th valign="top"><font size="2"><strong>Called Function</strong> </font></th>
            <th valign="top"><font size="2"><strong>Creation</strong> </font></th>
        </tr>
        <tr>
            <td valign="top"><font size="2">Back inserter</font></td>
            <td valign="top"><font size="2"><tt class="monofont">back_insert_iterator</tt> </font></td>
            <td valign="top"><font size="2"><tt class="monofont">push_back</tt> <em>(value)</em> </font></td>
            <td valign="top"><font size="2"><tt class="monofont">back_inserter</tt> <em>(cont)</em> </font></td>
        </tr>
        <tr>
            <td valign="top"><font size="2">Front inserter</font></td>
            <td valign="top"><font size="2"><tt class="monofont">front_insert_iterator</tt> </font></td>
            <td valign="top"><font size="2"><tt class="monofont">push_front</tt> <em>(value)</em> </font></td>
            <td valign="top"><font size="2"><tt class="monofont">front_inserter</tt> <em>(cont)</em> </font></td>
        </tr>
        <tr>
            <td valign="top"><font size="2">General inserter</font></td>
            <td valign="top"><font size="2"><tt class="monofont">insert_iterator</tt> </font></td>
            <td valign="top"><font size="2"><tt class="monofont">insert</tt><em> (pos, value)</em> </font></td>
            <td valign="top"><font size="2"><tt class="monofont">inserter</tt><em> (cont, pos)</em> </font></td>
        </tr>
    </tbody>
</table><img src ="http://www.cppblog.com/flyonok/aggbug/38527.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyonok/" target="_blank">flyonok</a> 2007-12-14 16:47 <a href="http://www.cppblog.com/flyonok/archive/2007/12/14/38527.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用在STL中的引用类指针</title><link>http://www.cppblog.com/flyonok/archive/2007/12/14/38492.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Fri, 14 Dec 2007 04:00:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2007/12/14/38492.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/38492.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2007/12/14/38492.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/38492.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/38492.html</trackback:ping><description><![CDATA[<p>// cont/countptr.hpp</p>
<p>&nbsp;&nbsp; #ifndef COUNTED_PTR_HPP<br>&nbsp;&nbsp; #define COUNTED_PTR_HPP</p>
<p>&nbsp;&nbsp; /*class for counted reference semantics<br>&nbsp;&nbsp;&nbsp; *-deletes the object to which it refers when the last CountedPtr<br>&nbsp;&nbsp;&nbsp; * that refers to it is destroyed<br>&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp; template &lt;class T&gt;<br>&nbsp;&nbsp; class CountedPtr {<br>&nbsp;&nbsp;&nbsp;&nbsp; private:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; T* ptr;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // pointer to the value<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; long* count;&nbsp;&nbsp; // shared number of owners</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; public:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //initialize pointer with existing pointer<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //-requires that the pointer p is a return value of new<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; explicit CountedPtr (T* p=0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : ptr(p), count(new long(1)) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //copy pointer (one more owner)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CountedPtr (const CountedPtr&lt;T&gt;&amp; p) throw()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : ptr(p.ptr), count(p.count) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ++*count;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //destructor (delete value if this was the last owner)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ~CountedPtr () throw() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dispose();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //assignment (unshare old and share new value)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CountedPtr&lt;T&gt;&amp; operator= (const CountedPtr&lt;T&gt;&amp; p) throw() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (this != &amp;p) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dispose();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ptr = p.ptr;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; count = p.count;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ++*count;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return *this;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //access the value to which the pointer refers<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; T&amp; operator*() const throw() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return *ptr;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; T* operator-&gt;() const throw() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return ptr;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; private:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void dispose() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (--*count == 0) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; delete count;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; delete ptr;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp; };</p>
<p>&nbsp;&nbsp; #endif /*COUNTED_PTR_HPP*/<br></p><img src ="http://www.cppblog.com/flyonok/aggbug/38492.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyonok/" target="_blank">flyonok</a> 2007-12-14 12:00 <a href="http://www.cppblog.com/flyonok/archive/2007/12/14/38492.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>gdb</title><link>http://www.cppblog.com/flyonok/archive/2007/12/13/38442.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Thu, 13 Dec 2007 09:10:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2007/12/13/38442.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/38442.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2007/12/13/38442.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/38442.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/38442.html</trackback:ping><description><![CDATA[&nbsp;你可以在空闲的时候阅读全部的GDB手册。然而一些命令足以使你开始使用GDB，这一章就描述这些命令。 GNU m4（一个通用的宏处理器）的初始版本之一存在下面的bug：当我们改变单引号的默认表示时，一个用来捕获一个宏定义的命令停止工作。在下面较短的m4会话中，我们定义一个宏foo扩展成0000；然後我们使用m4内置的defn命令定义bar做同样的事情。然而当我们改变左单引号为&lt;QUOTE&gt;,右单引号为&lt;UNQUOTE&gt;後，同样的程序在定义一个新的同义词baz时却失败了。<br><br>$ <strong><wbr>cd gnu/m4</strong><wbr>$ <strong><wbr>./m4<br></strong><wbr><strong><wbr>define(foo,0000)</strong><wbr><strong><wbr>foo<br></strong><wbr>0000<strong><wbr>define(bar,defn(`foo'))</strong><wbr><strong><wbr>bar<br></strong><wbr>0000<strong><wbr>changequote(&lt;QUOTE&gt;,&lt;UNQUOTE&gt;)<br></strong><wbr><strong><wbr>define(baz,defn(&lt;QUOTE&gt;foo&lt;UNQUOTE&gt;))</strong><wbr><strong><wbr>baz</strong><wbr><strong><wbr>C-d</strong><wbr>m4: End of input: 0: fatal error: EOF in string<br>让我们使用GDB来看看发生了什么。 <br>$ <strong><wbr>gdb m4</strong><wbr><br>GDB is free software and you are welcome to distribute copies of it under certain conditions; type "show copying" to see the conditions.There is absolutely no warranty for GDB; type "show warranty" for details.GDB 6.5.50.20060706, Copyright 1999 Free Software Foundation, Inc...(gdb)<br>GDB只读取很少的符号数据，这些数据足以让它知道在哪里能找到剩下的它需要的符号数据，因此，第一个命令提示符显示的很快。现在我们告诉GDB使用比平时更短的显示宽度，这样这些例子可以更好的显示在这个手册中。 <br>(gdb) <strong><wbr>set width 70</strong><wbr><br><br>我们需要看一看m4内置的changquote是怎么工作的。看了源码之後，我们知道相关的子例程是m4_changequote，因为我们使用GDB的break命令在那里设置一个断点。 <br>(gdb) <strong><wbr>break m4_changequote</strong><wbr><br>Breakpoint 1 at 0x62f4: file builtin.c, line 879.<br>通过使用run命令，在GDB的控制下我们启动m4的运行。只要控制没有达到m4_changequote子例程，程序运行的和平时一样。 <br>(gdb) <strong><wbr>run<br></strong><wbr>Starting program: /work/Editorial/gdb/gnu/m4/m4<strong><wbr>define(foo,0000)</strong><wbr><strong><wbr>foo</strong><wbr>0000<br>为了触发断点，我们调用changequote。GDB悬挂m4的执行，显示它停止地方的上下文信息。 <br><strong><wbr>changequote(&lt;QUOTE&gt;,&lt;UNQUOTE&gt;)<br></strong><wbr>Breakpoint 1, m4_changequote (argc=3, argv=0x33c70)&nbsp;&nbsp;&nbsp;&nbsp;at builtin.c:879879&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (bad_argc(TOKEN_DATA_TEXT(argv[0]),argc,1,3))<br>现在我们使用命令n（next）执行当前函数的下一行。 <br><br>(gdb) <strong><wbr>n<br></strong><wbr>882&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set_quotes((argc &gt;= 2) ? TOKEN_DATA_TEXT(argv[1])\ : nil,<br>set_quotes看起来像是有问题的子例程，通过使用命令s（step）代替next我们能进入它的内部执行。 step会进入将被执行的子例程的下一行，因此它进入了set_quotes。 <br>(gdb) <strong><wbr>s</strong><wbr>set_quotes (lq=0x34c78 "&lt;QUOTE&gt;", rq=0x34c88 "")&nbsp;&nbsp;&nbsp;&nbsp;at input.c:530530&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (lquote != def_lquote)<br>上面展示了m4（以及它的参数）被悬挂在这个栈里，这个显示被称为栈桢显示。它显示了栈的概要。我们可以使用backtrace命令（它也可以写成bt）来看一看在所有的栈中我们处在哪一个栈里：backtrace命令显示每一个活动子例程的栈桢。 <br><br>(gdb) <strong><wbr>bt<br></strong><wbr>#0&nbsp;&nbsp;set_quotes (lq=0x34c78 "&lt;QUOTE&gt;", rq=0x34c88 "")&nbsp;&nbsp;&nbsp;&nbsp;at input.c:530#1&nbsp;&nbsp;0x6344 in m4_changequote (argc=3, argv=0x33c70)&nbsp;&nbsp;&nbsp;&nbsp;at builtin.c:882#2&nbsp;&nbsp;0x8174 in expand_macro (sym=0x33320) at macro.c:242#3&nbsp;&nbsp;0x7a88 in expand_token (obs=0x0, t=209696, td=0xf7fffa30)&nbsp;&nbsp;&nbsp;&nbsp;at macro.c:71#4&nbsp;&nbsp;0x79dc in expand_input () at macro.c:40#5&nbsp;&nbsp;0x2930 in main (argc=0, argv=0xf7fffb20) at m4.c:195我们再前进几行看看会发生什么。开始的两次，我们使用s；後面的两次我们使用n来避免进入 xstrdup子例程。 <br>(gdb) <strong><wbr>s</strong><wbr>0x3b5c&nbsp;&nbsp;532&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (rquote != def_rquote)(gdb) <strong><wbr>s</strong><wbr>0x3b80&nbsp;&nbsp;535&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lquote = (lq == nil || *lq == '\0') ?&nbsp;&nbsp;\def_lquote : xstrdup(lq);(gdb) <br><strong><wbr>n<br></strong><wbr>536&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rquote = (rq == nil || *rq == '\0') ? def_rquote\ : xstrdup(rq);(gdb) <strong><wbr>n</strong><wbr>538&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; len_lquote = strlen(rquote);<br>最後一行显示看起来有点奇怪。我们可以检查lquote和rquote变量，看看它们实际上是不是我们指定的新的左单引号和右单引号。我们使用命令p（print）来看它们的值。 <br>gdb) <strong><wbr>p lquote<br></strong><wbr>$1 = 0x35d40 "&lt;QUOTE&gt;"<br>(gdb) <strong><wbr>p rquote</strong><wbr>$2 = 0x35d50 "&lt;UNQUOTE&gt;"<br>lquote和rquote的确是新的左右单引号。为了看一些上下文，我们可以使用l（list)命令来显示围绕当前行的前後10行源代码。 <br>(gdb) <strong><wbr>l</strong><wbr>533&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xfree(rquote);534535&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lquote = (lq == nil || *lq == '\0') ? def_lquote\ : xstrdup (lq);536&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rquote = (rq == nil || *rq == '\0') ? def_rquote\ : xstrdup (rq);537538&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; len_lquote = strlen(rquote);539&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; len_rquote = strlen(lquote);540&nbsp;&nbsp;&nbsp;&nbsp; }541542&nbsp;&nbsp;&nbsp;&nbsp; void<br>让我们再向前执行两行设置len_lquote和len_rquote值的源代码，然後检查这些变量的值。<br><br>(gdb) <strong><wbr>n<br></strong><wbr>539&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; len_rquote = strlen(lquote);<br>(gdb) <strong><wbr>n<br></strong><wbr>540&nbsp;&nbsp;&nbsp;&nbsp; }<br>(gdb) <strong><wbr>p len_lquote<br></strong><wbr>$3 = 9<br>(gdb) <strong><wbr>p len_rquote</strong><wbr>$4 = 7<br>假如len_lquote和len_rquote分别意味着lquote和rquote的长度，这看起来一定有问题。因为p命令能打印任何表达式的值--那些表达式能包括子例程和赋值。通过使用它，我们能把它们设置成别的更好的值。<br><br>gdb) <strong><wbr>p len_lquote=strlen(lquote)</strong><wbr><br>$5 = 7(<br>gdb) <strong><wbr>p len_rquote=strlen(rquote)</strong><wbr><br>$6 = 9<br>对于修正使用m4内置的defn来设置新的引号引起的问题，这已经足够了吗？我们使用c（continue）命令可以让m4继续，然後试一试最初因为麻烦的那个例子。 <br><br>(gdb) <strong><wbr>c<br></strong><wbr>Continuing.<br><strong><wbr>define(baz,defn(&lt;QUOTE&gt;foo&lt;UNQUOTE&gt;))</strong><wbr>baz0000<br>成功了！新的引号现在和默认的引号一样工作正常。问题似乎是那两个定义了错误的长度的行。我们给m4输入一个EOF让它退出。 <br><strong><wbr>C-d<br></strong><wbr>Program exited normally.<br>这个消息`Program exited normally.'来自于GDB，它表明m4已经完成了执行。我们可以使用 GDB的quit命令来结束我们的GDB会话。 <br>(gdb) <strong><wbr>quit</strong><wbr><br><img src ="http://www.cppblog.com/flyonok/aggbug/38442.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyonok/" target="_blank">flyonok</a> 2007-12-13 17:10 <a href="http://www.cppblog.com/flyonok/archive/2007/12/13/38442.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ACE 在cgywin的编译</title><link>http://www.cppblog.com/flyonok/archive/2007/12/11/38276.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Tue, 11 Dec 2007 15:44:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2007/12/11/38276.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/38276.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2007/12/11/38276.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/38276.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/38276.html</trackback:ping><description><![CDATA[<p>因为最近要开发一个服务器项目，也打算学习一下ACE，所以就拿Cygwin玩玩ACE，</p>
<p>下载ACE5.5源码包之后，就马上开始编译：</p>
<p>首先参考ACE-Install.htm,其中说到Cygwin下的ACE的编译方法：</p>
<ol>
    <li>
    <p>Open a Cygwin shell. Set your <tt>PATH</tt> environment variable so your Cygwin <tt>bin</tt> directory is first: </p>
    <blockquote><code></code>
    <pre>       % export PATH=//c/cygwin/bin:$PATH       </pre>
    </blockquote>
    <p>Note Cygwin uses ``<tt>/</tt>'' as directory separator, and ``<tt>//X</tt>'' as a notation for Win32 drive <tt>X</tt>. Note also that you <em>can't</em> use ``<tt>c:/cygwin/bin</tt>'' because, for Cygwin, ``<tt>:</tt>'' is path separator character, as in UNIX. <br><br></p>
    </li>
    <li>Add an <tt>ACE_ROOT</tt> environment variable pointing to the root of your ACE wrappers source tree:
    <blockquote><code></code>
    <pre>       % export ACE_ROOT=c:/work/cygwin/ACE_wrappers       </pre>
    </blockquote>
    <p>Note here you <em>can't</em> use the ``<tt>//X</tt>'' Cygwin notation as this is seen by Cygwin's compiler and it doesn't support that (it <em>does</em> support ``<tt>/</tt>'' as directory separator however). </p>
    <p>From now on, we will refer to the root directory of the ACE source tree as <tt>$ACE_ROOT</tt>. <br><br></p>
    </li>
    <li>Create a file called <tt>config.h</tt> in the <tt>$ACE_ROOT/ace</tt> directory that contains:
    <blockquote><code></code>
    <pre>       #include "ace/config-cygwin32.h"       </pre>
    </blockquote>
    </li>
    <li>Create a file called <tt>platform_macros.GNU</tt> in the <tt>$ACE_ROOT/include/makeinclude</tt> directory containing:
    <blockquote><code></code>
    <pre>       include $(ACE_ROOT)/include/makeinclude/platform_cygwin32.GNU       </pre>
    </blockquote>In the above text, don't replace <tt>$(ACE_ROOT)</tt> with the actual directory, GNU make will take the value from the environment variable you defined previously.
    </li>
    <li>On the Cygwin shell, change to the $ACE_ROOT/ace directory and run make:
    <blockquote><code></code>
    <pre>       % cd $ACE_ROOT/ace       % make       </pre>
    </blockquote>
    <p>This should create <tt>libACE.dll</tt> (the Win32 shared library) and <tt>libACE.dll.a</tt> (the Win32 import library for the DLL). Note the name for the ACE DLL on Cygwin follows the UNIX convention. <br><br></p>
    <p>If you want static libs also, you may run: </p>
    <blockquote><code></code>
    <pre>       % make static_libs=1</pre>
    </blockquote></li>
</ol>
<blockquote>
<pre>但是死活编译不成功，make返回Error2,烦死。</pre>
<pre>google一下，大部分人都是这样编译过了，但是本机就死活不行，</pre>
<pre>后来终于找到了一个文章，里面说了一种不同于官方文档的编译方法：</pre>
<pre>先设置ACE_ROOT环境，命令：&#8220;vi /etc/profile&#8221;在其中加入4行<br>ACE_ROOT=~/ACE_wrappers export ACE_ROOTLD_LIBRARY_PATH=$ACE_ROOT/ace:$LD_LIBRARY_PATH export LD_LIBRARY_PATH我是加在&#8220;export PATH USER&#8230;.&#8221;后的。完成后将/etc/profile执行一次，<br>命令：&#8220;chmod 555 /etc/profile&#8221;&#8220;/etc/profile&#8221;这样我们的ACE_ROOT就设置好了，<br>可以用如下命令查看ACE_ROOT是否设置好了：&#8220;echo $ACE_ROOT&#8221;,</pre>
<pre>然后执行官方文档的3，4步骤，</pre>
<pre>最后：</pre>
<pre>       % cd $ACE_ROOT/ace       %  make static_libs=1</pre>
<pre>经过漫长的等待，终于搞定！发文记录一下。</pre>
<pre>&nbsp;</pre>
<pre>1、定义ACE_ROOT</pre>
<pre>export ACE_ROOT＝/datas/ACE_wrappers;</pre>
<pre>2、定义配置文件，$ACE_ROOT/ace/config.h,该文件包含和平台相关的配置文件：</pre>
<pre>如在linux平台下，内容为</pre>
<pre><blockquote><code>#include "ace/config-linux.h" <br>3、Create a build configuration file, <code>$ACE_ROOT/include/makeinclude/platform_macros.GNU</code>, that contains the appropriate platform/compiler-specific Makefile configurations, e.g., <br>&nbsp;&nbsp;&nbsp;include $(ACE_ROOT)/include/makeinclude/platform_linux.GNU <br>4、Note that because ACE builds shared libraries, you'll need to set LD_LIBRARY_PATH (or equivalent for your platform) to the directory where binary version of the ACE library is built into. For example, you probably want to do something like the following: <br>&nbsp;&nbsp;&nbsp;<code>% setenv LD_LIBRARY_PATH $ACE_ROOT/lib:$LD_LIBRARY_PATH<br>5、</code>When all this is done, hopefully all you'll need to do is type: </code><blockquote><code>% make<br>6、在fedora6上的config.h的第一行加上如下语句：<br>#define ACE_GCC_HAS_TEMPLATE_INSTANTIATION_VISIBILITY_ATTRS 1<br></code></blockquote></blockquote></pre>
</blockquote><img src ="http://www.cppblog.com/flyonok/aggbug/38276.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flyonok/" target="_blank">flyonok</a> 2007-12-11 23:44 <a href="http://www.cppblog.com/flyonok/archive/2007/12/11/38276.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>