﻿<?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-随笔分类-network prog</title><link>http://www.cppblog.com/flyonok/category/14886.html</link><description /><language>zh-cn</language><lastBuildDate>Sun, 12 Sep 2010 19:55:01 GMT</lastBuildDate><pubDate>Sun, 12 Sep 2010 19:55:01 GMT</pubDate><ttl>60</ttl><item><title>在libevent中使用线程池--转摘</title><link>http://www.cppblog.com/flyonok/archive/2010/09/11/126368.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Fri, 10 Sep 2010 16:17:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2010/09/11/126368.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/126368.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2010/09/11/126368.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/126368.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/126368.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: libevent多线程&nbsp;&nbsp;<a href='http://www.cppblog.com/flyonok/archive/2010/09/11/126368.html'>阅读全文</a><img src ="http://www.cppblog.com/flyonok/aggbug/126368.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> 2010-09-11 00:17 <a href="http://www.cppblog.com/flyonok/archive/2010/09/11/126368.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用libevent编写linux服务--转摘</title><link>http://www.cppblog.com/flyonok/archive/2010/09/11/126367.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Fri, 10 Sep 2010 16:11:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2010/09/11/126367.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/126367.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2010/09/11/126367.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/126367.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/126367.html</trackback:ping><description><![CDATA[<p><span><font face=宋体>一</font></span><span> libevent</span><font face=宋体><span>简介</span></font></p>
<p><span>libevent</span><span><font face=宋体>是一个支持</font></span><span>Windows</span><span><font face=宋体>、</font></span><span>linux</span><span><font face=宋体>和</font></span><span>bsd</span><span><font face=宋体>等平台的网络事件驱动程序库。它支持多种</font></span><span>I/O</span><span><font face=宋体>服用机制，按照优先级从高到低依次为：</font></span><span>evport</span><span><font face=宋体>、</font></span><span>kqueue</span><span><font face=宋体>、</font></span><span>epoll</span><span><font face=宋体>、</font></span><span>devpoll</span><span><font face=宋体>、</font></span><span>rtsig</span><span><font face=宋体>、</font></span><span>poll</span><span><font face=宋体>、</font></span><span>select</span><font face=宋体><span>。它可根据操作系统，按照优先级从高到底自主选择驱动。</span></font></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span><font face=宋体>用户可以通过</font></span><span><a href="http://www.monkey.org/~provos/libevent/"><span>http://www.monkey.org/~provos/libevent/</span></a></span><span><font face=宋体>来获取</font></span><span>libevent</span><span><font face=宋体>的源码、</font></span><span>libevent</span><font face=宋体><span>出现的背景、以及其他一些详细资料。</span></font></p>
<p><span><font face=宋体>二</font></span><span> libevent</span><font face=宋体><span>的使用</span></font></p>
<p><font size=3><span>1 </span><span><font face=宋体>初始化事件</font></span><span>&nbsp;&nbsp;</span></font></p>
<p><span><font face=宋体>我们首先完成对</font></span><span>libenvent</span><font face=宋体><span>的事件初始化和事件驱动模型的选择。（在使用多线程的情况下，一般我们需获取所返回的事件根基）</span></font></p>
<p>&#160;</p>
<p><span><span>main_base = event_init();</span></span></p>
<p><span><span>&nbsp;&nbsp; </span></span></p>
<p><span>event_init</span><span><font face=宋体>函数返回的是一个</font></span><span>event_base</span><font face=宋体><span>对象，该对象包括了事件处理过程中的一些全局变量，其结构为：<br></p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;eventop&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">evsel;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">evbase;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;event_count;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;counts&nbsp;number&nbsp;of&nbsp;total&nbsp;events&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;event_count_active;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;counts&nbsp;number&nbsp;of&nbsp;active&nbsp;events&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br><br></span><span style="COLOR: #0000ff">&nbsp;&nbsp;&nbsp;&nbsp;int</span><span style="COLOR: #000000">&nbsp;event_gotterm;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;Set&nbsp;to&nbsp;terminate&nbsp;loop&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;event_break;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;Set&nbsp;to&nbsp;terminate&nbsp;loop&nbsp;immediately&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;active&nbsp;event&nbsp;management&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_list&nbsp;</span><span style="COLOR: #000000">**</span><span style="COLOR: #000000">activequeues;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;nactivequeues;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;signal&nbsp;handling&nbsp;info&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;evsignal_info&nbsp;sig;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_list&nbsp;eventqueue;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timeval&nbsp;event_tv;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;min_heap&nbsp;timeheap;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timeval&nbsp;tv_cache;<br>};<br><br></span></div>
<p><br></span></font></p>
<p><font size=3><span>2 </span><font face=宋体><span>添加事件</span></font></font></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span><font face=宋体>在事件初始化完毕后，我们可以使用</font></span><span>event_set</span><span><font face=宋体>设置事件，然后使用</font></span><span>event_add</span><font face=宋体><span>将其加入。</span></font></p>
<p><span>&nbsp;&nbsp; </span><span><font face=宋体>这里我们首先完成</font></span><span>socket</span><font face=宋体><span>的监听，然后将其加入的事件队列中（这里我们对所有的异常都不做考虑）。</span></font></p>
<p><span><font face=宋体>（</font></span><span>1</span><span><font face=宋体>）</font></span><span>socket</span><font face=宋体><span>监听<br></p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;sockaddr_in&nbsp;listen_addr;<br><br></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;port&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">10000</span><span style="COLOR: #000000">;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">socket监听端口</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000"><br></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;listen_fd&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;socket(AF_INET,&nbsp;SOCK_STREAM,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">);<br><br>memset(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">listen_addr,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(listen_addr));<br><br>listen_addr.sin_family&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;AF_INET;<br>listen_addr.sin_addr.s_addr&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;INADDR_ANY;<br>listen_addr.sin_port&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;htons(port)<br><br>reuseaddr_on&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;<br><br>setsockopt(listen_fd,&nbsp;SOL_SOCKET,&nbsp;SO_REUSEADDR,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">reuseaddr_on,&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(reuseaddr_on))&nbsp;#支持端口复用<br><br>bind(listen_fd,&nbsp;(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;sockaddr&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">listen_addr,&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(listen_addr))</span><span style="COLOR: #000000">'<br></span><span style="COLOR: #000000"><br>listen(listen_fd,&nbsp;</span><span style="COLOR: #000000">1024</span><span style="COLOR: #000000">);<br><br></span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">将描述符设置为非阻塞</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br><br></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;flags&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;fcntl(listen_fd,,&nbsp;F_GETFL);<br><br>flags&nbsp;</span><span style="COLOR: #000000">|=</span><span style="COLOR: #000000">&nbsp;O_NONBLOCK;<br><br>fcntl(listen_fd,&nbsp;F_SETFL,&nbsp;flags);<br><br></span></div>
<p><br></span></font></p>
<p><span><font face=宋体>（</font></span><span>2</span><font face=宋体><span>）事件设置</span></font></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; socket</span><span><font face=宋体>服务建立后，就可以进行事件设置。我们使用</font></span><span>event_set</span><span><font face=宋体>来设置事件对象，其传入参数包括事件根基</font></span><span>(event_base</span><span><font face=宋体>对象</font></span><span>)</span><span><font face=宋体>，描述符，事件类型，事件发生时的回调函数，回调函数传入参数。其中事件类型包括</font></span><span>EV_READ</span><span><font face=宋体>、</font></span><span>EV_WRITE</span><span><font face=宋体>、</font></span><span>EV_PERSIST</span><span><font face=宋体>，</font></span><span>EV_PERSIST</span><font face=宋体><span>和前两者结合使用，表示该事件为持续事件。</span></font></p>
<p><span>struct event ev;</span></p>
<p><span>event_set(&amp;ev, listen_fd, EV_READ | EV_PERSIST, accept_handle, (void *)&amp;ev); </span></p>
<p><span><font face=宋体>（</font></span><span>3</span><font face=宋体><span>）事件添加与删除</span></font></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span><font face=宋体>事件设置好后，就可以将其加入事件队列。</font></span><span>event_add</span><span><font face=宋体>用来将事件加入，它接受两个参数：要添加的事件和时间的超时值。</font></span><span> </span><span><font face=宋体>如果需要将事件删除，可以使用</font></span><span>event_del</span><span><font face=宋体>来完成。</font></span><span>event_del</span><font face=宋体><span>函数会取消所指定的事件。</span></font></p>
<p><span>event_add(&amp;ev, NULL)</span></p>
<p><font size=3><span>3 </span><font face=宋体><span>进入事件循环</span></font></font></p>
<p><span>&nbsp;&nbsp;&nbsp; </span><span><font face=宋体>事件成功添加后就是万事具备只欠东风了，</font></span><span>libevent</span><span><font face=宋体>提供了多种方式来进入事件循环，我个人常用的是</font></span><span>event_dispatch</span><span><font face=宋体>和</font></span><span>event_base_loop</span><span><font face=宋体>，前者最后实际是使用当前事件根基来调用</font></span><span>event_base_loop</span><font face=宋体><span>。</span></font></p>
<p><span>&nbsp;&nbsp; event_base_loop(main_base, 0);<br><br></p>
<p><font size=3><span>4 </span><font face=宋体><span>处理连接</span></font></font></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span><font face=宋体>到这里为止，大家已经完成了事件的设置、事件的添加并进入到了事件循环。但是当事件发生时（这里就是连接建立）如何处理呢？</font></span><span> </span><span><font face=宋体>聪明的用户会想到前面我们在事件设置时指定的回调函数</font></span><span>accept_handle</span><span><font face=宋体>。没错，当连接建立时回调函数</font></span><span>accept_handle</span><font face=宋体><span>会自动的得到调用。</span></font></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span><font face=宋体>对于缓冲区的读写在非阻塞式网络编程中是一个难以处理的问题，幸运的是</font></span><span>libevent</span><span><font face=宋体>提供了</font></span><span>bufferevent</span><span><font face=宋体>和</font></span><span>evbuf</span><span><font face=宋体>来替我们完成该项工作。这里我们采用</font></span><span>bufferevent</span><font face=宋体><span>来处理。</span></font></p>
<p><span>(1)</span><span><font face=宋体>生成</font></span><span>bufferevent</span><font face=宋体><span>对象</span></font></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span><font face=宋体>使用</font></span><span>bufferevent_new</span><span><font face=宋体>对象来生成</font></span><span>bufferevent</span><font face=宋体><span>对象，并分别指定读、写、连接错误时的处理函数和函数传入参数。</span></font></p>
<p><span>(2) </span><font face=宋体><span>设置读取量</span></font></p>
<p><span>bufferevent</span><span><font face=宋体>的读事件激活以后，即使用户没有读取完</font></span><span>bufferevent</span><span><font face=宋体>缓冲区中的数据</font></span><span>, bufferevent</span><span><font face=宋体>读事件也不会再次被激活。因为</font></span><span>bufferevent</span><span><font face=宋体>的读事件是由其所监控的描述符的读事件激活的，只有描述符可读，读事件才会被激活。可通过设置</font></span><span>wm_read.high</span><span><font face=宋体>来控制</font></span><span>bufferevent</span><font face=宋体><span>从描述符缓冲区中读取的数据量。</span></font></p>
<p><span>(3) </span><font face=宋体><span>将事件加入事件队列</span></font></p>
<p><span><font face=宋体>和前面一样，在事件设置好后，需将事件加入到事件队列中，</font></span><span> </span><span><font face=宋体>不过</font></span><span>bufferevent</span><span><font face=宋体>的有自己专门的加入函数</font></span><span>bufferevent_base_set</span><span><font face=宋体>和激活函数</font></span><span>bufferevent_enable</span><font face=宋体><span>。</span></font></p>
<p><span>bufferevent</span><span><font face=宋体>接收两个参数事件根基个事件对象，前者用来指定事件将加入到哪个事件根基中，后者说明需将那个</font></span><span>bufferevnet</span><font face=宋体><span>事件加入。（在多线程的情况下，每个线程可能有自己单独的事件根基）</span></font></p>
<p><span><font face=宋体>在</font></span><span>bufferevent</span><span><font face=宋体>初始化完毕后，可以使用</font></span><span>bufferevent_enable</span><span><font face=宋体>和</font></span><span>bufferevent_disable</span><span><font face=宋体>反复的激活与禁止事件，其接收参数为事件对象和事件标志。其中标志参数为</font></span><span>EV_READ</span><span><font face=宋体>和</font></span><span>EV_WRITE</span><font face=宋体><span>。<br></span></font></p>
<p><font size=3><span>&nbsp;</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;accept_handle(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;sfd,&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">short</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">arg)<br>{<br><br>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;sockaddr_in&nbsp;addr;<br><br>&nbsp;&nbsp;&nbsp;socklen_t&nbsp;addrlen&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(addr);<br><br>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;fd&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;accept(sfd,&nbsp;(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;sockaddr&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">addr,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">addrlen);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">处理连接</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;buf_ev&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;bufferevent_new(fd,&nbsp;&nbsp;&nbsp;buffered_on_read,&nbsp;NULL,&nbsp;NULL,&nbsp;fd)<br><br>&nbsp;&nbsp;&nbsp;&nbsp;buf_ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">wm_read.high&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">4096</span><span style="COLOR: #000000"><br><br>&nbsp;&nbsp;&nbsp; bufferevent_base_set(main_base,&nbsp;buf_ev);<br><br>&nbsp;&nbsp;&nbsp; bufferevent_enable(buf_ev,&nbsp;EV_READ);<br><br>}<br><br></span></div>
<p>5 </span><font face=宋体><span>读取缓冲区</span></font></font></p>
<p><span><font face=宋体>当缓冲区读就绪时会自动激活前面注册的缓冲区读函数，我们可以使用</font></span><span>bufferevent_read</span><font face=宋体><span>函数来读取缓冲区</span></font></p>
<p><span>bufferevent_read</span><span><font face=宋体>函数参数分别为</font></span><span>:</span><font face=宋体><span>所需读取的事件缓冲区，读入数据的存放地，希望读取的字节数。函数返回实际读取的字节数。</span></font></p>
<p><font face=宋体><span>注意：及时缓冲区未读完，事件也不会再次被激活（除非再次有数据）。因此此处需反复读取直到全部读取完毕。</span></font></p>
<p><span>6 </span><font face=宋体><span>写回客户端</span></font></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>bufferevent</span><font face=宋体><span>系列函数不但支持读取缓冲区，而且支持写缓冲区（即将结果返回给客户端）。</span></font></p>
<p>&#160;</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;buffered_on_read(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;bufferevent&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">bev,&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;arg){<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;buffer[</span><span style="COLOR: #000000">4096</span><span style="COLOR: #000000">]<br><br>&nbsp;&nbsp;&nbsp; ret&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;bufferevent_read(bev,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">buffer,&nbsp;</span><span style="COLOR: #000000">4096</span><span style="COLOR: #000000">);<br><br>&nbsp;&nbsp;&nbsp; bufferevent_write(bef,&nbsp;(</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">buffer,&nbsp;</span><span style="COLOR: #000000">4096</span><span style="COLOR: #000000">);<br><br>}<br><br></span></div>
<p>&#160;</p>
<p><span><font face=宋体>三</font></span><span> </span><font face=宋体><span>结束语</span></font></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span><font face=宋体>至此我们已经可以使用</font></span><span>libevent</span><span><font face=宋体>编写非阻塞的事件驱动服务器，它支持连接建立、</font></span><span>socket</span><font face=宋体><span>可读等事件的处理。</span></font></p>
<p><span><font face=宋体>但在实际的使用事件驱动的服务器中，通常是使用一个线程处理连接，然后使用多个线程来处理请求。后面我将继续介绍如何使用</font></span><span>libevent</span><span><font face=宋体>来编写多线程的服务器。</font></span></span></p>
<img src ="http://www.cppblog.com/flyonok/aggbug/126367.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> 2010-09-11 00:11 <a href="http://www.cppblog.com/flyonok/archive/2010/09/11/126367.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>libevent源码深度剖析十一 from csdn</title><link>http://www.cppblog.com/flyonok/archive/2010/09/10/126308.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Fri, 10 Sep 2010 06:04:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2010/09/10/126308.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/126308.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2010/09/10/126308.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/126308.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/126308.html</trackback:ping><description><![CDATA[<p style="TEXT-ALIGN: center">——时间管理<br>张亮</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 为了支持定时器，Libevent必须和系统时间打交道，这一部分的内容也比较简单，主要涉及到时间的加减辅助函数、时间缓存、时间校正和定时器堆的时间值调整等。下面就结合源代码来分析一下。</p>
<h3>1 初始化检测</h3>
<p>&nbsp;&nbsp;&nbsp; Libevent在初始化时会检测系统时间的类型，通过调用函数detect_monotonic()完成，它通过调用clock_gettime()来检测系统是否支持monotonic时钟类型：<br></p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;detect_monotonic(</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">)<br>{<br></span><span style="COLOR: #0000ff">#if</span><span style="COLOR: #000000">&nbsp;defined(HAVE_CLOCK_GETTIME)&nbsp;&amp;&amp;&nbsp;defined(CLOCK_MONOTONIC)</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timespec&nbsp;&nbsp;&nbsp;&nbsp;ts;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(clock_gettime(CLOCK_MONOTONIC,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">ts)&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;use_monotonic&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;系统支持monotonic时间</span><span style="COLOR: #008000"><br></span><span style="COLOR: #0000ff">#endif</span><span style="COLOR: #000000"><br>}</span></div>
Monotonic时间指示的是系统从boot后到现在所经过的时间，如果系统支持Monotonic时间就将全局变量use_monotonic设置为1，设置use_monotonic到底有什么用，这个在后面说到时间校正时就能看出来了。
<h3>2 时间缓存</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 结构体event_base中的tv_cache，用来记录时间缓存。这个还要从函数gettime()说起，先来看看该函数的代码：<br></p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;gettime(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timeval&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">tp)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;如果tv_cache时间缓存已设置，就直接使用</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">tv_cache.tv_sec)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">tp&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">tv_cache;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;如果支持monotonic，就用clock_gettime获取monotonic时间</span><span style="COLOR: #008000"><br></span><span style="COLOR: #0000ff">#if</span><span style="COLOR: #000000">&nbsp;defined(HAVE_CLOCK_GETTIME)&nbsp;&amp;&amp;&nbsp;defined(CLOCK_MONOTONIC)</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(use_monotonic)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timespec&nbsp;&nbsp;&nbsp;&nbsp;ts;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(clock_gettime(CLOCK_MONOTONIC,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">ts)&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tp</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">tv_sec&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;ts.tv_sec;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tp</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">tv_usec&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;ts.tv_nsec&nbsp;</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1000</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></span><span style="COLOR: #0000ff">#endif</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;否则只能取得系统当前时间</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;(evutil_gettimeofday(tp,&nbsp;NULL));<br>}</span></div>
如果tv_cache已经设置，那么就直接使用缓存的时间；否则需要再次执行系统调用获取系统时间。<br>&nbsp;&nbsp;&nbsp;&nbsp; 函数evutil_gettimeofday()用来获取当前系统时间，在Linux下其实就是系统调用gettimeofday()；Windows没有提供函数gettimeofday，而是通过调用_ftime()来完成的。<br>&nbsp;&nbsp;&nbsp;&nbsp; 在每次系统事件循环中，时间缓存tv_cache将会被相应的清空和设置，再次来看看下面event_base_loop的主要代码逻辑：<br>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;event_base_loop(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;flags)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;清空时间缓存</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">tv_cache.tv_sec&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">done){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timeout_correct(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">tv);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;时间校正<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;更新event_tv到tv_cache指示的时间或者当前时间（第一次）<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;event_tv&nbsp;&lt;---&nbsp;tv_cache</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gettime(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">event_tv);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;清空时间缓存--&nbsp;时间点1</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">tv_cache.tv_sec&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;等待I/O事件就绪</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;res&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;evsel</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">dispatch(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;evbase,&nbsp;tv_p);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;缓存tv_cache存储了当前时间的值--&nbsp;时间点2<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;tv_cache&nbsp;&lt;---&nbsp;now</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gettime(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">tv_cache);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;..&nbsp;处理就绪事件</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;退出时也要清空时间缓存</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">tv_cache.tv_sec&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">);<br>}</span></div>
<p>时间event_tv指示了dispatch()上次返回，也就是I/O事件就绪时的时间，第一次进入循环时，由于tv_cache被清空，因此gettime()执行系统调用获取当前系统时间；而后将会更新为tv_cache指示的时间。<br>&nbsp;&nbsp;&nbsp;&nbsp; 时间tv_cache在dispatch()返回后被设置为当前系统时间，因此它缓存了本次I/O事件就绪时的时间（event_tv）。<br>从代码逻辑里可以看出event_tv取得的是tv_cache上一次的值，因此event_tv应该小于tv_cache的值。<br>&nbsp;&nbsp;&nbsp;&nbsp; 设置时间缓存的优点是不必每次获取时间都执行系统调用，这是个相对费时的操作；在上面标注的时间点2到时间点1的这段时间（处理就绪事件时），调用gettime()取得的都是tv_cache缓存的时间。</p>
<p>3 时间校正<br>&nbsp;&nbsp;&nbsp;&nbsp; 如果系统支持monotonic时间，该时间是系统从boot后到现在所经过的时间，因此不需要执行校正。<br>根据前面的代码逻辑，如果系统不支持monotonic时间，用户可能会手动的调整时间，如果时间被向前调整了（MS前面第7部分讲成了向后调整，要改正），比如从5点调整到了3点，那么在时间点2取得的值可能会小于上次的时间，这就需要调整了，下面来看看校正的具体代码，由函数timeout_correct()完成：</p>
<p>&#160;</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;timeout_correct(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timeval&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">tv)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">**</span><span style="COLOR: #000000">pev;<br>&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;size;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timeval&nbsp;off;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(use_monotonic)&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;monotonic时间就直接返回，无需调整</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;gettime(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;tv);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;tv&nbsp;&lt;---tv_cache<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;根据前面的分析可以知道event_tv应该小于tv_cache<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;如果tv&nbsp;&lt;&nbsp;event_tv表明用户向前调整时间了，需要校正时间</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(evutil_timercmp(tv,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">event_tv,&nbsp;</span><span style="COLOR: #000000">&gt;=</span><span style="COLOR: #000000">))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">event_tv&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">tv;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;计算时间差值</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;evutil_timersub(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">event_tv,&nbsp;tv,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">off);<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;调整定时事件小根堆</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;pev&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">timeheap.p;<br>&nbsp;&nbsp;&nbsp;&nbsp;size&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">timeheap.n;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">&nbsp;(;&nbsp;size</span><span style="COLOR: #000000">--</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">pev)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timeval&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">ev_tv&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">**</span><span style="COLOR: #000000">pev).ev_timeout;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;evutil_timersub(ev_tv,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">off,&nbsp;ev_tv);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">event_tv&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">tv;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;更新event_tv为tv_cache</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">}</span></div>
<p>&nbsp;在调整小根堆时，因为所有定时事件的时间值都会被减去相同的值，因此虽然堆中元素的时间键值改变了，但是相对关系并没有改变，不会改变堆的整体结构。因此只需要遍历堆中的所有元素，将每个元素的时间键值减去相同的值即可完成调整，不需要重新调整堆的结构。<br>当然调整完后，要将event_tv值重新设置为tv_cache值了。</p>
<h3>4 小节</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 主要分析了一下libevent对系统时间的处理，时间缓存、时间校正和定时堆的时间值调整等，逻辑还是很简单的，时间的加减、设置等辅助函数则非常简单，主要在头文件evutil.h中，就不再多说了。</p>
<img src ="http://www.cppblog.com/flyonok/aggbug/126308.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> 2010-09-10 14:04 <a href="http://www.cppblog.com/flyonok/archive/2010/09/10/126308.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>libevent源码深度剖析十 -- from csdn</title><link>http://www.cppblog.com/flyonok/archive/2010/09/10/126295.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Fri, 10 Sep 2010 03:25:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2010/09/10/126295.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/126295.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2010/09/10/126295.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/126295.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/126295.html</trackback:ping><description><![CDATA[<p style="TEXT-ALIGN: center">——支持I/O多路复用技术<br>张亮</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Libevent的核心是事件驱动、同步非阻塞，为了达到这一目标，必须采用系统提供的I/O多路复用技术，而这些在Windows、Linux、Unix等不同平台上却各有不同，如何能提供优雅而统一的支持方式，是首要关键的问题，这其实不难，本节就来分析一下。</p>
<h3>1 统一的关键</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Libevent支持多种I/O多路复用技术的关键就在于结构体eventop，这个结构体前面也曾提到过，它的成员是一系列的函数指针, 定义在event-internal.h文件中：<br></p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;eventop&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">name;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">init)(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;初始化</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">add)(</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;注册事件</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">del)(</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;删除事件</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">dispatch)(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timeval&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;事件分发</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">dealloc)(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;注销，释放资源</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;set&nbsp;if&nbsp;we&nbsp;need&nbsp;to&nbsp;reinitialize&nbsp;the&nbsp;event&nbsp;base&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;need_reinit;<br>};</span></div>
&nbsp; 在libevent中，每种I/O demultiplex机制的实现都必须提供这五个函数接口，来完成自身的初始化、销毁释放；对事件的注册、注销和分发。<br>比如对于epoll，libevent实现了5个对应的接口函数，并在初始化时并将eventop的5个函数指针指向这5个函数，那么程序就可以使用epoll作为I/O demultiplex机制了。
<h3>2 设置I/O demultiplex机制</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Libevent把所有支持的I/O demultiplex机制存储在一个全局静态数组eventops中，并在初始化时选择使用何种机制，数组内容根据优先级顺序声明如下：<br></p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;In&nbsp;order&nbsp;of&nbsp;preference&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br></span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;eventop&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">eventops[]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;{<br>#ifdef&nbsp;HAVE_EVENT_PORTS<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">evportops,<br></span><span style="COLOR: #0000ff">#endif</span><span style="COLOR: #000000"><br>#ifdef&nbsp;HAVE_WORKING_KQUEUE<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">kqops,<br></span><span style="COLOR: #0000ff">#endif</span><span style="COLOR: #000000"><br>#ifdef&nbsp;HAVE_EPOLL<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">epollops,<br></span><span style="COLOR: #0000ff">#endif</span><span style="COLOR: #000000"><br>#ifdef&nbsp;HAVE_DEVPOLL<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">devpollops,<br></span><span style="COLOR: #0000ff">#endif</span><span style="COLOR: #000000"><br>#ifdef&nbsp;HAVE_POLL<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">pollops,<br></span><span style="COLOR: #0000ff">#endif</span><span style="COLOR: #000000"><br>#ifdef&nbsp;HAVE_SELECT<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">selectops,<br></span><span style="COLOR: #0000ff">#endif</span><span style="COLOR: #000000"><br>#ifdef&nbsp;WIN32<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">win32ops,<br></span><span style="COLOR: #0000ff">#endif</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;NULL<br>};&nbsp;</span></div>
然后libevent根据系统配置和编译选项决定使用哪一种I/O demultiplex机制，这段代码在函数event_base_new()中：<br>&nbsp;
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">evbase&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;NULL;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">&nbsp;(i&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;eventops[i]&nbsp;</span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">!</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">evbase;&nbsp;i</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">evsel&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;eventops[i];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">evbase&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">evsel</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">init(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">nbsp;</span></div>
可以看出，libevent在编译阶段选择系统的I/O demultiplex机制，而不支持在运行阶段根据配置再次选择。<br>&nbsp;&nbsp;&nbsp; 以Linux下面的epoll为例，实现在源文件epoll.c中，eventops对象epollops定义如下:<br>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;eventop&nbsp;epollops&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">epoll</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,<br>&nbsp;&nbsp;&nbsp;&nbsp;epoll_init,<br>&nbsp;&nbsp;&nbsp;&nbsp;epoll_add,<br>&nbsp;&nbsp;&nbsp;&nbsp;epoll_del,<br>&nbsp;&nbsp;&nbsp;&nbsp;epoll_dispatch,<br>&nbsp;&nbsp;&nbsp;&nbsp;epoll_dealloc,<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;need&nbsp;reinit&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br>};</span></div>
变量epollops中的函数指针具体声明如下，注意到其返回值和参数都和eventop中的定义严格一致，这是函数指针的语法限制。<br>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">epoll_init&nbsp;&nbsp;&nbsp;&nbsp;(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">);<br></span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;epoll_add&nbsp;&nbsp;&nbsp;&nbsp;(</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">);<br></span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;epoll_del&nbsp;&nbsp;&nbsp;&nbsp;(</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">);<br></span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;epoll_dispatch(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timeval&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">);<br></span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;epoll_dealloc&nbsp;&nbsp;&nbsp;&nbsp;(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">);</span></div>
<p>那么如果选择的是epoll，那么调用结构体eventop的init和dispatch函数指针时，实际调用的函数就是epoll的初始化函数epoll_init()和事件分发函数epoll_dispatch()了；<br>C++语言提供了虚函数来实现多态，在C语言中，这是通过函数指针实现的。对于各类函数指针的详细说明可以参见文章：<br><a href="http://blog.csdn.net/sparkliang/archive/2009/06/09/4254115.aspx">http://blog.csdn.net/sparkliang/archive/2009/06/09/4254115.aspx</a> <br>同样的，上面epollops以及epoll的各种函数都直接定义在了epoll.c源文件中，对外都是不可见的。对于libevent的使用者而言，完全不会知道它们的存在，对epoll的使用也是通过eventop来完成的，达到了信息隐藏的目的。</p>
<p>3 小节<br>&nbsp;&nbsp;&nbsp; 支持多种I/O demultiplex机制的方法其实挺简单的，借助于函数指针就OK了。通过对源代码的分析也可以看出，Libevent是在编译阶段选择系统的I/O demultiplex机制的，而不支持在运行阶段根据配置再次选择。</p>
<p><a href="http://blog.csdn.net/sparkliang/archive/2009/12/30/5105392.aspx"></a>&nbsp;</p>
<img src ="http://www.cppblog.com/flyonok/aggbug/126295.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> 2010-09-10 11:25 <a href="http://www.cppblog.com/flyonok/archive/2010/09/10/126295.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux Epoll介绍和程序实例--from csdn</title><link>http://www.cppblog.com/flyonok/archive/2010/09/10/126294.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Fri, 10 Sep 2010 03:19:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2010/09/10/126294.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/126294.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2010/09/10/126294.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/126294.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/126294.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 1. Epoll 是何方神圣？ Epoll 可是当前在 Linux 下开发大规模并发网络程序的热门人选， Epoll 在 Linux2.6 内核中正式引入，和 select 相似，其实都 I/O 多路复用技术而已 ，并没有什么神秘的。 其实在 Linux 下设计并发网络程序，向来不缺少方法，比如典型的 Apache 模型（ Process Per Connection ，简称 PPC ），...&nbsp;&nbsp;<a href='http://www.cppblog.com/flyonok/archive/2010/09/10/126294.html'>阅读全文</a><img src ="http://www.cppblog.com/flyonok/aggbug/126294.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> 2010-09-10 11:19 <a href="http://www.cppblog.com/flyonok/archive/2010/09/10/126294.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>libevent源码深度剖析九</title><link>http://www.cppblog.com/flyonok/archive/2010/09/09/126254.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Thu, 09 Sep 2010 10:07:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2010/09/09/126254.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/126254.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2010/09/09/126254.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/126254.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/126254.html</trackback:ping><description><![CDATA[<p>——集成定时器事件<br>张亮</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 现在再来详细分析libevent中I/O事件和Timer事件的集成，与Signal相比，Timer事件的集成会直观和简单很多。Libevent对堆的调整操作做了一些优化，本节还会描述这些优化方法。</p>
<p>1 集成到事件主循环<br>&nbsp;&nbsp;&nbsp;&nbsp; 因为系统的I/O机制像select()和epoll_wait()都允许程序制定一个最大等待时间（也称为最大超时时间）timeout，即使没有I/O事件发生，它们也保证能在timeout时间内返回。<br>那么根据所有Timer事件的最小超时时间来设置系统I/O的timeout时间；当系统I/O返回时，再激活所有就绪的Timer事件就可以了，这样就能将Timer事件完美的融合到系统的I/O机制中了。<br>具体的代码在源文件event.c的event_base_loop()中，现在就对比代码来看看这一处理方法：</p>
<p>&nbsp;</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">!</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">event_count_active&nbsp;</span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">(flags&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;EVLOOP_NONBLOCK))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;根据Timer事件计算evsel-&gt;dispatch的最大等待时间</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timeout_next(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">tv_p);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000">&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;如果还有活动事件，就不要等待，让evsel-&gt;dispatch立即返回</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;evutil_timerclear(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">tv);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;<img src="http://www.cppblog.com/Images/dot.gif"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;调用select()&nbsp;or&nbsp;epoll_wait()&nbsp;等待就绪I/O事件</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;res&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;evsel</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">dispatch(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;evbase,&nbsp;tv_p);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;<img src="http://www.cppblog.com/Images/dot.gif"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;处理超时事件，将超时事件插入到激活链表中</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timeout_process(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">);</span></div>
&nbsp; timeout_next()函数根据堆中具有最小超时值的事件和当前时间来计算等待时间，下面看看代码：<br>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;timeout_next(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timeval&nbsp;</span><span style="COLOR: #000000">**</span><span style="COLOR: #000000">tv_p)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timeval&nbsp;now;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">ev;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timeval&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">tv&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">tv_p;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;堆的首元素具有最小的超时值</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;((ev&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;min_heap_top(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">timeheap))&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;NULL)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;如果没有定时事件，将等待时间设置为NULL,表示一直阻塞直到有I/O事件发生</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">tv_p&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;NULL;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;取得当前时间</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;gettime(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">now);<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;如果超时时间&lt;=当前值，不能等待，需要立即返回</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(evutil_timercmp(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_timeout,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">now,&nbsp;</span><span style="COLOR: #000000">&lt;=</span><span style="COLOR: #000000">))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;evutil_timerclear(tv);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;计算等待的时间=当前时间-最小的超时时间</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;evutil_timersub(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_timeout,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">now,&nbsp;tv);<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">);<br>}</span></div>
<h3>2 Timer小根堆</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Libevent使用堆来管理Timer事件，其key值就是事件的超时时间，源代码位于文件min_heap.h中。<br>所有的数据结构书中都有关于堆的详细介绍，向堆中插入、删除元素时间复杂度都是O(lgN)，N为堆中元素的个数，而获取最小key值（小根堆）的复杂度为O(1)。堆是一个完全二叉树，基本存储方式是一个数组。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Libevent实现的堆还是比较轻巧的，虽然我不喜欢这种编码方式（搞一些复杂的表达式）。轻巧到什么地方呢，就以插入元素为例，来对比说明，下面伪代码中的size表示当前堆的元素个数：<br>典型的代码逻辑如下：<br></p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #000000">Heap[size</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;先放到数组末尾，元素个数+1<br></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;下面就是shift_up()的代码逻辑，不断的将new向上调整</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">_child&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;size;<br></span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">(_child</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;循环</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">{<br>&nbsp;&nbsp;&nbsp;_parent&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(_child</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;计算parent</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(Heap[_parent].key&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">&nbsp;Heap[_child].key)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">break</span><span style="COLOR: #000000">;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;调整结束，跳出循环</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;swap(_parent,&nbsp;_child);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;交换parent和child</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">}</span></div>
&nbsp; 而libevent的heap代码对这一过程做了优化，在插入新元素时，只是为新元素预留了一个位置hole（初始时hole位于数组尾部），但并不立刻将新元素插入到hole上，而是不断向上调整hole的值，将父节点向下调整，最后确认hole就是新元素的所在位置时，才会真正的将新元素插入到hole上，因此在调整过程中就比上面的代码少了一次赋值的操作，代码逻辑是：<br>&nbsp;&nbsp;&nbsp;&nbsp; 下面就是shift_up()的代码逻辑，不断的将new的&#8220;预留位置&#8221;向上调整<br>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;下面就是shift_up()的代码逻辑，不断的将new的&#8220;预留位置&#8221;向上调整</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">_hole&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;size;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;_hole就是为new预留的位置，但并不立刻将new放上</span><span style="COLOR: #008000"><br></span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">(_hole</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;循环</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">{<br>&nbsp;&nbsp;&nbsp;&nbsp;_parent&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(_hole</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;计算parent</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(Heap[_parent].key&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">.key)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">break</span><span style="COLOR: #000000">;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;调整结束，跳出循环</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;Heap[_hole]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;Heap[_parent];&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;将parent向下调整</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;_hole&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;_parent;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;将_hole调整到_parent</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">}<br>Heap[_hole]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;调整结束，将new插入到_hole指示的位置</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">size</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;元素个数+1</span></div>
由于每次调整都少做一次赋值操作，在调整路径比较长时，调整效率会比第一种有所提高。libevent中的min_heap_shift_up_()函数就是上面逻辑的具体实现，对应的向下调整函数是min_heap_shift_down_()。<br>举个例子，向一个小根堆3, 5, 8, 7, 12中插入新元素2，使用第一中典型的代码逻辑，其调整过程如下图所示：<br>&nbsp;<img alt="heap insert" src="http://p.blog.csdn.net/images/p_blog_csdn_net/sparkliang/EntryImages/20091222/heap1.JPG" width=452 height=133> <br>使用libevent中的堆调整逻辑，调整过程如下图所示：<br>&nbsp;<img alt="heap insert" src="http://p.blog.csdn.net/images/p_blog_csdn_net/sparkliang/EntryImages/20091222/heap2.JPG" width=464 height=176><br>对于删除和元素修改操作，也遵从相同的逻辑，就不再罗嗦了。
<h3>3 小节</h3>
<p>通过设置系统I/O机制的wait时间，从而简捷的集成Timer事件；主要分析了libevent对堆调整操作的优化</p>
<img src ="http://www.cppblog.com/flyonok/aggbug/126254.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> 2010-09-09 18:07 <a href="http://www.cppblog.com/flyonok/archive/2010/09/09/126254.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>libevent源码深度剖析八--from csdn</title><link>http://www.cppblog.com/flyonok/archive/2010/09/09/126253.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Thu, 09 Sep 2010 09:59:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2010/09/09/126253.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/126253.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2010/09/09/126253.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/126253.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/126253.html</trackback:ping><description><![CDATA[<p>&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>&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;张亮</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 现在我们已经了解了libevent的基本框架：事件管理框架和事件主循环。上节提到了libevent中I/O事件和Signal以及Timer事件的集成，这一节将分析如何将Signal集成到事件主循环的框架中。</p>
<p>1 集成策略——使用socket pair<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 前一节已经做了足够多的介绍了，基本方法就是采用&#8220;消息机制&#8221;。在libevent中这是通过socket pair完成的，下面就来详细分析一下。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Socket pair就是一个socket对，包含两个socket，一个读socket，一个写socket。工作方式如下图所示：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/flyonok/sockpair.jpg" width=406 height=184>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; 创建一个socket pair并不是复杂的操作，可以参见下面的流程图，清晰起见，其中忽略了一些错误处理和检查。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/flyonok/sockpairflow.jpg" width=406 height=421>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>Libevent提供了辅助函数evutil_socketpair()来创建一个socket pair，可以结合上面的创建流程来分析该函数。</p>
<p>2 集成到事件主循环——通知event_base<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Socket pair创建好了，可是libevent的事件主循环还是不知道Signal是否发生了啊，看来我们还差了最后一步，那就是：为socket pair的读socket在libevent的event_base实例上注册一个persist的读事件。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这样当向写socket写入数据时，读socket就会得到通知，触发读事件，从而event_base就能相应的得到通知了。<br>前面提到过，Libevent会在事件主循环中检查标记，来确定是否有触发的signal，如果标记被设置就处理这些signal，这段代码在各个具体的I/O机制中，以Epoll为例，在epoll_dispatch()函数中，代码片段如下：</p>
<p>&nbsp;</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;res&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;epoll_wait(epollop</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">epfd,&nbsp;events,&nbsp;epollop</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">nevents,&nbsp;timeout);<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(res&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(errno&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;EINTR)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;event_warn(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">epoll_wait</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;evsignal_process(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;处理signal事件</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">sig.evsignal_caught)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;evsignal_process(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;处理signal事件</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<p>完整的处理框架如下所示：<br><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/flyonok/frame.jpg" width=566 height=422><br>注1：libevent中，初始化阶段并不注册读socket的读事件，而是在注册信号阶段才会测试并注册；<br>注2：libevent中，检查I/O事件是在各系统I/O机制的dispatch()函数中完成的，该dispatch()函数在event_base_loop()函数中被调用；</p>
<h3>3 evsignal_info结构体</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Libevent中Signal事件的管理是通过结构体evsignal_info完成的，结构体位于evsignal.h文件中，定义如下：<br></p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;evsignal_info&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">&nbsp;ev_signal;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;ev_signal_pair[</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">];<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;ev_signal_added;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">volatile</span><span style="COLOR: #000000">&nbsp;sig_atomic_t&nbsp;evsignal_caught;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_list&nbsp;evsigevents[NSIG];<br>&nbsp;&nbsp;&nbsp;&nbsp;sig_atomic_t&nbsp;evsigcaught[NSIG];<br>#ifdef&nbsp;HAVE_SIGACTION<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;sigaction&nbsp;</span><span style="COLOR: #000000">**</span><span style="COLOR: #000000">sh_old;<br></span><span style="COLOR: #0000ff">#else</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;ev_sighandler_t&nbsp;</span><span style="COLOR: #000000">**</span><span style="COLOR: #000000">sh_old;<br></span><span style="COLOR: #0000ff">#endif</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;sh_old_max;<br>};</span></div>
<p>下面详细介绍一下个字段的含义和作用：<br>1）ev_signal， 为socket pair的读socket向event_base注册读事件时使用的event结构体；<br>2）ev_signal_pair，socket pair对，作用见第一节的介绍；<br>3）ev_signal_added，记录ev_signal事件是否已经注册了；<br>4）evsignal_caught，是否有信号发生的标记；是volatile类型，因为它会在另外的线程中被修改；<br>5）evsigvents[NSIG]，数组，evsigevents[signo]表示注册到信号signo的事件链表；<br>6）evsigcaught[NSIG]，具体记录每个信号触发的次数，evsigcaught[signo]是记录信号signo被触发的次数；<br>7）sh_old记录了原来的signal处理函数指针，当信号signo注册的event被清空时，需要重新设置其处理函数；<br>&nbsp;&nbsp;&nbsp; evsignal_info的初始化包括，创建socket pair，设置ev_signal事件（但并没有注册，而是等到有信号注册时才检查并注册），并将所有标记置零，初始化信号的注册事件链表指针等。</p>
<p>4 注册、注销signal事件<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注册signal事件是通过evsignal_add(struct event *ev)函数完成的，libevent对所有的信号注册同一个处理函数evsignal_handler()，该函数将在下一段介绍，注册过程如下：<br>1 取得ev要注册到的信号signo；<br>2 如果信号signo未被注册，那么就为signo注册信号处理函数evsignal_handler()；<br>3 如果事件ev_signal还没哟注册，就注册ev_signal事件；<br>4 将事件ev添加到signo的event链表中；<br>从signo上注销一个已注册的signal事件就更简单了，直接从其已注册事件的链表中移除即可。如果事件链表已空，那么就恢复旧的处理函数；<br>下面的讲解都以signal()函数为例，sigaction()函数的处理和signal()相似。<br>处理函数evsignal_handler()函数做的事情很简单，就是记录信号的发生次数，并通知event_base有信号触发，需要处理：</p>
<p>&#160;</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;evsignal_handler(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;sig)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;save_errno&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;errno;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;不覆盖原来的错误代码</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(evsignal_base&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;NULL)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;event_warn(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">%s:&nbsp;received&nbsp;signal&nbsp;%d,&nbsp;but&nbsp;have&nbsp;no&nbsp;base&nbsp;configured</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,&nbsp;__func__,&nbsp;sig);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;记录信号sig的触发次数，并设置event触发标记</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;evsignal_base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">sig.evsigcaught[sig]</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;evsignal_base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">sig.evsignal_caught&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;<br>#ifndef&nbsp;HAVE_SIGACTION<br>&nbsp;&nbsp;&nbsp;&nbsp;signal(sig,&nbsp;evsignal_handler);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;重新注册信号</span><span style="COLOR: #008000"><br></span><span style="COLOR: #0000ff">#endif</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;向写socket写一个字节数据，触发event_base的I/O事件，从而通知其有信号触发，需要处理</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;send(evsignal_base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">sig.ev_signal_pair[</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">],&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">a</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;errno&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;save_errno;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;错误代码</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">}</span></div>
<p><br><a href="http://blog.csdn.net/sparkliang/archive/2009/12/15/5011400.aspx"></a>&nbsp;5 小节<br>本节介绍了libevent对signal事件的具体处理框架，包括事件注册、删除和socket pair通知机制，以及是如何将Signal事件集成到事件主循环之中的。</p>
<img src ="http://www.cppblog.com/flyonok/aggbug/126253.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> 2010-09-09 17:59 <a href="http://www.cppblog.com/flyonok/archive/2010/09/09/126253.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>libevent源码深度剖析七--from csdn</title><link>http://www.cppblog.com/flyonok/archive/2010/09/09/126251.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Thu, 09 Sep 2010 09:44:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2010/09/09/126251.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/126251.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2010/09/09/126251.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/126251.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/126251.html</trackback:ping><description><![CDATA[<p>现在我们已经初步了解了libevent的Reactor组件——event_base和事件管理框架，接下来就是libevent事件处理的中心部分——事件主循环，根据系统提供的事件多路分发机制执行事件循环，对已注册的就绪事件，调用注册事件的回调函数来处理事件。</p>
<p>1 阶段性的胜利<br>&nbsp;&nbsp;&nbsp; Libevent将I/O事件、定时器和信号事件处理很好的结合到了一起，本节也会介绍libevent是如何做到这一点的。<br>&nbsp;&nbsp;&nbsp; 在看完本节的内容后，读者应该会对Libevent的基本框架：事件管理和主循环有比较清晰的认识了，并能够把libevent的事件控制流程清晰的串通起来，剩下的就是一些细节的内容了。</p>
<p>2 事件处理主循环<br>&nbsp;&nbsp;&nbsp;&nbsp; Libevent的事件主循环主要是通过event_base_loop ()函数完成的，其主要操作如下面的流程图所示，event_base_loop所作的就是持续执行下面的循环。<br>&nbsp; <img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/flyonok/main-loop.jpg" width=334 height=467></p>
<p>清楚了event_base_loop所作的主要操作，就可以对比源代码看个究竟了，代码结构还是相当清晰的。</p>
<p>&nbsp;</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;event_base_loop(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;flags)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;eventop&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">evsel&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">evsel;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">evbase&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">evbase;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timeval&nbsp;tv;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timeval&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">tv_p;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;res,&nbsp;done;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;清空时间缓存</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">tv_cache.tv_sec&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;evsignal_base是全局变量，在处理signal时，用于指名signal所属的event_base实例</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">sig.ev_signal_added)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;evsignal_base&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;done&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">done)&nbsp;{&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;事件主循环<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;查看是否需要跳出循环，程序可以调用event_loopexit_cb()设置event_gotterm标记<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;调用event_base_loopbreak()设置event_break标记</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">event_gotterm)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">event_gotterm&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">break</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">event_break)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">event_break&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">break</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;校正系统时间，如果系统使用的是非MONOTONIC时间，用户可能会向后调整了系统时间<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;在timeout_correct函数里，比较last&nbsp;wait&nbsp;time和当前时间，如果当前时间&lt;&nbsp;last&nbsp;wait&nbsp;time<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;表明时间有问题，这是需要更新timer_heap中所有定时事件的超时时间。</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timeout_correct(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">tv);<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;根据timer&nbsp;heap中事件的最小超时时间，计算系统I/O&nbsp;demultiplexer的最大等待时间</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tv_p&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">tv;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">!</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">event_count_active&nbsp;</span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">(flags&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;EVLOOP_NONBLOCK))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timeout_next(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">tv_p);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000">&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;依然有未处理的就绪时间，就让I/O&nbsp;demultiplexer立即返回，不必等待<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;下面会提到，在libevent中，低优先级的就绪事件可能不能立即被处理</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;evutil_timerclear(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">tv);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;如果当前没有注册事件，就退出</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">event_haveevents(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;event_debug((</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">%s:&nbsp;no&nbsp;events&nbsp;registered.</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,&nbsp;__func__));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;更新last&nbsp;wait&nbsp;time，并清空time&nbsp;cache</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gettime(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">event_tv);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">tv_cache.tv_sec&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;调用系统I/O&nbsp;demultiplexer等待就绪I/O&nbsp;events，可能是epoll_wait，或者select等；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;在evsel-&gt;dispatch()中，会把就绪signal&nbsp;event、I/O&nbsp;event插入到激活链表中</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;res&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;evsel</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">dispatch(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;evbase,&nbsp;tv_p);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(res&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;将time&nbsp;cache赋值为当前系统时间</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gettime(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">tv_cache);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;检查heap中的timer&nbsp;events，将就绪的timer&nbsp;event从heap上删除，并插入到激活链表中</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timeout_process(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;调用event_process_active()处理激活链表中的就绪event，调用其回调函数执行事件处理<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;该函数会寻找最高优先级（priority值越小优先级越高）的激活事件链表，<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;然后处理链表中的所有就绪事件；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;因此低优先级的就绪事件可能得不到及时处理；</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">event_count_active)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;event_process_active(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">!</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">event_count_active&nbsp;</span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000">&nbsp;(flags&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;EVLOOP_ONCE))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;done&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(flags&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;EVLOOP_NONBLOCK)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;done&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;循环结束，清空时间缓存</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">tv_cache.tv_sec&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;event_debug((</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">%s:&nbsp;asked&nbsp;to&nbsp;terminate&nbsp;loop.</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,&nbsp;__func__));<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">);<br>}</span></div>
<p><br>3 I/O和Timer事件的统一<br>&nbsp;&nbsp;&nbsp;&nbsp; Libevent将Timer和Signal事件都统一到了系统的I/O 的demultiplex机制中了，相信读者从上面的流程和代码中也能窥出一斑了，下面就再啰嗦一次了。<br>&nbsp;&nbsp;&nbsp;&nbsp; 首先将Timer事件融合到系统I/O多路复用机制中，还是相当清晰的，因为系统的I/O机制像select()和epoll_wait()都允许程序制定一个最大等待时间（也称为最大超时时间）timeout，即使没有I/O事件发生，它们也保证能在timeout时间内返回。<br>那么根据所有Timer事件的最小超时时间来设置系统I/O的timeout时间；当系统I/O返回时，再激活所有就绪的Timer事件就可以了，这样就能将Timer事件完美的融合到系统的I/O机制中了。<br>&nbsp;&nbsp;&nbsp;&nbsp; 这是在Reactor和Proactor模式（主动器模式，比如Windows上的IOCP）中处理Timer事件的经典方法了，ACE采用的也是这种方法，大家可以参考POSA vol2书中的Reactor模式一节。<br>&nbsp;&nbsp;&nbsp;&nbsp; 堆是一种经典的数据结构，向堆中插入、删除元素时间复杂度都是O(lgN)，N为堆中元素的个数，而获取最小key值（小根堆）的复杂度为O(1)；因此变成了管理Timer事件的绝佳人选（当然是非唯一的），libevent就是采用的堆结构。</p>
<p>4 I/O和Signal事件的统一<br>&nbsp;&nbsp;&nbsp;&nbsp; Signal是异步事件的经典事例，将Signal事件统一到系统的I/O多路复用中就不像Timer事件那么自然了，Signal事件的出现对于进程来讲是完全随机的，进程不能只是测试一个变量来判别是否发生了一个信号，而是必须告诉内核&#8220;在此信号发生时，请执行如下的操作&#8221;。<br>如果当Signal发生时，并不立即调用event的callback函数处理信号，而是设法通知系统的I/O机制，让其返回，然后再统一和I/O事件以及Timer一起处理，不就可以了嘛。是的，这也是libevent中使用的方法。<br>&nbsp;&nbsp;&nbsp;&nbsp; 问题的核心在于，当Signal发生时，如何通知系统的I/O多路复用机制，这里先买个小关子，放到信号处理一节再详细说明，我想读者肯定也能想出通知的方法，比如使用pipe。</p>
<p>5 小节<br>&nbsp;&nbsp;&nbsp;&nbsp; 介绍了libevent的事件主循环，描述了libevent是如何处理就绪的I/O事件、定时器和信号事件，以及如何将它们无缝的融合到一起。<br>加油！</p>
<img src ="http://www.cppblog.com/flyonok/aggbug/126251.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> 2010-09-09 17:44 <a href="http://www.cppblog.com/flyonok/archive/2010/09/09/126251.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>libevent源码深度剖析六--from csdn</title><link>http://www.cppblog.com/flyonok/archive/2010/09/09/126248.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Thu, 09 Sep 2010 09:00:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2010/09/09/126248.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/126248.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2010/09/09/126248.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/126248.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/126248.html</trackback:ping><description><![CDATA[前面已经对libevent的事件处理框架和event结构体做了描述，现在是时候剖析libevent对事件的详细处理流程了，本节将分析libevent的事件处理框架event_base和libevent注册、删除事件的具体流程，可结合前一节libevent对event的管理。
<h3>1 事件处理框架-event_base<br></h3>
<p>回想Reactor模式的几个基本组件，本节讲解的部分对应于Reactor框架组件。在libevent中，这就表现为event_base结构体，结构体声明如下，它位于event-internal.h文件中：<br></p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;{<br>&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;eventop&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">evsel;<br>&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">evbase;　<br>&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;event_count;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;counts&nbsp;number&nbsp;of&nbsp;total&nbsp;events&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br>&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;event_count_active;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;counts&nbsp;number&nbsp;of&nbsp;active&nbsp;events&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br>&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;event_gotterm;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;Set&nbsp;to&nbsp;terminate&nbsp;loop&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br>&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;event_break;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;Set&nbsp;to&nbsp;terminate&nbsp;loop&nbsp;immediately&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br>&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;active&nbsp;event&nbsp;management&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br>&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_list&nbsp;</span><span style="COLOR: #000000">**</span><span style="COLOR: #000000">activequeues;<br>&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;nactivequeues;<br>&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;signal&nbsp;handling&nbsp;info&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br>&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;evsignal_info&nbsp;sig;<br>&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_list&nbsp;eventqueue;<br>&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timeval&nbsp;event_tv;<br>&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;min_heap&nbsp;timeheap;<br>&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timeval&nbsp;tv_cache;<br>};</span></div>
<p>下面详细解释一下结构体中各字段的含义。<br>1）evsel和evbase这两个字段的设置可能会让人有些迷惑，这里你可以把evsel和evbase看作是类和静态函数的关系，比如添加事件时的调用行为：evsel-&gt;add(evbase, ev)，实际执行操作的是evbase；这相当于class::add(instance, ev)，instance就是class的一个对象实例。<br>evsel指向了全局变量static const struct eventop *eventops[]中的一个；<br>前面也说过，libevent将系统提供的I/O demultiplex机制统一封装成了eventop结构；因此eventops[]包含了select、poll、kequeue和epoll等等其中的若干个全局实例对象。<br>evbase实际上是一个eventop实例对象；<br>先来看看eventop结构体，它的成员是一系列的函数指针, 在event-internal.h文件中：<br></p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;eventop&nbsp;{<br>&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">name;<br>&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">init)(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;初始化</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">add)(</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;注册事件</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">del)(</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;删除事件</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">dispatch)(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timeval&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;事件分发</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">dealloc)(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;注销，释放资源</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;set&nbsp;if&nbsp;we&nbsp;need&nbsp;to&nbsp;reinitialize&nbsp;the&nbsp;event&nbsp;base&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br>&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;need_reinit;<br>};<br></span></div>
<p>也就是说，在libevent中，每种I/O demultiplex机制的实现都必须提供这五个函数接口，来完成自身的初始化、销毁释放；对事件的注册、注销和分发。<br>比如对于epoll，libevent实现了5个对应的接口函数，并在初始化时并将eventop的5个函数指针指向这5个函数，那么程序就可以使用epoll作为I/O demultiplex机制了，这个在后面会再次提到。<br>2）activequeues是一个二级指针，前面讲过libevent支持事件优先级，因此你可以把它看作是数组，其中的元素activequeues[priority]是一个链表，链表的每个节点指向一个优先级为priority的就绪事件event。 <br>3）eventqueue，链表，保存了所有的注册事件event的指针。<br>4）sig是由来管理信号的结构体，将在后面信号处理时专门讲解；<br>5）timeheap是管理定时事件的小根堆，将在后面定时事件处理时专门讲解；<br>6）event_tv和tv_cache是libevent用于时间管理的变量，将在后面讲到；<br>其它各个变量都能因名知意，就不再啰嗦了。</p>
<p>2 创建和初始化event_base<br>创建一个event_base对象也既是创建了一个新的libevent实例，程序需要通过调用event_init()（内部调用event_base_new函数执行具体操作）函数来创建，该函数同时还对新生成的libevent实例进行了初始化。<br>该函数首先为event_base实例申请空间，然后初始化timer mini-heap，选择并初始化合适的系统I/O 的demultiplexer机制，初始化各事件链表；<br>函数还检测了系统的时间设置，为后面的时间管理打下基础。</p>
<p>3 接口函数</p>
<p>前面提到Reactor框架的作用就是提供事件的注册、注销接口；根据系统提供的事件多路分发机制执行事件循环，当有事件进入&#8220;就绪&#8221;状态时，调用注册事件的回调函数来处理事件。<br>Libevent中对应的接口函数主要就是：<br></p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;&nbsp;event_add(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">ev,&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timeval&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">timeout);<br></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;&nbsp;event_del(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">ev);<br></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;&nbsp;event_base_loop(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;loops);<br></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;event_active(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;res,&nbsp;</span><span style="COLOR: #0000ff">short</span><span style="COLOR: #000000">&nbsp;events);<br></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;event_process_active(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">);&nbsp;</span></div>
<p>本节将按介绍事件注册和删除的代码流程，libevent的事件循环框架将在下一节再具体描述。<br>对于定时事件，这些函数将调用timer heap管理接口执行插入和删除操作；对于I/O和Signal事件将调用eventopadd和delete接口函数执行插入和删除操作（eventop会对Signal事件调用Signal处理接口执行操作）；这些组件将在后面的内容描述。<br>1）注册事件<br>函数原型：<br>int event_add(struct event *ev, const struct timeval *tv)<br>参数：ev：指向要注册的事件；<br>tv：超时时间；<br>函数将ev注册到ev-&gt;ev_base上，事件类型由ev-&gt;ev_events指明，如果注册成功，ev将被插入到已注册链表中；如果tv不是NULL，则会同时注册定时事件，将ev添加到timer堆上；<br>如果其中有一步操作失败，那么函数保证没有事件会被注册，可以讲这相当于一个原子操作。这个函数也体现了libevent细节之处的巧妙设计，且仔细看程序代码，部分有省略，注释直接附在代码中。<br></p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;event_add(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">ev,&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timeval&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">tv)<br>{<br>&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_base;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;要注册到的event_base</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;eventop&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">evsel&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">evsel;<br>&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">evbase&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">evbase;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;base使用的系统I/O策略<br>&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;新的timer事件，调用timer&nbsp;heap接口在堆上预留一个位置<br>&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;注：这样能保证该操作的原子性：<br>&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;向系统I/O机制注册可能会失败，而当在堆上预留成功后，<br>&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;定时事件的添加将肯定不会失败；<br>&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;而预留位置的可能结果是堆扩充，但是内部元素并不会改变</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(tv&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;NULL&nbsp;</span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">(ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_flags&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;EVLIST_TIMEOUT))&nbsp;{<br>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(min_heap_reserve(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">timeheap,<br>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;min_heap_size(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">timeheap))&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)<br>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">);&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;ENOMEM&nbsp;==&nbsp;errno&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br>&nbsp;}<br>&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;如果事件ev不在已注册或者激活链表中，则调用evbase注册事件</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;((ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_events&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;(EV_READ</span><span style="COLOR: #000000">|</span><span style="COLOR: #000000">EV_WRITE</span><span style="COLOR: #000000">|</span><span style="COLOR: #000000">EV_SIGNAL))&nbsp;</span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">(ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_flags&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;(EVLIST_INSERTED</span><span style="COLOR: #000000">|</span><span style="COLOR: #000000">EVLIST_ACTIVE)))&nbsp;{<br>&nbsp;&nbsp;&nbsp;res&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;evsel</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">add(evbase,&nbsp;ev);<br>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(res&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;注册成功，插入event到已注册链表中</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;event_queue_insert(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;ev,&nbsp;EVLIST_INSERTED);<br>&nbsp;}<br>&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;准备添加定时事件</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(res&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000">&nbsp;tv&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;NULL)&nbsp;{<br>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timeval&nbsp;now;<br>&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;EVLIST_TIMEOUT表明event已经在定时器堆中了，删除旧的</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_flags&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;EVLIST_TIMEOUT)<br>&nbsp;&nbsp;&nbsp;event_queue_remove(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;ev,&nbsp;EVLIST_TIMEOUT);<br>&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;如果事件已经是就绪状态则从激活链表中删除</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;((ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_flags&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;EVLIST_ACTIVE)&nbsp;</span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;(ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_res&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;EV_TIMEOUT))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;将ev_callback调用次数设置为0</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_ncalls&nbsp;</span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000">&nbsp;ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_pncalls)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_pncalls&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;event_queue_remove(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;ev,&nbsp;EVLIST_ACTIVE);<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;计算时间，并插入到timer小根堆中</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;gettime(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">now);<br>&nbsp;&nbsp;evutil_timeradd(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">now,&nbsp;tv,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_timeout);<br>&nbsp;&nbsp;event_queue_insert(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;ev,&nbsp;EVLIST_TIMEOUT);<br>&nbsp;}<br>&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;(res);<br>}<br>&nbsp;<br>event_queue_insert()负责将事件插入到对应的链表中，下面是程序代码；<br>event_queue_remove()负责将事件从对应的链表中删除，这里就不再重复贴代码了；<br></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;event_queue_insert(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">ev,&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;queue)<br>{<br>&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;ev可能已经在激活列表中了，避免重复插入</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_flags&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;queue)&nbsp;{<br>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(queue&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;EVLIST_ACTIVE)<br>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">;<br>&nbsp;}<br>&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;<img src="http://www.cppblog.com/Images/dot.gif"></span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_flags&nbsp;</span><span style="COLOR: #000000">|=</span><span style="COLOR: #000000">&nbsp;queue;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;记录queue标记</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">switch</span><span style="COLOR: #000000">&nbsp;(queue)&nbsp;{<br>&nbsp;</span><span style="COLOR: #0000ff">case</span><span style="COLOR: #000000">&nbsp;EVLIST_INSERTED:&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;I/O或Signal事件，加入已注册事件链表</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;TAILQ_INSERT_TAIL(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">eventqueue,&nbsp;ev,&nbsp;ev_next);<br>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">break</span><span style="COLOR: #000000">;<br>&nbsp;</span><span style="COLOR: #0000ff">case</span><span style="COLOR: #000000">&nbsp;EVLIST_ACTIVE:&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;就绪事件，加入激活链表</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">event_count_active</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;TAILQ_INSERT_TAIL(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">activequeues[ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_pri],&nbsp;ev,&nbsp;ev_active_next);<br>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">break</span><span style="COLOR: #000000">;<br>&nbsp;</span><span style="COLOR: #0000ff">case</span><span style="COLOR: #000000">&nbsp;EVLIST_TIMEOUT:&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;定时事件，加入堆</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;min_heap_push(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">timeheap,&nbsp;ev);<br>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">break</span><span style="COLOR: #000000">;<br>&nbsp;}<br>}</span></div>
<p>2）删除事件：<br>函数原型为：int&nbsp; event_del(struct event *ev);<br>该函数将删除事件ev，对于I/O事件，从I/O 的demultiplexer上将事件注销；对于Signal事件，将从Signal事件链表中删除；对于定时事件，将从堆上删除；<br>同样删除事件的操作则不一定是原子的，比如删除时间事件之后，有可能从系统I/O机制中注销会失败。</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;event_del(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">ev)<br>{<br>&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">;<br>&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;eventop&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">evsel;<br>&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">evbase;<br>&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;ev_base为NULL，表明ev没有被注册</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_base&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;NULL)<br>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">);<br>&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;取得ev注册的event_base和eventop指针</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_base;<br>&nbsp;evsel&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">evsel;<br>&nbsp;evbase&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">evbase;<br>&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;将ev_callback调用次数设置为</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_ncalls&nbsp;</span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000">&nbsp;ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_pncalls)&nbsp;{<br>&nbsp;&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_pncalls&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br>&nbsp;}<br>&nbsp;<br>&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;从对应的链表中删除</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_flags&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;EVLIST_TIMEOUT)<br>&nbsp;&nbsp;event_queue_remove(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;ev,&nbsp;EVLIST_TIMEOUT);<br>&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_flags&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;EVLIST_ACTIVE)<br>&nbsp;&nbsp;event_queue_remove(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;ev,&nbsp;EVLIST_ACTIVE);<br>&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(ev</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ev_flags&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;EVLIST_INSERTED)&nbsp;{<br>&nbsp;&nbsp;event_queue_remove(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">,&nbsp;ev,&nbsp;EVLIST_INSERTED);<br>&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;EVLIST_INSERTED表明是I/O或者Signal事件，<br>&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;需要调用I/O&nbsp;demultiplexer注销事件</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;(evsel</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">del(evbase,&nbsp;ev));<br>&nbsp;}<br>&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">);<br>}</span></div>
4 小节<br>分析了event_base这一重要结构体，初步看到了libevent对系统的I/O demultiplex机制的封装event_op结构，并结合源代码分析了事件的注册和删除处理，下面将会接着分析事件管理框架中的主事件循环部分
<img src ="http://www.cppblog.com/flyonok/aggbug/126248.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> 2010-09-09 17:00 <a href="http://www.cppblog.com/flyonok/archive/2010/09/09/126248.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>libevent源码深度剖析五--from csdn</title><link>http://www.cppblog.com/flyonok/archive/2010/09/09/126243.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Thu, 09 Sep 2010 08:01:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2010/09/09/126243.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/126243.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2010/09/09/126243.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/126243.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/126243.html</trackback:ping><description><![CDATA[<p>&nbsp; 对事件处理流程有了高层的认识后，本节将详细介绍libevent的核心结构event，以及libevent对event的管理。</p>
<h3>1 libevent的核心-event</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Libevent是基于事件驱动（event-driven）的，从名字也可以看到event是整个库的核心。event就是Reactor框架中的事件处理程序组件；它提供了函数接口，供Reactor在事件发生时调用，以执行相应的事件处理，通常它会绑定一个有效的句柄。<br>首先给出event结构体的声明，它位于event.h文件中：<br></p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">&nbsp;{&nbsp;&nbsp;&nbsp;<br>&nbsp;TAILQ_ENTRY&nbsp;(</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">)&nbsp;ev_next;&nbsp;&nbsp;&nbsp;<br>&nbsp;TAILQ_ENTRY&nbsp;(</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">)&nbsp;ev_active_next;&nbsp;&nbsp;&nbsp;<br>&nbsp;TAILQ_ENTRY&nbsp;(</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">)&nbsp;ev_signal_next;&nbsp;&nbsp;&nbsp;<br>&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;min_heap_idx;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;for&nbsp;managing&nbsp;timeouts&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000">&nbsp;&nbsp;<br>&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">ev_base;&nbsp;&nbsp;&nbsp;<br>&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;ev_fd;&nbsp;&nbsp;&nbsp;<br>&nbsp;</span><span style="COLOR: #0000ff">short</span><span style="COLOR: #000000">&nbsp;ev_events;&nbsp;&nbsp;&nbsp;<br>&nbsp;</span><span style="COLOR: #0000ff">short</span><span style="COLOR: #000000">&nbsp;ev_ncalls;&nbsp;&nbsp;&nbsp;<br>&nbsp;</span><span style="COLOR: #0000ff">short</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">ev_pncalls;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;Allows&nbsp;deletes&nbsp;in&nbsp;callback&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000">&nbsp;&nbsp;<br>&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timeval&nbsp;ev_timeout;&nbsp;&nbsp;&nbsp;<br>&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;ev_pri;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;smaller&nbsp;numbers&nbsp;are&nbsp;higher&nbsp;priority&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000">&nbsp;&nbsp;<br>&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">ev_callback)(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">short</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">arg);&nbsp;&nbsp;&nbsp;<br>&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">ev_arg;&nbsp;&nbsp;&nbsp;<br>&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;ev_res;&nbsp;&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;result&nbsp;passed&nbsp;to&nbsp;event&nbsp;callback&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000">&nbsp;&nbsp;<br>&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;ev_flags;&nbsp;&nbsp;&nbsp;<br>};&nbsp;&nbsp;<br></span></div>
下面简单解释一下结构体中各字段的含义。<br>1）ev_events：event关注的事件类型，它可以是以下3种类型：<br>I/O事件： EV_WRITE和EV_READ<br>定时事件：EV_TIMEOUT<br>信号：&nbsp;&nbsp;&nbsp; EV_SIGNAL<br>辅助选项：EV_PERSIST，表明是一个永久事件<br>Libevent中的定义为：<br>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;EV_TIMEOUT&nbsp;0x01&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;EV_READ&nbsp;&nbsp;0x02&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;EV_WRITE&nbsp;0x04&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;EV_SIGNAL&nbsp;0x08&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;EV_PERSIST&nbsp;0x10&nbsp;/*&nbsp;Persistant&nbsp;event&nbsp;*/&nbsp;&nbsp;</span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span></div>
<p>可以看出事件类型可以使用&#8220;|&#8221;运算符进行组合，需要说明的是，信号和I/O事件不能同时设置；<br>还可以看出libevent使用event结构体将这3种事件的处理统一起来；<br>2）ev_next，ev_active_next和ev_signal_next都是双向链表节点指针；它们是libevent对不同事件类型和在不同的时期，对事件的管理时使用到的字段。<br>libevent使用双向链表保存所有注册的I/O和Signal事件，ev_next就是该I/O事件在链表中的位置；称此链表为&#8220;已注册事件链表&#8221;；<br>同样ev_signal_next就是signal事件在signal事件链表中的位置；<br>ev_active_next：libevent将所有的激活事件放入到链表active list中，然后遍历active list执行调度，ev_active_next就指明了event在active list中的位置；<br>2）min_heap_idx和ev_timeout，如果是timeout事件，它们是event在小根堆中的索引和超时值，libevent使用小根堆来管理定时事件，这将在后面定时事件处理时专门讲解<br>3）ev_base该事件所属的反应堆实例，这是一个event_base结构体，下一节将会详细讲解；<br>4）ev_fd，对于I/O事件，是绑定的文件描述符；对于signal事件，是绑定的信号；<br>5）ev_callback，event的回调函数，被ev_base调用，执行事件处理程序，这是一个函数指针，原型为：<br>void (*ev_callback)(int fd, short events, void *arg)<br>其中参数fd对应于ev_fd；events对应于ev_events；arg对应于ev_arg；<br>6）ev_arg：void*，表明可以是任意类型的数据，在设置event时指定；<br>7）eb_flags：libevent用于标记event信息的字段，表明其当前的状态，可能的值有：</p>
<p>&nbsp;</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;EVLIST_TIMEOUT&nbsp;0x01&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;event在time堆中&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000"><br></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;EVLIST_INSERTED&nbsp;0x02&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;event在已注册事件链表中&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000"><br></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;EVLIST_SIGNAL&nbsp;0x04&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;未见使用&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000"><br></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;EVLIST_ACTIVE&nbsp;0x08&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;event在激活链表中&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000"><br></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;EVLIST_INTERNAL&nbsp;0x10&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;内部使用标记&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000"><br></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;EVLIST_INIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0x80&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;event已被初始化&nbsp;&nbsp;</span><span style="COLOR: #000000"><br></span></div>
<p>8）ev_ncalls：事件就绪执行时，调用ev_callback的次数，通常为1；<br>9）ev_pncalls：指针，通常指向ev_ncalls或者为NULL；<br>10）ev_res：记录了当前激活事件的类型；<br></p>
<h3>2 libevent对event的管理<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 从event结构体中的3个链表节点指针和一个堆索引出发，大体上也能窥出libevent对event的管理方法了，可以参见下面的示意图：<br>&nbsp;<img alt="event management" src="http://p.blog.csdn.net/images/p_blog_csdn_net/sparkliang/EntryImages/20091209/event%20management.JPG" width=485 height=298> </p>
<p>每次当有事件event转变为就绪状态时，libevent就会把它移入到active event list[priority]中，其中priority是event的优先级；<br>接着libevent会根据自己的调度策略选择就绪事件，调用其cb_callback()函数执行事件处理；并根据就绪的句柄和事件类型填充cb_callback函数的参数。</p>
<p>3 事件设置的接口函数<br>&nbsp;&nbsp;&nbsp;&nbsp; 要向libevent添加一个事件，需要首先设置event对象，这通过调用libevent提供的函数有：event_set(), event_base_set(), event_priority_set()来完成；下面分别进行讲解。</p>
<p>void event_set(struct event *ev, int fd, short events,<br>&nbsp;&nbsp; void (*callback)(int, short, void *), void *arg)<br>1.设置事件ev绑定的文件描述符或者信号，对于定时事件，设为-1即可；<br>2.设置事件类型，比如EV_READ|EV_PERSIST, EV_WRITE, EV_SIGNAL等；<br>3.设置事件的回调函数以及参数arg；<br>4.初始化其它字段，比如缺省的event_base和优先级；<br>int event_base_set(struct event_base *base, struct event *ev)<br>设置event ev将要注册到的event_base；<br>libevent有一个全局event_base指针current_base，默认情况下事件ev将被注册到current_base上，使用该函数可以指定不同的event_base；<br>如果一个进程中存在多个libevent实例，则必须要调用该函数为event设置不同的event_base；</p>
<p>int event_priority_set(struct event *ev, int pri)<br>设置event ev的优先级，没什么可说的，注意的一点就是：当ev正处于就绪状态时，不能设置，返回-1。</p>
<p>4 小结<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 本节讲述了libevent的核心event结构，以及libevent支持的事件类型和libevent对event的管理模型；接下来将会描述libevent的事件处理框架，以及其中使用的重要的结构体event_base；</p>
<img src ="http://www.cppblog.com/flyonok/aggbug/126243.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> 2010-09-09 16:01 <a href="http://www.cppblog.com/flyonok/archive/2010/09/09/126243.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>libevent源码深度剖析四 -- from csdn</title><link>http://www.cppblog.com/flyonok/archive/2010/09/09/126239.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Thu, 09 Sep 2010 07:20:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2010/09/09/126239.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/126239.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2010/09/09/126239.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/126239.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/126239.html</trackback:ping><description><![CDATA[<p>——libevent源代码文件组织</p>
<p>1 前言<br>详细分析源代码之前，如果能对其代码文件的基本结构有个大概的认识和分类，对于代码的分析将是大有裨益的。本节内容不多，我想并不是说它不重要！</p>
<p>2 源代码组织结构<br>Libevent的源代码虽然都在一层文件夹下面，但是其代码分类还是相当清晰的，主要可分为头文件、内部使用的头文件、辅助功能函数、日志、libevent框架、对系统I/O多路复用机制的封装、信号管理、定时事件管理、缓冲区管理、基本数据结构和基于libevent的两个实用库等几个部分，有些部分可能就是一个源文件。<br>源代码中的test部分就不在我们关注的范畴了。<br>1）头文件<br>主要就是event.h：事件宏定义、接口函数声明，主要结构体event的声明；<br>2）内部头文件<br>xxx-internal.h：内部数据结构和函数，对外不可见，以达到信息隐藏的目的；<br>3）libevent框架<br>event.c：event整体框架的代码实现；<br>4）对系统I/O多路复用机制的封装<br>epoll.c：对epoll的封装；<br>select.c：对select的封装；<br>devpoll.c：对dev/poll的封装;<br>kqueue.c：对kqueue的封装；<br>5）定时事件管理<br>min-heap.h：其实就是一个以时间作为key的小根堆结构；<br>6）信号管理<br>signal.c：对信号事件的处理；<br>7）辅助功能函数<br>evutil.h 和evutil.c：一些辅助功能函数，包括创建socket pair和一些时间操作函数：加、减和比较等。<br>8）日志<br>log.h和log.c：log日志函数<br>9）缓冲区管理<br>evbuffer.c和buffer.c：libevent对缓冲区的封装；<br>10）基本数据结构<br>compat\sys下的两个源文件：queue.h是libevent基本数据结构的实现，包括链表，双向链表，队列等；_libevent_time.h：一些用于时间操作的结构体定义、函数和宏定义；<br>11）实用网络库<br>http和evdns：是基于libevent实现的http服务器和异步dns查询库；</p>
<p>3 小结<br>本节介绍了libevent的组织和分类，下面将会详细介绍libevent的核心部分event结构</p>
<p><br>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/sparkliang/archive/2009/12/07/4957885.aspx">http://blog.csdn.net/sparkliang/archive/2009/12/07/4957885.aspx</a></p>
<img src ="http://www.cppblog.com/flyonok/aggbug/126239.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> 2010-09-09 15:20 <a href="http://www.cppblog.com/flyonok/archive/2010/09/09/126239.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>libevent源码深度剖析三 from csdn</title><link>http://www.cppblog.com/flyonok/archive/2010/09/09/126237.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Thu, 09 Sep 2010 07:06:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2010/09/09/126237.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/126237.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2010/09/09/126237.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/126237.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/126237.html</trackback:ping><description><![CDATA[<p>&nbsp; </p>
<p><span>1 </span><span>前言</span></p>
<p><span>学习源代码该从哪里入手？我觉得从程序的基本使用场景和代码的整体处理流程入手是个不错的方法，至少从个人的经验上讲，用此方法分析</span><span>libevent</span><span>是比较有效的。</span></p>
<p>&nbsp;</p>
<p><span>2 </span><span>基本应用场景</span></p>
<p><span>基本应用场景也是使用</span><span>libevnet</span><span>的基本流程，下面来考虑一个最简单的场景，使用</span><span>livevent</span><span>设置定时器，应用程序只需要执行下面几个简单的步骤即可。</span></p>
<p><span>1</span><span>）首先初始化</span><span>libevent</span><span>库，并保存返回的指针</span></p>
<p><span>struct event_base * base = event_init();</span></p>
<p><span>实际上这一步相当于初始化一个</span><span>Reactor</span><span>实例；在初始化</span><span>libevent</span><span>后，就可以注册事件了。</span></p>
<p>&nbsp;</p>
<p><span>2</span><span>）初始化事件</span><span>event</span><span>，设置回调函数和关注的事件</span></p>
<p><span>evtimer_set(&amp;ev, timer_cb, NULL);</span></p>
<p><span>事实上这等价于调用</span><span>event_set(&amp;ev, -1, 0, timer_cb, NULL);</span></p>
<p><span>event_set</span><span>的函数原型是：</span></p>
<p><span>void event_set(struct event *ev, int fd, short event, void (*cb)(int, short, void *), void *arg)</span></p>
<p><span>ev</span><span>：执行要初始化的</span><span>event</span><span>对象；</span></p>
<p><span>fd</span><span>：该</span><span>event</span><span>绑定的&#8220;句柄&#8221;，对于信号事件，它就是关注的信号；</span></p>
<p><span>event</span><span>：在该</span><span>fd</span><span>上关注的事件类型，它可以是</span><span>EV_READ, EV_WRITE, EV_SIGNAL</span><span>；</span></p>
<p><span>cb</span><span>：这是一个函数指针，当</span><span>fd</span><span>上的事件</span><span>event</span><span>发生时，调用该函数执行处理，它有三个参数，调用时由</span><span>event_base</span><span>负责传入，按顺序，实际上就是</span><span>event_set</span><span>时的</span><span>fd, event</span><span>和</span><span>arg</span><span>；</span></p>
<p><span>arg</span><span>：传递给</span><span>cb</span><span>函数指针的参数；</span></p>
<p><span>由于定时事件不需要</span><span>fd</span><span>，并且定时事件是根据添加时（</span><span>event_add</span><span>）的超时值设定的，因此这里</span><span>event</span><span>也不需要设置。</span></p>
<p><span>这一步相当于初始化一个</span><span>event handler</span><span>，在</span><span>libevent</span><span>中事件类型保存在</span><span>event</span><span>结构体中。</span></p>
<p><span>注意：</span><span>libevent</span><span>并不会管理</span><span>event</span><span>事件集合，这需要应用程序自行管理；</span></p>
<p>&nbsp;</p>
<p><span>3</span><span>）设置</span><span>event</span><span>从属的</span><span>event_base</span></p>
<p><span>event_base_set(base, &amp;ev);<span>&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span>这一步相当于指明</span><span>event</span><span>要注册到哪个</span><span>event_base</span><span>实例上；</span></p>
<p>&nbsp;</p>
<p><span>4</span><span>）是正式的添加事件的时候了</span></p>
<p><span>event_add(&amp;ev, timeout);</span></p>
<p><span>基本信息都已设置完成，只要简单的调用</span><span>event_add()</span><span>函数即可完成，其中</span><span>timeout</span><span>是定时值；</span></p>
<p><span>这一步相当于调用</span><span>Reactor::register_handler()</span><span>函数注册事件。</span></p>
<p>&nbsp;</p>
<p><span>5</span><span>）程序进入无限循环，等待就绪事件并执行事件处理</span></p>
<p><span>event_base_dispatch(base);</span></p>
<p>&nbsp;</p>
<p><span>3 </span><span>实例代码</span></p>
<p><span>上面例子的程序代码如下所示</span></p>
<p>&nbsp;</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">&nbsp;ev;&nbsp;&nbsp;&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timeval&nbsp;tv;&nbsp;&nbsp;&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;time_cb(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;fd,&nbsp;</span><span style="COLOR: #0000ff">short</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">argc)&nbsp;&nbsp;&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img id=Codehighlighter1_94_184_Open_Image onclick="this.style.display='none'; Codehighlighter1_94_184_Open_Text.style.display='none'; Codehighlighter1_94_184_Closed_Image.style.display='inline'; Codehighlighter1_94_184_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_94_184_Closed_Image onclick="this.style.display='none'; Codehighlighter1_94_184_Closed_Text.style.display='none'; Codehighlighter1_94_184_Open_Image.style.display='inline'; Codehighlighter1_94_184_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_94_184_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_94_184_Open_Text><span style="COLOR: #000000">{&nbsp;&nbsp;&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">timer&nbsp;wakeup\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);&nbsp;&nbsp;&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;event_add(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">ev,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">tv);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;reschedule&nbsp;timer&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"></span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif">}</span></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;main()&nbsp;&nbsp;&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img id=Codehighlighter1_205_426_Open_Image onclick="this.style.display='none'; Codehighlighter1_205_426_Open_Text.style.display='none'; Codehighlighter1_205_426_Closed_Image.style.display='inline'; Codehighlighter1_205_426_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_205_426_Closed_Image onclick="this.style.display='none'; Codehighlighter1_205_426_Closed_Text.style.display='none'; Codehighlighter1_205_426_Open_Image.style.display='inline'; Codehighlighter1_205_426_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_205_426_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_205_426_Open_Text><span style="COLOR: #000000">{&nbsp;&nbsp;&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;event_base&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;event_init();&nbsp;&nbsp;&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;tv.tv_sec&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">10</span><span style="COLOR: #000000">;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;10s&nbsp;period&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"></span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;tv.tv_usec&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;&nbsp;&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;evtimer_set(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">ev,&nbsp;time_cb,&nbsp;NULL);&nbsp;&nbsp;&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;event_add(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">ev,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">tv);&nbsp;&nbsp;&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;event_base_dispatch(</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">);&nbsp;&nbsp;&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif">}</span></span><span style="COLOR: #000000">&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span></div>
<p>&nbsp;</p>
<p><span>4 </span><span>事件处理流程</span></p>
<p><span>当应用程序向</span><span>libevent</span><span>注册一个事件后，</span><span>libevent</span><span>内部是怎么样进行处理的呢？下面的图就给出了这一基本流程。</span></p>
<p><span>1</span><span>）</span><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>首先应用程序准备并初始化</span><span>event</span><span>，设置好事件类型和回调函数；这对应于前面第步骤</span><span>2</span><span>和</span><span>3</span><span>；</span></p>
<p><span>2</span><span>）</span><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>向</span><span>libevent</span><span>添加该事件</span><span>event</span><span>。对于定时事件，</span><span>libevent</span><span>使用一个小根堆管理，</span><span>key</span><span>为超时时间；对于</span><span>Signal</span><span>和</span><span>I/O</span><span>事件，</span><span>libevent</span><span>将其放入到等待链表（</span><span>wait list</span><span>）中，这是一个双向链表结构；</span></p>
<p><span>3</span><span>）</span><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>程序调用</span><span>event_base_dispatch()</span><span>系列函数进入无限循环，等待事件，以</span><span>select()</span><span>函数为例；每次循环前</span><span>libevent</span><span>会检查定时事件的最小超时时间</span><span>tv</span><span>，根据</span><span>tv</span><span>设置</span><span>select()</span><span>的最大等待时间，以便于后面及时处理超时事件；</span></p>
<p><span>当</span><span>select()</span><span>返回后，首先检查超时事件，然后检查</span><span>I/O</span><span>事件；</span></p>
<p><span>Libevent</span><span>将所有的就绪事件，放入到激活链表中；</span></p>
<p><span>然后对激活链表中的事件，调用事件的回调函数执行事件处理；</span></p>
<p><span>&nbsp;</span></p>
<p>&nbsp;<img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/flyonok/2.GIF" width=674 height=279></p>
<p><span>5 </span><span>小结</span></p>
<p><span>本节介绍了</span><span>libevent</span><span>的简单实用场景，并旋风般的介绍了</span><span>libevent</span><span>的事件处理流程，读者应该对</span><span>libevent</span><span>有了基本的印象，下面将会详细介绍</span><span>libevent</span><span>的事件管理框架（</span><span>Reactor</span><span>模式中的</span><span>Reactor</span><span>框架）做详细的介绍，在此之前会对源代码文件做简单的分类。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>本文来自</span><span>CSDN</span><span>博客，转载请标明出处：</span><span>http://blog.csdn.net/sparkliang/archive/2009/12/07/4957820.aspx</span></p>
<img src ="http://www.cppblog.com/flyonok/aggbug/126237.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> 2010-09-09 15:06 <a href="http://www.cppblog.com/flyonok/archive/2010/09/09/126237.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>libevent源码深度剖析二</title><link>http://www.cppblog.com/flyonok/archive/2010/09/08/126185.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Wed, 08 Sep 2010 14:05:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2010/09/08/126185.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/126185.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2010/09/08/126185.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/126185.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/126185.html</trackback:ping><description><![CDATA[<p>——Reactor模式</p>
<p>张亮</p>
<p>前面讲到，整个libevent本身就是一个Reactor，因此本节将专门对Reactor模式进行必要的介绍，并列出libevnet中的几个重要组件和Reactor的对应关系，在后面的章节中可能还会提到本节介绍的基本概念。</p>
<p>1 Reactor的事件处理机制<br>首先来回想一下普通函数调用的机制：程序调用某函数?函数执行，程序等待?函数将结果和控制权返回给程序?程序继续处理。<br>Reactor释义&#8220;反应堆&#8221;，是一种事件驱动机制。和普通函数调用的不同之处在于：应用程序不是主动的调用某个API完成处理，而是恰恰相反，Reactor逆置了事件处理流程，应用程序需要提供相应的接口并注册到Reactor上，如果相应的时间发生，Reactor将主动调用应用程序注册的接口，这些接口又称为&#8220;回调函数&#8221;。使用Libevent也是想Libevent框架注册相应的事件和回调函数；当这些时间发声时，Libevent会调用这些回调函数处理相应的事件（I/O读写、定时和信号）。<br>&nbsp;&nbsp;&nbsp; 用&#8220;好莱坞原则&#8221;来形容Reactor再合适不过了：不要打电话给我们，我们会打电话通知你。<br>&nbsp;&nbsp;&nbsp; 举个例子：你去应聘某xx公司，面试结束后。<br>&#8220;普通函数调用机制&#8221;公司HR比较懒，不会记你的联系方式，那怎么办呢，你只能面试完后自己打电话去问结果；有没有被录取啊，还是被据了；</p>
<p>&#8220;Reactor&#8221;公司HR就记下了你的联系方式，结果出来后会主动打电话通知你：有没有被录取啊，还是被据了；你不用自己打电话去问结果，事实上也不能，你没有HR的留联系方式。</p>
<p>2 Reactor模式的优点<br>Reactor模式是编写高性能网络服务器的必备技术之一，它具有如下的优点：<br>&nbsp;&nbsp;&nbsp; 1）响应快，不必为单个同步时间所阻塞，虽然Reactor本身依然是同步的；<br>&nbsp;&nbsp;&nbsp; 2）编程相对简单，可以最大程度的避免复杂的多线程及同步问题，并且避免了多线程/进程的切换开销；<br>&nbsp;&nbsp;&nbsp; 3）可扩展性，可以方便的通过增加Reactor实例个数来充分利用CPU资源；<br>&nbsp;&nbsp;&nbsp; 4）可复用性，reactor框架本身与具体事件处理逻辑无关，具有很高的复用性；</p>
<p>3 Reactor模式框架<br>&nbsp;&nbsp;&nbsp; 使用Reactor模型，必备的几个组件：事件源、Reactor框架、多路复用机制和事件处理程序，先来看看Reactor模型的整体框架，接下来再对每个组件做逐一说明。<br>&nbsp; </p>
<p>1） 事件源<br>Linux上是文件描述符，Windows上就是Socket或者Handle了，这里统一称为&#8220;句柄集&#8221;；程序在指定的句柄上注册关心的事件，比如I/O事件。</p>
<p>2） event demultiplexer——事件多路分发机制<br>由操作系统提供的I/O多路复用机制，比如select和epoll。<br>&nbsp;&nbsp;&nbsp; 程序首先将其关心的句柄（事件源）及其事件注册到event demultiplexer上；<br>当有事件到达时，event demultiplexer会发出通知&#8220;在已经注册的句柄集中，一个或多个句柄的事件已经就绪&#8221;；<br>&nbsp;&nbsp;&nbsp; 程序收到通知后，就可以在非阻塞的情况下对事件进行处理了。<br>对应到libevent中，依然是select、poll、epoll等，但是libevent使用结构体eventop进行了封装，以统一的接口来支持这些I/O多路复用机制，达到了对外隐藏底层系统机制的目的。</p>
<p>3） Reactor——反应器<br>&nbsp;&nbsp;&nbsp; Reactor，是事件管理的接口，内部使用event demultiplexer注册、注销事件；并运行事件循环，当有事件进入&#8220;就绪&#8221;状态时，调用注册事件的回调函数处理事件。<br>对应到libevent中，就是event_base结构体。<br>一个典型的Reactor声明方式<br>view plaincopy to clipboardprint?<br>class Reactor&nbsp;&nbsp; <br>{&nbsp;&nbsp; <br>public:&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; int register_handler(Event_Handler *pHandler, int event);&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; int remove_handler(Event_Handler *pHandler, int event);&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; void handle_events(timeval *ptv);&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; // ...&nbsp;&nbsp; <br>};&nbsp; <br>class Reactor<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; int register_handler(Event_Handler *pHandler, int event);<br>&nbsp;&nbsp;&nbsp; int remove_handler(Event_Handler *pHandler, int event);<br>&nbsp;&nbsp;&nbsp; void handle_events(timeval *ptv);<br>&nbsp;&nbsp;&nbsp; // ...<br>}; <br>4） Event Handler——事件处理程序<br>&nbsp;&nbsp;&nbsp; 事件处理程序提供了一组接口，每个接口对应了一种类型的事件，供Reactor在相应的事件发生时调用，执行相应的事件处理。通常它会绑定一个有效的句柄。<br>对应到libevent中，就是event结构体。<br>下面是两种典型的Event Handler类声明方式，二者互有优缺点。<br>view plaincopy to clipboardprint?<br>class Event_Handler&nbsp;&nbsp; <br>{&nbsp;&nbsp; <br>public:&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; virtual void handle_read() = 0;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; virtual void handle_write() = 0;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; virtual void handle_timeout() = 0;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; virtual void handle_close() = 0;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; virtual HANDLE get_handle() = 0;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; // ...&nbsp;&nbsp; <br>};&nbsp;&nbsp; <br>class Event_Handler&nbsp;&nbsp; <br>{&nbsp;&nbsp; <br>public:&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; // events maybe read/write/timeout/close .etc&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; virtual void handle_events(int events) = 0;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; virtual HANDLE get_handle() = 0;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; // ...&nbsp;&nbsp; <br>};&nbsp; <br>class Event_Handler<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; virtual void handle_read() = 0;<br>&nbsp;&nbsp;&nbsp; virtual void handle_write() = 0;<br>&nbsp;&nbsp;&nbsp; virtual void handle_timeout() = 0;<br>&nbsp;&nbsp;&nbsp; virtual void handle_close() = 0;<br>&nbsp;&nbsp;&nbsp; virtual HANDLE get_handle() = 0;<br>&nbsp;&nbsp;&nbsp; // ...<br>};<br>class Event_Handler<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; // events maybe read/write/timeout/close .etc<br>&nbsp;&nbsp;&nbsp; virtual void handle_events(int events) = 0;<br>&nbsp;&nbsp;&nbsp; virtual HANDLE get_handle() = 0;<br>&nbsp;&nbsp;&nbsp; // ...<br>}; </p>
<p>4 Reactor事件处理流程<br>前面说过Reactor将事件流&#8220;逆置&#8221;了，那么使用Reactor模式后，事件控制流是什么样子呢？<br>可以参见下面的序列图。<br>&nbsp; </p>
<p>5 小结<br>上面讲到了Reactor的基本概念、框架和处理流程，对Reactor有个基本清晰的了解后，再来对比看libevent就会更容易理解了，接下来就正式进入到libevent的代码世界了，加油！</p>
<p>&nbsp;</p>
<p><br>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/sparkliang/archive/2009/12/07/4957744.aspx">http://blog.csdn.net/sparkliang/archive/2009/12/07/4957744.aspx</a></p>
<img src ="http://www.cppblog.com/flyonok/aggbug/126185.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> 2010-09-08 22:05 <a href="http://www.cppblog.com/flyonok/archive/2010/09/08/126185.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>libevent源码深度剖析一</title><link>http://www.cppblog.com/flyonok/archive/2010/09/08/126184.html</link><dc:creator>flyonok</dc:creator><author>flyonok</author><pubDate>Wed, 08 Sep 2010 14:04:00 GMT</pubDate><guid>http://www.cppblog.com/flyonok/archive/2010/09/08/126184.html</guid><wfw:comment>http://www.cppblog.com/flyonok/comments/126184.html</wfw:comment><comments>http://www.cppblog.com/flyonok/archive/2010/09/08/126184.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flyonok/comments/commentRss/126184.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flyonok/services/trackbacks/126184.html</trackback:ping><description><![CDATA[<p>——序幕<br>张亮</p>
<p>1 前言<br>Libevent是一个轻量级的开源高性能网络库，使用者众多，研究者更甚，相关文章也不少。写这一系列文章的用意在于，一则分享心得；二则对libevent代码和设计思想做系统的、更深层次的分析，写出来，也可供后来者参考。</p>
<p>附带一句：Libevent是用c语言编写的（MS大牛们都偏爱c语言哪），而且几乎是无处不函数指针，学习其源代码也需要相当的c语言基础。</p>
<p>2 Libevent简介<br>上来当然要先夸奖啦，Libevent 有几个显著的亮点：<br>事件驱动（event-driven），高性能;<br>轻量级，专注于网络，不如ACE那么臃肿庞大；<br>源代码相当精炼、易读；<br>跨平台，支持Windows、Linux、*BSD和Mac Os；<br>支持多种I/O多路复用技术， epoll、poll、dev/poll、select和kqueue等；<br>支持I/O，定时器和信号等事件；<br>注册事件优先级；</p>
<p>Libevent已经被广泛的应用，作为底层的网络库；比如memcached、Vomit、Nylon、Netchat等等。<br>Libevent当前的最新稳定版是1.4.13；这也是本文参照的版本。</p>
<p>3 学习的好处<br>学习libevent有助于提升程序设计功力，除了网络程序设计方面外，Libevent的代码里有很多有用的设计技巧和基础数据结构，比如信息隐藏、函数指针、c语言的多态支持、链表和堆等等，都有助于提升自身的程序功力。<br>程序设计不止要了解框架，很多细节之处恰恰也是事关整个系统成败的关键。只对libevent本身的框架大概了解，那或许仅仅是一知半解，不深入代码分析，就难以了解其设计的精巧之处，也就难以为自己所用。</p>
<p>事实上Libevent本身就是一个典型的Reactor模型，理解Reactor模式是理解libevent的基石；因此下一节将介绍典型的事件驱动设计模式——Reactor模式。</p>
<p>&nbsp;</p>
<p>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/sparkliang/archive/2009/12/07/4957667.aspx">http://blog.csdn.net/sparkliang/archive/2009/12/07/4957667.aspx</a></p>
<img src ="http://www.cppblog.com/flyonok/aggbug/126184.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> 2010-09-08 22:04 <a href="http://www.cppblog.com/flyonok/archive/2010/09/08/126184.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>