﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-网络服务器软件开发/中间件开发，关注ACE/ICE/boost-随笔分类-网络服务器开发</title><link>http://www.cppblog.com/true/category/3803.html</link><description /><language>zh-cn</language><lastBuildDate>Sun, 29 May 2011 13:43:21 GMT</lastBuildDate><pubDate>Sun, 29 May 2011 13:43:21 GMT</pubDate><ttl>60</ttl><item><title>tcpdump的常用方式</title><link>http://www.cppblog.com/true/archive/2011/05/25/147104.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Wed, 25 May 2011 10:53:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2011/05/25/147104.html</guid><wfw:comment>http://www.cppblog.com/true/comments/147104.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2011/05/25/147104.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/147104.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/147104.html</trackback:ping><description><![CDATA[<p>1.sudo /usr/sbin/tcpdump -i eth1 -n port 7999</p>
<p>2.sudo /usr/sbin/tcpdump -i any -X -n -s0 port 7999</p>
<p>3.sudo /usr/sbin/tcpdump -i any -X -n -s0 dst port&nbsp; 7999 and ip[39]==0 and ip[40]==2<br />最后的&nbsp;ip[39]==0 and ip[40]==0是过滤条件，含义为，从ip头开始算起的第39个字节为十进制0，第40个字节为十进制2</p><img src ="http://www.cppblog.com/true/aggbug/147104.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2011-05-25 18:53 <a href="http://www.cppblog.com/true/archive/2011/05/25/147104.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Buffer类就应该简单，直观</title><link>http://www.cppblog.com/true/archive/2011/01/13/138422.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Wed, 12 Jan 2011 16:51:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2011/01/13/138422.html</guid><wfw:comment>http://www.cppblog.com/true/comments/138422.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2011/01/13/138422.html#Feedback</comments><slash:comments>16</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/138422.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/138422.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 最近几天一直在思考一个问题：我们需要什么样的网络基础开发包？libevent，asio，ace，还是自己封装？Buffer类，内存池&nbsp;&nbsp;<a href='http://www.cppblog.com/true/archive/2011/01/13/138422.html'>阅读全文</a><img src ="http://www.cppblog.com/true/aggbug/138422.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2011-01-13 00:51 <a href="http://www.cppblog.com/true/archive/2011/01/13/138422.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>winpcap抓取无线网卡上的数据包</title><link>http://www.cppblog.com/true/archive/2010/12/22/137232.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Wed, 22 Dec 2010 13:59:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2010/12/22/137232.html</guid><wfw:comment>http://www.cppblog.com/true/comments/137232.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2010/12/22/137232.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/137232.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/137232.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;wireshark（<a href="http://www.wireshark.org/">http://www.wireshark.org/</a>）是我经常用到的抓包工具，这对于网络程序的调试至关重要，特别是客户端人员和服务端人员都认为自己的代码没问题时，wireshark本身是开源的，在windows平台下基于&nbsp;winpcap（<a href="http://www.winpcap.org/">http://www.winpcap.org/</a>）开发的，安装wireshark的时候，会提示在线安装winpcap，今天在笔记本上用VS2008,编译了Examples-pcap下面的basic_dump和basic_dump_ex，不曾想到的是抓不到包，甚是奇怪，因为用wireshark抓包是可以的，因此怀疑是不是哪个参数设施不对，终于比对wireshark，得出结论：将pcap_open_live的第四个参数设为0，即不能打开混杂模式，<br><br>if ((adhandle= pcap_open_live(d-&gt;name,&nbsp;// name of the device<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 65536,&nbsp;&nbsp;&nbsp;// portion of the packet to capture. <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 65536 grants that the whole packet will be captured on all the MACs.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #0000ff"> 0</span>,&nbsp;&nbsp;&nbsp;&nbsp;// promiscuous mode (nonzero means promiscuous)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1000,&nbsp;&nbsp;&nbsp;// read timeout<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; errbuf&nbsp;&nbsp;&nbsp;// error buffer<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )) == NULL)<br>&nbsp;{<br>&nbsp;&nbsp;fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n", d-&gt;name);<br>&nbsp;&nbsp;/* Free the device list */<br>&nbsp;&nbsp;pcap_freealldevs(alldevs);<br>&nbsp;&nbsp;return -1;<br>&nbsp;} 
<img src ="http://www.cppblog.com/true/aggbug/137232.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2010-12-22 21:59 <a href="http://www.cppblog.com/true/archive/2010/12/22/137232.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>体验RakNet的RPC3</title><link>http://www.cppblog.com/true/archive/2009/10/05/97901.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Mon, 05 Oct 2009 12:50:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2009/10/05/97901.html</guid><wfw:comment>http://www.cppblog.com/true/comments/97901.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2009/10/05/97901.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/97901.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/97901.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 初次接触了RakNet的RPC3，RakNet是一个侧重于游戏的网络开发包&nbsp;&nbsp;<a href='http://www.cppblog.com/true/archive/2009/10/05/97901.html'>阅读全文</a><img src ="http://www.cppblog.com/true/aggbug/97901.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2009-10-05 20:50 <a href="http://www.cppblog.com/true/archive/2009/10/05/97901.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>协议设计之二  结构体类型的编码</title><link>http://www.cppblog.com/true/archive/2009/09/11/95949.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Fri, 11 Sep 2009 11:35:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2009/09/11/95949.html</guid><wfw:comment>http://www.cppblog.com/true/comments/95949.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2009/09/11/95949.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/95949.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/95949.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 结构体类型的序列化&nbsp;&nbsp;<a href='http://www.cppblog.com/true/archive/2009/09/11/95949.html'>阅读全文</a><img src ="http://www.cppblog.com/true/aggbug/95949.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2009-09-11 19:35 <a href="http://www.cppblog.com/true/archive/2009/09/11/95949.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>怪异的boost，fatal error LNK1104: 无法打开文件“libboost_system-vc80-mt-gd-1_35.lib”</title><link>http://www.cppblog.com/true/archive/2008/07/16/56283.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Wed, 16 Jul 2008 05:19:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2008/07/16/56283.html</guid><wfw:comment>http://www.cppblog.com/true/comments/56283.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2008/07/16/56283.html#Feedback</comments><slash:comments>13</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/56283.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/56283.html</trackback:ping><description><![CDATA[<p>#include &lt;iostream&gt;<br>#include &lt;boost/asio.hpp&gt;<br>#include &lt;boost/date_time/posix_time/posix_time.hpp&gt;</p>
<p>int main()<br>{<br>&nbsp;boost::asio::io_service io;</p>
<p>&nbsp;boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));<br>&nbsp;t.wait();</p>
<p>&nbsp;std::cout &lt;&lt; "Hello, world!\n";</p>
<p>&nbsp;return 0;<br>}<br></p>
上面是boost文档自带的代码，编译出现错误。代码是不需要链接libboost_system-vc80-mt-gd-1_35.lib，为什么还出现这样的错误呢？他默认连接？知道的，恢复一下：）
<img src ="http://www.cppblog.com/true/aggbug/56283.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2008-07-16 13:19 <a href="http://www.cppblog.com/true/archive/2008/07/16/56283.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>epoll()实现分析【ZT】</title><link>http://www.cppblog.com/true/archive/2008/06/01/51848.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Sun, 01 Jun 2008 14:55:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2008/06/01/51848.html</guid><wfw:comment>http://www.cppblog.com/true/comments/51848.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2008/06/01/51848.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/51848.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/51848.html</trackback:ping><description><![CDATA[<div class=tit>epoll()实现分析<br><br><a href="http://hi.baidu.com/rwen2012/blog/item/0f2f8c13eb7f3621dd5401a8.html">http://hi.baidu.com/rwen2012/blog/item/0f2f8c13eb7f3621dd5401a8.html</a></div>
<div class=date>2007-07-04 17:50</div>
<table style="TABLE-LAYOUT: fixed">
    <tbody>
        <tr>
            <td>
            <div class=cnt id=blog_text>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>/*<br>* This structure is stored inside the "private_data" member of the file<br>* structure and rapresent the main data sructure for the eventpoll<br>* interface.<br>*/<br>struct eventpoll {<br>&nbsp;&nbsp; /* Protect the this structure access */<br>&nbsp;&nbsp; rwlock_t lock;<br></div>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/true/aggbug/51848.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2008-06-01 22:55 <a href="http://www.cppblog.com/true/archive/2008/06/01/51848.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在Linux上开发网络服务器的一些相关细节:poll与epoll[转]</title><link>http://www.cppblog.com/true/archive/2008/06/01/51843.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Sun, 01 Jun 2008 14:15:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2008/06/01/51843.html</guid><wfw:comment>http://www.cppblog.com/true/comments/51843.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2008/06/01/51843.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/51843.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/51843.html</trackback:ping><description><![CDATA[<div class=tit><a href="http://hi.baidu.com/xproduct/blog/calendar/200706">http://hi.baidu.com/xproduct/blog/calendar/200706</a><br></div>
<div class=date>2007-06-30 23:58</div>
<table style="TABLE-LAYOUT: fixed">
    <tbody>
        <tr>
            <td>
            <div class=cnt>随着2.6内核对epoll的完全支持，网络上很多的文章和示例代码都提供了这样一个信息：使用epoll代替传统的poll能给网络服务应用带来性能上 的提升。但大多文章里关于性能提升的原因解释的较少，这里我将试分析一下内核（2.6.21.1）代码中poll与epoll的工作原理，然后再通过一些 测试数据来对比具体效果。<br><br>&nbsp;&nbsp; POLL：<br><br>&nbsp;&nbsp; 先说poll，poll或select为大部分Unix/Linux程序员所熟悉，这俩个东西原理类似，性能上也不存在明显差异，但select对所监控的文件描述符数量有限制，所以这里选用poll做说明。<br><br>&nbsp;&nbsp; poll是一个系统调用，其内核入口函数为sys_poll，sys_poll几乎不做任何处理直接调用do_sys_poll，do_sys_poll的执行过程可以分为三个部分：<br><br>&nbsp;&nbsp; 1，将用户传入的pollfd数组拷贝到内核空间，因为拷贝操作和数组长度相关，时间上这是一个O（n）操作，这一步的代码在do_sys_poll中包括从函数开始到调用do_poll前的部分。<br><br>&nbsp;&nbsp; 2，查询每个文件描述符对应设备的状态，如果该设备尚未就绪，则在该设备的等待队列中加入一项并继续查询下一设备的状态。查询完所有设备后如果没有一个设 备就绪，这时则需要挂起当前进程等待，直到设备就绪或者超时，挂起操作是通过调用schedule_timeout执行的。设备就绪后进程被通知继续运 行，这时再次遍历所有设备，以查找就绪设备。这一步因为两次遍历所有设备，时间复杂度也是O（n），这里面不包括等待时间。相关代码在do_poll函数 中。<br><br>&nbsp;&nbsp; 3，将获得的数据传送到用户空间并执行释放内存和剥离等待队列等善后工作，向用户空间拷贝数据与剥离等待队列等操作的的时间复杂度同样是O（n），具体代码包括do_sys_poll函数中调用do_poll后到结束的部分。<br><br>&nbsp;&nbsp; EPOLL：<br><br>&nbsp;&nbsp; 接下来分析epoll，与poll/select不同，epoll不再是一个单独的系统调用，而是由epoll_create/epoll_ctl/epoll_wait三个系统调用组成，后面将会看到这样做的好处。<br><br>&nbsp;&nbsp; 先来看sys_epoll_create(epoll_create对应的内核函数），这个函数主要是做一些准备工作，比如创建数据结构，初始化数据并最终返回一个文件描述符（表示新创建的虚拟epoll文件），这个操作可以认为是一个固定时间的操作。<br><br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; epoll是做为一个虚拟文件系统来实现的，这样做至少有以下两个好处：<br><br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 1，可以在内核里维护一些信息，这些信息在多次epoll_wait间是保持的，比如所有受监控的文件描述符。<br><br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 2， epoll本身也可以被poll/epoll;<br><br>&nbsp;&nbsp; 具体epoll的虚拟文件系统的实现和性能分析无关，不再赘述。<br><br>&nbsp;&nbsp; 在sys_epoll_create中还能看到一个细节，就是epoll_create的参数size在现阶段是没有意义的，只要大于零就行。<br><br>&nbsp;&nbsp; 接着是sys_epoll_ctl(epoll_ctl对应的内核函数），需要明确的是每次调用sys_epoll_ctl只处理一个文件描述符，这里主 要描述当op为EPOLL_CTL_ADD时的执行过程，sys_epoll_ctl做一些安全性检查后进入ep_insert，ep_insert里将 ep_poll_callback做为回掉函数加入设备的等待队列（假定这时设备尚未就绪），由于每次poll_ctl只操作一个文件描述符，因此也可以 认为这是一个O(1)操作<br><br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; ep_poll_callback函数很关键，它在所等待的设备就绪后被系统回掉，执行两个操作：<br><br>&nbsp;&nbsp; 1，将就绪设备加入就绪队列，这一步避免了像poll那样在设备就绪后再次轮询所有设备找就绪者，降低了时间复杂度，由O（n）到O（1）;<br><br>&nbsp;&nbsp; 2，唤醒虚拟的epoll文件;<br><br>&nbsp;&nbsp; 最后是sys_epoll_wait，这里实际执行操作的是ep_poll函数。该函数等待将进程自身插入虚拟epoll文件的等待队列，直到被唤醒（见 上面ep_poll_callback函数描述），最后执行ep_events_transfer将结果拷贝到用户空间。由于只拷贝就绪设备信息，所以这 里的拷贝是一个O(1）操作。<br><br>&nbsp;&nbsp; 还有一个让人关心的问题就是epoll对EPOLLET的处理，即边沿触发的处理，粗略看代码就是把一部分水平触发模式下内核做的工作交给用户来处理，直觉上不会对性能有太大影响，感兴趣的朋友欢迎讨论。<br><br>&nbsp;&nbsp; POLL/EPOLL对比：<br><br>&nbsp;&nbsp; 表面上poll的过程可以看作是由一次epoll_create/若干次epoll_ctl/一次epoll_wait/一次close等系统调用构成， 实际上epoll将poll分成若干部分实现的原因正是因为服务器软件中使用poll的特点（比如Web服务器）：<br><br>&nbsp;&nbsp; 1，需要同时poll大量文件描述符;<br><br>&nbsp;&nbsp; 2，每次poll完成后就绪的文件描述符只占所有被poll的描述符的很少一部分。<br><br>&nbsp;&nbsp; 3，前后多次poll调用对文件描述符数组（ufds）的修改只是很小; <br><br>&nbsp;&nbsp; 传统的poll函数相当于每次调用都重起炉灶，从用户空间完整读入ufds，完成后再次完全拷贝到用户空间，另外每次poll都需要对所有设备做至少做一次加入和删除等待队列操作，这些都是低效的原因。<br><br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; epoll将以上情况都细化考虑，不需要每次都完整读入输出ufds，只需使用epoll_ctl调整其中一小部分，不需要每次 epoll_wait都执行一次加入删除等待队列操作，另外改进后的机制使的不必在某个设备就绪后搜索整个设备数组进行查找，这些都能提高效率。另外最明 显的一点，从用户的使用来说，使用epoll不必每次都轮询所有返回结果已找出其中的就绪部分，O（n）变O（1），对性能也提高不少。<br><br>&nbsp;&nbsp; 此外这里还发现一点，是不是将epoll_ctl改成一次可以处理多个fd（像semctl那样）会提高些许性能呢？特别是在假设系统调用比较耗时的基础上。不过关于系统调用的耗时问题还会在以后分析。<br><br>&nbsp;&nbsp; POLL/EPOLL测试数据对比：<br><br>&nbsp;&nbsp; 测试的环境：我写了三段代码来分别模拟服务器，活动的客户端，僵死的客户端，服务器运行于一个自编译的标准2.6.11内核系统上，硬件为 PIII933，两个客户端各自运行在另外的PC上，这两台PC比服务器的硬件性能要好，主要是保证能轻易让服务器满载，三台机器间使用一个100M交换 机连接。<br><br>&nbsp;&nbsp; 服务器接受并poll所有连接，如果有request到达则回复一个response，然后继续poll。<br><br>&nbsp;&nbsp; 活动的客户端（Active Client）模拟若干并发的活动连接，这些连接不间断的发送请求接受回复。<br><br>&nbsp;&nbsp; 僵死的客户端（zombie）模拟一些只连接但不发送请求的客户端，其目的只是占用服务器的poll描述符资源。<br><br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 测试过程：保持10个并发活动连接，不断的调整僵并发连接数，记录在不同比例下使用poll与epoll的性能差别。僵死并发连接数根据比例分别是：0，10，20，40，80，160，320，640，1280，2560，5120，10240。<br><br>&nbsp;&nbsp; 下图中横轴表示僵死并发连接与活动并发连接之比，纵轴表示完成40000次请求回复所花费的时间，以秒为单位。红色线条表示poll数据，绿色表示 epoll数据。可以看出，poll在所监控的文件描述符数量增加时，其耗时呈线性增长，而epoll则维持了一个平稳的状态，几乎不受描述符个数影响。<br><br>&nbsp;&nbsp; 在监控的所有客户端都是活动时，poll的效率会略高于epoll（主要在原点附近，即僵死并发连接为0时，图上不易看出来），究竟epoll实现比poll复杂，监控少量描述符并非它的长</div>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/true/aggbug/51843.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2008-06-01 22:15 <a href="http://www.cppblog.com/true/archive/2008/06/01/51843.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>man epoll</title><link>http://www.cppblog.com/true/archive/2008/05/21/50647.html</link><dc:creator>true</dc:creator><author>true</author><pubDate>Wed, 21 May 2008 06:43:00 GMT</pubDate><guid>http://www.cppblog.com/true/archive/2008/05/21/50647.html</guid><wfw:comment>http://www.cppblog.com/true/comments/50647.html</wfw:comment><comments>http://www.cppblog.com/true/archive/2008/05/21/50647.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/true/comments/commentRss/50647.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/true/services/trackbacks/50647.html</trackback:ping><description><![CDATA[<pre>EPOLL(4)                   Linux Programmer's Manual                  EPOLL(4)
NAME
epoll - I/O event notification facility
SYNOPSIS
#include &lt;sys/epoll.h&gt;
DESCRIPTION
epoll  is a variant of poll(2) that can be used either as Edge or Level
Triggered interface and scales well to large numbers  of  watched  fds.
Three  system  calls  are  provided to set up and control an epoll set:
epoll_create(2), epoll_ctl(2), epoll_wait(2).
An epoll set is connected to a file descriptor  created  by  epoll_cre-
ate(2).   Interest  for certain file descriptors is then registered via
epoll_ctl(2).  Finally, the actual wait is started by epoll_wait(2).
NOTES
The epoll event distribution interface is able to behave both  as  Edge
Triggered  ( ET ) and Level Triggered ( LT ). The difference between ET
and LT event distribution mechanism can be described as  follows.  Sup-
pose that this scenario happens :
1      The file descriptor that represent the read side of a pipe ( RFD
) is added inside the epoll device.
2      Pipe writer writes 2Kb of data on the write side of the pipe.
3      A call to epoll_wait(2) is done that will return  RFD  as  ready
file descriptor.
4      The pipe reader reads 1Kb of data from RFD.
5      A call to epoll_wait(2) is done.
If  the RFD file descriptor has been added to the epoll interface using
the EPOLLET flag, the call to epoll_wait(2) done in step 5 will  proba-
bly  hang because of the available data still present in the file input
buffers and the remote peer might be expecting a response based on  the
data  it already sent. The reason for this is that Edge Triggered event
distribution delivers events only when events happens on the  monitored
file.  So, in step 5 the caller might end up waiting for some data that
is already present inside the input buffer. In the  above  example,  an
event on RFD will be generated because of the write done in 2 , and the
event is consumed in 3.  Since the read operation done in  4  does  not
consume the whole buffer data, the call to epoll_wait(2) done in step 5
might lock indefinitely. The epoll interface, when used with the  EPOL-
LET flag ( Edge Triggered ) should use non-blocking file descriptors to
avoid having a blocking read or write starve the task that is  handling
multiple  file  descriptors.  The suggested way to use epoll as an Edge
Triggered ( EPOLLET ) interface is  below,  and  possible  pitfalls  to
avoid follow.
i      with non-blocking file descriptors
ii     by  going  to  wait  for an event only after read(2) or write(2)
return EAGAIN
On the contrary, when used as a Level Triggered interface, epoll is  by
all means a faster poll(2), and can be used wherever the latter is used
since it shares the same semantics. Since even with the Edge  Triggered
epoll  multiple  events  can  be  generated  up on receival of multiple
chunks of data, the caller has the option to specify  the  EPOLLONESHOT
flag, to tell epoll to disable the associated file descriptor after the
receival of an event with epoll_wait(2).  When the EPOLLONESHOT flag is
specified,  it  is  caller  responsibility to rearm the file descriptor
using epoll_ctl(2) with EPOLL_CTL_MOD.
EXAMPLE FOR SUGGESTED USAGE
While the usage of epoll when employed like a Level Triggered interface
does  have  the  same  semantics  of  poll(2),  an Edge Triggered usage
requires more clarifiction to avoid stalls  in  the  application  event
loop.  In this example, listener is a non-blocking socket on which lis-
ten(2) has been called. The function do_use_fd()  uses  the  new  ready
file descriptor until EAGAIN is returned by either read(2) or write(2).
An event driven state machine application should, after having received
EAGAIN,  record  its  current  state  so  that  at  the  next  call  to
do_use_fd() it will continue to  read(2)  or  write(2)  from  where  it
stopped before.
struct epoll_event ev, *events;
for(;;) {
nfds = epoll_wait(kdpfd, events, maxevents, -1);
for(n = 0; n &lt; nfds; ++n) {
if(events[n].data.fd == listener) {
client = accept(listener, (struct sockaddr *) &amp;local,
&amp;addrlen);
if(client &lt; 0){
perror("accept");
continue;
}
setnonblocking(client);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = client;
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &amp;ev) &lt; 0) {
fprintf(stderr, "epoll set insertion error: fd=%d0,
client);
return -1;
}
}
else
do_use_fd(events[n].data.fd);
}
}
When  used  as an Edge triggered interface, for performance reasons, it
is possible to add the file descriptor inside  the  epoll  interface  (
EPOLL_CTL_ADD  )  once  by specifying ( EPOLLIN|EPOLLOUT ). This allows
you to avoid continuously switching between EPOLLIN and EPOLLOUT  call-
ing epoll_ctl(2) with EPOLL_CTL_MOD.
QUESTIONS AND ANSWERS (from linux-kernel)
Q1     What happens if you add the same fd to an epoll_set twice?
A1     You  will  probably get EEXIST. However, it is possible that two
threads may add the same fd twice. This is a harmless condition.
Q2     Can  two  epoll  sets  wait  for  the same fd? If so, are events
reported to both epoll sets fds?
A2     Yes. However, it is not recommended. Yes it would be reported to
both.
Q3     Is the epoll fd itself poll/epoll/selectable?
A3     Yes.
Q4     What happens if the epoll fd is put into its own fd set?
A4     It  will  fail.  However, you can add an epoll fd inside another
epoll fd set.
Q5     Can I send the epoll fd over a unix-socket to another process?
A5     No.
Q6     Will the close of an fd cause it to be removed  from  all  epoll
sets automatically?
A6     Yes.
Q7     If more than one event comes in between epoll_wait(2) calls, are
they combined or reported separately?
A7     They will be combined.
Q8     Does an operation on an fd affect the already collected but  not
yet reported events?
A8     You  can  do  two  operations on an existing fd. Remove would be
meaningless for this case. Modify will re-read available I/O.
Q9     Do I need to continuously read/write an  fd  until  EAGAIN  when
using the EPOLLET flag ( Edge Triggered behaviour ) ?
A9     No  you don't. Receiving an event from epoll_wait(2) should sug-
gest to you that such file descriptor is ready for the requested
I/O  operation.  You  have simply to consider it ready until you
will receive the next EAGAIN. When and how  you  will  use  such
file  descriptor is entirely up to you. Also, the condition that
the read/write I/O space is exhausted can be detected by  check-
ing  the  amount  of  data  read/write  from/to  the target file
descriptor. For example, if you call read(2) by asking to read a
certain  amount  of  data  and read(2) returns a lower number of
bytes, you can be sure to have exhausted the read I/O space  for
such  file  descriptor.  Same  is  valid  when writing using the
write(2) function.
POSSIBLE PITFALLS AND WAYS TO AVOID THEM
o Starvation ( Edge Triggered )
If there is a large amount of I/O space, it is possible that by  trying
to  drain it the other files will not get processed causing starvation.
This is not specific to epoll.
The solution is to maintain a ready list and mark the  file  descriptor
as  ready in its associated data structure, thereby allowing the appli-
cation to remember which files need to be  processed  but  still  round
robin  amongst  all the ready files. This also supports ignoring subse-
quent events you receive for fd's that are already ready.
o If using an event cache...
If you use  an  event  cache  or  store  all  the  fd's  returned  from
epoll_wait(2),  then  make  sure  to  provide a way to mark its closure
dynamically (ie- caused by a previous event's processing). Suppose  you
receive  100  events  from epoll_wait(2), and in eventi #47 a condition
causes event #13 to be closed.  If you remove the structure and close()
the  fd  for event #13, then your event cache might still say there are
events waiting for that fd causing confusion.
One solution for this is to call, during the processing  of  event  47,
epoll_ctl(EPOLL_CTL_DEL)  to  delete  fd  13 and close(), then mark its
associated data structure as removed and link it to a cleanup list.  If
you  find  another  event  for fd 13 in your batch processing, you will
discover the fd had been previously removed and there will be no confu-
sion.
CONFORMING TO
epoll(4) is a new API introduced in Linux kernel 2.5.44.  Its interface
should be finalized in Linux kernel 2.5.66.
SEE ALSO
epoll_create(2) epoll_ctl(2) epoll_wait(2)
Linux                           23 October 2002                       EPOLL(4)
</pre>
<img src ="http://www.cppblog.com/true/aggbug/50647.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/true/" target="_blank">true</a> 2008-05-21 14:43 <a href="http://www.cppblog.com/true/archive/2008/05/21/50647.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>