【无标题】

<div id="article_content" class="article_content clearfix">
        <link rel="stylesheet" href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/editerView/ck_htmledit_views-bbac9290cd.css">
                <div id="content_views" class="markdown_views prism-atom-one-dark">
                    <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
                        <path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path>
                    </svg>
                    <p>原文:<a href="https://blog.csdn.net/jack0106/article/details/6258422">https://blog.csdn.net/jack0106/article/details/6258422</a><br> 作者联系方式:冯牮 fengjian0106@yahoo.com.cn<br> <br></p> 
<p><strong>Glib 源码</strong>:<a href="https://download.gnome.org/sources/glib/">https://download.gnome.org/sources/glib/</a><br> <strong>GLib API</strong>:<a href="https://developer.gnome.org/glib/stable/">https://developer.gnome.org/glib/stable/</a></p> 
<br> 
<p></p>
<div class="toc">
 <h3><a name="t0"></a> </h3>
 <ul><li><a href="#_17" target="_self">引入</a></li><li><a href="#GMainLoop_105" target="_self">GMainLoop的实现机制</a></li><li><ul><li><a href="#GSource_110" target="_self">GSource</a></li><li><a href="#GMainContext_131" target="_self">GMainContext</a></li></ul>
 </li></ul>
</div>
<p></p> 
<br> 
<p>做linux程序开发有一段时间了,也使用过好几个UI库,包括gtk,qt,还有clutter。其中感觉最神秘的,就是所谓的“主事件循环"。在qt中,就是QApplication,gtk中是gtk_main(),clutter中则是clutter_main()。这些事件循环对象,都被封装的很“严密",使用的时候,代码都很简单。而我们在编写应用程序的过程中,通常也只需要重载widget的event处理函数(或者是处理event对应的信号),至于event是怎样产生和传递的,这就是个谜。</p> 
<br> 
<h1><a name="t1"></a><a id="_17"></a>引入</h1> 
<p>最近时间比较充裕,仔细研究了一下事件循环,参考的代码是<code>glib</code>中的<code>GMainLoop</code>。<code>gtk_main()</code>和<code>clutter_main()</code>都是基于<code>GMainLoop</code>的。另外,其实事件循环的概念,也不仅仅使用在UI编程中,在网络编程中,同样大量的使用。可以这样说,<strong>event loop</strong> 是编程模型中,最基本的一个概念。可惜在大学教材中,从来没有看到过这个概念,玩单片机的时候,也用不到这个概念,只有在有操作系统的环境下,才会有<strong>event loop</strong>。</p> 
<p><strong>event loop</strong>的代码基础,还要用到一个概念 —— <strong><a href="https://www.baidu.com/s?wd=I/O%E7%9A%84%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8&amp;rsv_spt=1&amp;rsv_iqid=0x9b813163006f69c2&amp;issp=1&amp;f=8&amp;rsv_bp=1&amp;rsv_idx=2&amp;ie=utf-8&amp;tn=baiduhome_pg&amp;rsv_enter=1&amp;rsv_dl=ib&amp;rsv_sug3=3&amp;rsv_n=2">I/O的多路复用</a></strong>。目前常用的api接口,有3个,<code>select</code>,<code>poll</code>以及<code>epoll</code>。glib是一个跨平台的库,在linux上,使用的是<code>poll</code>函数,在window上,使用的是<code>select</code>。而<code>epoll</code>这个接口,在linux2.6中才正式推出,它的效率比前两者更高,在网络编程中大量使用。而本质上,这三个函数,其实是相同的。</p> 
<p>如果对<strong>I/O多路复用</strong>还不了解,请先自行google学习。下面,仅仅给出一个使用poll接口的代码模型片段。</p> 
<pre data-index="0" class="prettyprint"><code class="prism language-c has-numbering" οnclick="mdcp.copyCode(event)" style="position: unset;"><span class="token macro property">#<span class="token directive keyword">include</span> <span class="token string">&lt;poll.h&gt;</span></span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span class="token keyword">struct</span> pollfd fds<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token keyword">int</span> timeout_msecs <span class="token operator">=</span> <span class="token number">500</span><span class="token punctuation">;</span>
<span class="token keyword">int</span> ret<span class="token punctuation">;</span>
<span class="token keyword">int</span> i<span class="token punctuation">;</span>

<span class="token comment">/* Open STREAMS device. */</span>
fds<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>fd <span class="token operator">=</span> <span class="token function">open</span><span class="token punctuation">(</span><span class="token string">"/dev/dev0"</span><span class="token punctuation">,</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
fds<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>fd <span class="token operator">=</span> <span class="token function">open</span><span class="token punctuation">(</span><span class="token string">"/dev/dev1"</span><span class="token punctuation">,</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
fds<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>events <span class="token operator">=</span> POLLOUT <span class="token operator">|</span> POLLWRBAND<span class="token punctuation">;</span>
fds<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>events <span class="token operator">=</span> POLLOUT <span class="token operator">|</span> POLLWRBAND<span class="token punctuation">;</span>

<span class="token keyword">while</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    ret <span class="token operator">=</span> <span class="token function">poll</span><span class="token punctuation">(</span>fds<span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> timeout_msecs<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>ret <span class="token operator">&gt;</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
        <span class="token comment">/* An event on one of the fds has occurred. */</span>
        <span class="token keyword">for</span> <span class="token punctuation">(</span>i<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span> i<span class="token operator">&lt;</span><span class="token number">2</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>fds<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>revents <span class="token operator">&amp;</span> POLLWRBAND<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
        <span class="token comment">/* Priority data may be written on device number i. */</span>
        <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>fds<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>revents <span class="token operator">&amp;</span> POLLOUT<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
        <span class="token comment">/* Data may be written on device number i. */</span>
        <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>fds<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>revents <span class="token operator">&amp;</span> POLLHUP<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
        <span class="token comment">/* A hangup has occurred on device number i. */</span>
        <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
        <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<div class="hljs-button {2}" data-title="复制"></div></code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li><li style="color: rgb(153, 153, 153);">12</li><li style="color: rgb(153, 153, 153);">13</li><li style="color: rgb(153, 153, 153);">14</li><li style="color: rgb(153, 153, 153);">15</li><li style="color: rgb(153, 153, 153);">16</li><li style="color: rgb(153, 153, 153);">17</li><li style="color: rgb(153, 153, 153);">18</li><li style="color: rgb(153, 153, 153);">19</li><li style="color: rgb(153, 153, 153);">20</li><li style="color: rgb(153, 153, 153);">21</li><li style="color: rgb(153, 153, 153);">22</li><li style="color: rgb(153, 153, 153);">23</li><li style="color: rgb(153, 153, 153);">24</li><li style="color: rgb(153, 153, 153);">25</li><li style="color: rgb(153, 153, 153);">26</li><li style="color: rgb(153, 153, 153);">27</li><li style="color: rgb(153, 153, 153);">28</li><li style="color: rgb(153, 153, 153);">29</li><li style="color: rgb(153, 153, 153);">30</li><li style="color: rgb(153, 153, 153);">31</li><li style="color: rgb(153, 153, 153);">32</li><li style="color: rgb(153, 153, 153);">33</li><li style="color: rgb(153, 153, 153);">34</li></ul></pre> 
<p>上面这个代码,我们可以把它拆分成3部分:</p> 
<ol><li>准备要检测的文件集合(不是简单的准备“文件描述符"的集合,而是准备<code>struct pollfd</code>结构体的集合。这就包括了文件描述符,以及希望监控的事件,如可读/可写/或可执行其他操作等)。</li></ol> 
<pre data-index="1" class="prettyprint"><code class="prism language-c has-numbering" οnclick="mdcp.copyCode(event)" style="position: unset;"><span class="token keyword">struct</span> pollfd <span class="token punctuation">{<!-- --></span>
    <span class="token keyword">int</span> fd<span class="token punctuation">;</span>        <span class="token comment">/* 文件描述符 */</span>
    <span class="token keyword">short</span> events<span class="token punctuation">;</span>  <span class="token comment">/* 等待的事件 */</span>
    <span class="token keyword">short</span> revents<span class="token punctuation">;</span> <span class="token comment">/* 实际发生了的事件 */</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<div class="hljs-button {2}" data-title="复制"></div></code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li></ul></pre> 
<ol start="2"><li> <p>执行<code>poll</code>,等待事件发生(文件描述符对应的文件可读/可写/或可执行其他操作等)或者是函数超时返回。</p> </li><li> <p>遍历文件集合(<code>struct pollfd</code>结构体的集合),判断具体是哪些文件有“事件"发生,并且进一步判断是何种“事件"。然后,根据需求,执行对应的操作(上面的代码中,用…表示的对应操作)。</p> </li></ol> 
<p>其中<strong>2</strong>和<strong>3</strong>对应的代码,都放在一个<code>while</code>循环中。而在<strong>3</strong>中所谓的“对应的操作",还可以包括一种“退出"操作,这样的话,就可以从<code>while</code>循环中退出,这样的话,整个进程也有机会正常结束。<br> <br><br> <strong>再次提醒一下,请先把上面这段代码看懂,最好是有过实际的使用经验,这样更有助于理解。</strong><br> <br><br> 下面开始讨论重点。这段代码仅仅是演示,所以它很简单。但是,从另外一个角度来看,这个代码片段又很死板,尤其是对于新手或者是没有I/O多路复用实际使用经验的朋友来说,很容易被这段代码模型“框住"。它还能变得更灵活吗?怎样才能变得更灵活?详细解释之前,先提几个小问题。</p> 
<ol><li> <p>前面的代码,仅打开了2个文件,并且传递给poll函数。如果,在程序运行过程中,想动态的增加或者删除<code>poll</code>函数监控的文件,怎么办?</p> </li><li> <p>前面的代码,设置的超时时间,是固定的。假设,某个时刻有100个文件需要被监控,而针对这100个不同的文件,每个文件期望设置的超时时间都不一样,怎么办?</p> </li><li> <p>前面的代码,当<code>poll</code>函数返回,对文件集合进行遍历的时候,是逐个进行判断并且执行“对应的操作"。如果,有100个文件被监控,当<code>poll</code>返回时,这100个文件,都满足条件,可以进行“对应的操作",其中的50个文件的“对应的操作"很耗时间,但是并不是这么紧急(可以稍后再处理,比如等到下一轮<code>poll</code>返回时再处理),而另外50个文件的“对应的操作”需要立即执行,并且很快(在下一次<code>poll</code>的时候)又会有新的事件发生并且满足判断时的条件,怎么办?</p> </li></ol> 
<p>对第1个问题,可以想到,需要对 <strong>所有的文件</strong>(<code>struct pollfd</code>)做一个统一的管理,需要有添加和删除文件的功能。用面向对象的思想来看,这就是一个类,暂且叫做<strong>类A</strong>。</p> 
<p>对第2个问题,可以想到,还需要对 <strong>每一个被监控的文件</strong>(<code>struct pollfd</code>)做更多的控制。也可以用一个类来包装被监控的文件,对这个文件进行管理,在该对象中,包含了<code>struct pollfd</code>结构体,该类还可以提供对应的文件所期望的超时时间。暂且叫做<strong>类B</strong>。</p> 
<p>对第3个问题,可以考虑为每一个被监控的文件设置一个优先级,然后就可以根据优先级优先执行更“紧急"的“对应的操作"。这个优先级信息,也可以存储在<strong>类B</strong>中。设计出了<strong>类B</strong>之后,<strong>类A</strong>就不再是直接统一管理文件了,而是变成统一管理<strong>类B</strong>,可以看成是<strong>类B</strong>的一个容器类。</p> 
<p>有了这3个解答之后,就可以对这个代码片段添油加醋,重新组装,让它变得更灵活了。<code>glib</code>中的<code>GMainLoop</code>,做的就是这样的事情,而且,它做的事情,除了这3个解答中描述的内容外,还有更让人“吃惊的惊喜"。</p> 
<p>😃,这里又要提醒一下了,下面将对<code>GMainLoop</code>进行描述,所以,最好是先使用一下<code>GMainLoop</code>,包括其中的<code>g_timeout_source_new(guint interval)</code>,<code>g_idle_source_new(void)</code>以及<code>g_child_watch_source_new(GPid pid)</code>。顺便再强调一下,学习编程的最好的办法,就是看代码,而且是看高质量的代码。</p> 
<br> 
<h1><a name="t2"></a><a id="GMainLoop_105"></a>GMainLoop的实现机制</h1> 
<p>后面的讲解,主要是从原理上来介绍<code>GMainLoop</code>的实现机制,并不是代码的情景分析。代码( <strong>gmain.c</strong>)的详细阅读,还是需要自己老老实实的去实践的。后面的这些介绍,只是为了帮助大家更容易的理解源代码。</p> 
<p><code>glib</code>的主事件循环框架,由3个类来实现,<code>GMainLoop</code>,<code>GMainContext</code>和<code>GSource</code>,其中的<code>GMainLoop</code>仅仅是<code>GMainContext</code>的一个外壳,最重要的,还是<code>GMainContext</code>和<code>GSource</code>。<code>GMainContext</code>就相当于前面提到的<strong>类A</strong>,而<code>GSource</code>就相当于前面提到的<strong>类B</strong>。从原理上讲,<font color="red" size="4"><strong><code>g_main_loop_run(GMainLoop *loop)</code>这个函数的内部实现,和前面代码片段中的<code>while</code>循环,是一致的</strong>。</font>(还有一点要说明的,在<a href="https://so.csdn.net/so/search?q=%E5%A4%9A%E7%BA%BF%E7%A8%8B&amp;spm=1001.2101.3001.7020" target="_blank" class="hl hl-1" data-report-click="{&quot;spm&quot;:&quot;1001.2101.3001.7020&quot;,&quot;dest&quot;:&quot;https://so.csdn.net/so/search?q=%E5%A4%9A%E7%BA%BF%E7%A8%8B&amp;spm=1001.2101.3001.7020&quot;}" data-tit="多线程" data-pretit="多线程">多线程</a>的环境下,<code>GMainLoop</code>的代码实现显得比较复杂,为了学习起来更容易些,可以先不考虑<code>GMainLoop</code>中线程相关的代码,这样的话,整体结构就和前面的代码片段是一致的。后面的讲解以及代码片段,都略去了线程相关的代码,这并不影响对event loop的学习和理解)。</p> 
<h2><a name="t3"></a><a id="GSource_110"></a>GSource</h2> 
<p><code>GSource</code>相当于前面提到的<strong>类B</strong>,它里面会保存优先级信息。同时,<code>GSource</code>要管理对应的文件(保存<code>struct pollfd</code>结构体的指针,而且是<em>以链表的形式保存</em>),而且,<code>GSource</code>和被管理的文件的对应关系,不是 1对1,而是 1对n。这个n,甚至可以是0(这就是一个“吃惊的惊喜",后面会有更详细的解释)。<code>GSource</code>还必须提供3个重要的函数(从面向对象的角度看,<code>GSource</code>是一个抽象类,而且有三个重要的纯虚函数,需要子类来具体实现),这3个函数就是:</p> 
<pre data-index="2" class="prettyprint"><code class="prism language-c has-numbering" οnclick="mdcp.copyCode(event)" style="position: unset;">  gboolean <span class="token punctuation">(</span><span class="token operator">*</span>prepare<span class="token punctuation">)</span><span class="token punctuation">(</span>GSource <span class="token operator">*</span>source<span class="token punctuation">,</span> gint <span class="token operator">*</span>timeout_<span class="token punctuation">)</span><span class="token punctuation">;</span>

  gboolean <span class="token punctuation">(</span><span class="token operator">*</span>check<span class="token punctuation">)</span><span class="token punctuation">(</span>GSource <span class="token operator">*</span>source<span class="token punctuation">)</span><span class="token punctuation">;</span>

  gboolean <span class="token punctuation">(</span><span class="token operator">*</span>dispatch<span class="token punctuation">)</span><span class="token punctuation">(</span>GSource <span class="token operator">*</span>source<span class="token punctuation">,</span> GSourceFunc callback<span class="token punctuation">,</span> gpointer user_data<span class="token punctuation">)</span><span class="token punctuation">;</span>
<div class="hljs-button {2}" data-title="复制"></div></code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li></ul></pre> 
<p>再看一下前面代码片段中的3部分,这个<code>prepare</code>函数,就是要在第一部分被调用的,<code>check</code>和<code>dispathch</code>函数,就是在第3部分被调用的。有一点区别是,<code>prepare</code>函数也要放到<code>while</code>循环中,而不是在循环之外(因为要动态的增加或者删除<code>poll</code>函数监控的文件)。</p> 
<p><code>prepare</code>函数,会在执行<code>poll</code>之前被调用。该<code>GSource</code>中的<code>struct pollfd</code>是否希望被<code>poll</code>函数监控,就由<code>prepare</code>函数的返回值来决定。同时,该<code>GSource</code>希望的超时时间,也由参数<code>timeout_</code>返回。</p> 
<p><code>check</code>函数,在执行<code>poll</code>之后被调用。该<code>GSource</code>中的<code>struct pollfd</code>是否有事件发生,就由<code>check</code>函数的返回值来描述(在<code>check</code>函数中可以检测<code>struct pollfd</code>结构体中的返回信息)。</p> 
<p><code>dispatch</code>函数,在执行<code>poll</code>和<code>check</code>函数之后被调用,并且,仅当对应的<code>check</code>函数返回true的时候,对应的<code>dispatch</code>函数才会被调用,<code>dispatch</code>函数,就相当于“对应的操作"。<br> <img src="https://img-blog.csdnimg.cn/20200311164535617.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTM1NTQyMTM=,size_16,color_FFFFFF,t_70" alt="状态转化关系图"></p> 
<h2><a name="t4"></a><a id="GMainContext_131"></a>GMainContext</h2> 
<p><code>GMainContext</code>是<code>GSource</code>的容器,<code>GSource</code>可以添加到<code>GMainContext</code>里面(间接的就把<code>GSource</code>中的<code>struct pollfd</code>也添加到<code>GMainContext</code>里面了),<code>GSource</code>也可以从<code>GMainContext</code>中移除(间接的就把<code>GSource</code>中的<code>struct pollfd</code>从<code>GMainContext</code>中移除了)。<code>GMainContext</code>可以遍历<code>GSource</code>,自然就有机会调用每个<code>GSource</code>的<code>prepare/check/dispatch</code>函数,可以根据每个<code>GSource</code>的<code>prepare</code>函数的返回值来决定,是否要在<code>poll</code>函数中,监控该<code>GSource</code>管理的文件。当然可以根据<code>GSource</code>的优先级进行排序。当<code>poll</code>返回后,可以根据每个<code>GSource</code>的<code>check</code>函数的返回值来决定是否需要调用对应的<code>dispatch</code>函数。</p> 
<p>下面给出关键的代码片段,其中的<code>g_main_context_iterate()</code>函数,就相当于前面代码片段中的循环体中要做的动作。循环的退出,则是靠<code>loop-&gt;is_running</code>这个标记变量来标识的。</p> 
<pre data-index="3" class="prettyprint"><code class="prism language-c has-numbering" οnclick="mdcp.copyCode(event)" style="position: unset;"><span class="token keyword">void</span> g_main_loop_run <span class="token punctuation">(</span>GMainLoop <span class="token operator">*</span>loop<span class="token punctuation">)</span>
<span class="token punctuation">{<!-- --></span>
    GThread <span class="token operator">*</span>self <span class="token operator">=</span> G_THREAD_SELF<span class="token punctuation">;</span>
    g_return_if_fail <span class="token punctuation">(</span>loop <span class="token operator">!=</span> <span class="token constant">NULL</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    g_return_if_fail <span class="token punctuation">(</span>g_atomic_int_get <span class="token punctuation">(</span><span class="token operator">&amp;</span>loop<span class="token operator">-&gt;</span>ref_count<span class="token punctuation">)</span> <span class="token operator">&gt;</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>g_main_context_acquire <span class="token punctuation">(</span>loop<span class="token operator">-&gt;</span>context<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">{<!-- --></span>
      <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
    <span class="token punctuation">}</span> <span class="token keyword">else</span>
    LOCK_CONTEXT <span class="token punctuation">(</span>loop<span class="token operator">-&gt;</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>

    g_atomic_int_inc <span class="token punctuation">(</span><span class="token operator">&amp;</span>loop<span class="token operator">-&gt;</span>ref_count<span class="token punctuation">)</span><span class="token punctuation">;</span>
    loop<span class="token operator">-&gt;</span>is_running <span class="token operator">=</span> TRUE<span class="token punctuation">;</span>
    <span class="token keyword">while</span> <span class="token punctuation">(</span>loop<span class="token operator">-&gt;</span>is_running<span class="token punctuation">)</span>
       g_main_context_iterate <span class="token punctuation">(</span>loop<span class="token operator">-&gt;</span>context<span class="token punctuation">,</span> TRUE<span class="token punctuation">,</span> TRUE<span class="token punctuation">,</span> self<span class="token punctuation">)</span><span class="token punctuation">;</span>
       
    UNLOCK_CONTEXT <span class="token punctuation">(</span>loop<span class="token operator">-&gt;</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
    g_main_context_release <span class="token punctuation">(</span>loop<span class="token operator">-&gt;</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
    g_main_loop_unref <span class="token punctuation">(</span>loop<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">static</span> gboolean g_main_context_iterate <span class="token punctuation">(</span>GMainContext <span class="token operator">*</span>context<span class="token punctuation">,</span> 
        gboolean block<span class="token punctuation">,</span> gboolean dispatch<span class="token punctuation">,</span> GThread <span class="token operator">*</span>self<span class="token punctuation">)</span> 
<span class="token punctuation">{<!-- --></span>
    gint max_priority<span class="token punctuation">;</span>
    gint timeout<span class="token punctuation">;</span>
    gboolean some_ready<span class="token punctuation">;</span>
    gint nfds<span class="token punctuation">,</span> allocated_nfds<span class="token punctuation">;</span>
    GPollFD <span class="token operator">*</span>fds <span class="token operator">=</span> <span class="token constant">NULL</span><span class="token punctuation">;</span>
    
    UNLOCK_CONTEXT <span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>context<span class="token operator">-&gt;</span>cached_poll_array<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
        context<span class="token operator">-&gt;</span>cached_poll_array_size <span class="token operator">=</span> context<span class="token operator">-&gt;</span>n_poll_records<span class="token punctuation">;</span>
        context<span class="token operator">-&gt;</span>cached_poll_array <span class="token operator">=</span> g_new <span class="token punctuation">(</span>GPollFD<span class="token punctuation">,</span> context<span class="token operator">-&gt;</span>n_poll_records<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    allocated_nfds <span class="token operator">=</span> context<span class="token operator">-&gt;</span>cached_poll_array_size<span class="token punctuation">;</span>
    fds <span class="token operator">=</span> context<span class="token operator">-&gt;</span>cached_poll_array<span class="token punctuation">;</span>
    
    UNLOCK_CONTEXT <span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    <span class="token function">g_main_context_prepare</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> <span class="token operator">&amp;</span>max_priority<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>nfds <span class="token operator">=</span> <span class="token function">g_main_context_query</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> max_priority<span class="token punctuation">,</span> <span class="token operator">&amp;</span>timeout<span class="token punctuation">,</span> fds<span class="token punctuation">,</span>
                                        allocated_nfds<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">&gt;</span> allocated_nfds<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
        LOCK_CONTEXT <span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">g_free</span><span class="token punctuation">(</span>fds<span class="token punctuation">)</span><span class="token punctuation">;</span>
        context<span class="token operator">-&gt;</span>cached_poll_array_size <span class="token operator">=</span> allocated_nfds <span class="token operator">=</span> nfds<span class="token punctuation">;</span>
        context<span class="token operator">-&gt;</span>cached_poll_array <span class="token operator">=</span> fds <span class="token operator">=</span> g_new <span class="token punctuation">(</span>GPollFD<span class="token punctuation">,</span> nfds<span class="token punctuation">)</span><span class="token punctuation">;</span>
        UNLOCK_CONTEXT <span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>block<span class="token punctuation">)</span>
        timeout <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
    <span class="token function">g_main_context_poll</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> timeout<span class="token punctuation">,</span> max_priority<span class="token punctuation">,</span> fds<span class="token punctuation">,</span> nfds<span class="token punctuation">)</span><span class="token punctuation">;</span>
    some_ready <span class="token operator">=</span> <span class="token function">g_main_context_check</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> max_priority<span class="token punctuation">,</span> fds<span class="token punctuation">,</span> nfds<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>dispatch<span class="token punctuation">)</span>
        <span class="token function">g_main_context_dispatch</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
  
    LOCK_CONTEXT <span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> some_ready<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<div class="hljs-button {2}" data-title="复制"></div></code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li><li style="color: rgb(153, 153, 153);">12</li><li style="color: rgb(153, 153, 153);">13</li><li style="color: rgb(153, 153, 153);">14</li><li style="color: rgb(153, 153, 153);">15</li><li style="color: rgb(153, 153, 153);">16</li><li style="color: rgb(153, 153, 153);">17</li><li style="color: rgb(153, 153, 153);">18</li><li style="color: rgb(153, 153, 153);">19</li><li style="color: rgb(153, 153, 153);">20</li><li style="color: rgb(153, 153, 153);">21</li><li style="color: rgb(153, 153, 153);">22</li><li style="color: rgb(153, 153, 153);">23</li><li style="color: rgb(153, 153, 153);">24</li><li style="color: rgb(153, 153, 153);">25</li><li style="color: rgb(153, 153, 153);">26</li><li style="color: rgb(153, 153, 153);">27</li><li style="color: rgb(153, 153, 153);">28</li><li style="color: rgb(153, 153, 153);">29</li><li style="color: rgb(153, 153, 153);">30</li><li style="color: rgb(153, 153, 153);">31</li><li style="color: rgb(153, 153, 153);">32</li><li style="color: rgb(153, 153, 153);">33</li><li style="color: rgb(153, 153, 153);">34</li><li style="color: rgb(153, 153, 153);">35</li><li style="color: rgb(153, 153, 153);">36</li><li style="color: rgb(153, 153, 153);">37</li><li style="color: rgb(153, 153, 153);">38</li><li style="color: rgb(153, 153, 153);">39</li><li style="color: rgb(153, 153, 153);">40</li><li style="color: rgb(153, 153, 153);">41</li><li style="color: rgb(153, 153, 153);">42</li><li style="color: rgb(153, 153, 153);">43</li><li style="color: rgb(153, 153, 153);">44</li><li style="color: rgb(153, 153, 153);">45</li><li style="color: rgb(153, 153, 153);">46</li><li style="color: rgb(153, 153, 153);">47</li><li style="color: rgb(153, 153, 153);">48</li><li style="color: rgb(153, 153, 153);">49</li><li style="color: rgb(153, 153, 153);">50</li><li style="color: rgb(153, 153, 153);">51</li><li style="color: rgb(153, 153, 153);">52</li><li style="color: rgb(153, 153, 153);">53</li><li style="color: rgb(153, 153, 153);">54</li><li style="color: rgb(153, 153, 153);">55</li><li style="color: rgb(153, 153, 153);">56</li><li style="color: rgb(153, 153, 153);">57</li><li style="color: rgb(153, 153, 153);">58</li><li style="color: rgb(153, 153, 153);">59</li><li style="color: rgb(153, 153, 153);">60</li></ul></pre> 
<p>仔细看一下<code>g_main_context_iterate()</code>函数,也可以把它划分成3个部分,和前面代码片段的3部分对应上。</p> 
<ol><li>第一部份,准备要检测的文件集合</li></ol> 
<pre data-index="4" class="prettyprint"><code class="prism language-c has-numbering" οnclick="mdcp.copyCode(event)" style="position: unset;"><span class="token function">g_main_context_prepare</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> <span class="token operator">&amp;</span>max_priority<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>nfds <span class="token operator">=</span> <span class="token function">g_main_context_query</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> max_priority<span class="token punctuation">,</span> <span class="token operator">&amp;</span>timeout<span class="token punctuation">,</span> fds<span class="token punctuation">,</span>
                                    allocated_nfds<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">&gt;</span> allocated_nfds<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    LOCK_CONTEXT <span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">g_free</span><span class="token punctuation">(</span>fds<span class="token punctuation">)</span><span class="token punctuation">;</span>
    context<span class="token operator">-&gt;</span>cached_poll_array_size <span class="token operator">=</span> allocated_nfds <span class="token operator">=</span> nfds<span class="token punctuation">;</span>
    context<span class="token operator">-&gt;</span>cached_poll_array <span class="token operator">=</span> fds <span class="token operator">=</span> g_new <span class="token punctuation">(</span>GPollFD<span class="token punctuation">,</span> nfds<span class="token punctuation">)</span><span class="token punctuation">;</span>
    UNLOCK_CONTEXT <span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<div class="hljs-button {2}" data-title="复制"></div></code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li></ul></pre> 
<p>首先是调用<code>g_main_context_prepare(context, &amp;max_priority)</code>,这个就是遍历每个<code>GSource</code>,调用每个<code>GSource</code>的<code>prepare</code>函数,选出一个最高的优先级<code>max_priority</code>,函数内部其实还计算出了一个最短的超时时间。</p> 
<p>然后调用<code>g_main_context_query</code>,其实这是再次遍历每个<code>GSource</code>,把优先级等于<code>max_priority</code>的<code>GSource</code>中的<code>struct pollfd</code>,添加到<code>poll</code>的监控集合中。</p> 
<p>这个优先级,也是一个“吃惊的惊喜"。按照通常的想法,文件需要被监控的时候,会立刻把它放到监控集合中,但是有了优先级这个概念后,我们就可以有一个“隐藏的后台任务", <code>g_idle_source_new(void)</code>就是最典型的例子。</p> 
<ol start="2"><li>第二部份,执行<code>poll</code>,等待事件发生。</li></ol> 
<pre data-index="5" class="prettyprint"><code class="prism language-c has-numbering" οnclick="mdcp.copyCode(event)" style="position: unset;"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>block<span class="token punctuation">)</span>
    timeout <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token function">g_main_context_poll</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> timeout<span class="token punctuation">,</span> max_priority<span class="token punctuation">,</span> fds<span class="token punctuation">,</span> nfds<span class="token punctuation">)</span><span class="token punctuation">;</span>
<div class="hljs-button {2}" data-title="复制"></div></code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li></ul></pre> 
<p>就是调用<code>g_main_context_poll(context, timeout, max_priority, fds, nfds)</code>,<code>g_main_context_poll</code>只是对<code>poll</code>函数的一个简单封装。</p> 
<ol start="3"><li>第三部分,遍历文件集合(<code>struct pollfd</code>结构体的集合),执行对应的操作。</li></ol> 
<pre data-index="6" class="prettyprint"><code class="prism language-c has-numbering" οnclick="mdcp.copyCode(event)" style="position: unset;">some_ready <span class="token operator">=</span> <span class="token function">g_main_context_check</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> max_priority<span class="token punctuation">,</span> fds<span class="token punctuation">,</span> nfds<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>dispatch<span class="token punctuation">)</span>
    <span class="token function">g_main_context_dispatch</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span>
<div class="hljs-button {2}" data-title="复制"></div></code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li></ul></pre> 
<p>通常的想法,可能会是这种伪代码形式(这种形式也和前面代码片段的形式是一致的)</p> 
<pre data-index="7" class="prettyprint"><code class="prism language-c has-numbering" οnclick="mdcp.copyCode(event)" style="position: unset;"><span class="token function">foreach</span><span class="token punctuation">(</span>all_gsouce<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>gsourc<span class="token operator">-&gt;</span>check<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
         gsource<span class="token operator">-&gt;</span><span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<div class="hljs-button {2}" data-title="复制"></div></code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li></ul></pre> 
<p>实际上,<code>glib</code>的处理方式是,先遍历所有的<code>GSource</code>,执行<code>g_main_context_prepare(context, &amp;max_priority)</code>,调用每个GSource的<code>check</code>函数,然后把满足条件的<code>GSource</code>(<code>check</code>函数返回<code>true</code>的<code>GSource</code>),添加到一个内部链表中。</p> 
<p>然后执行<code>g_main_context_dispatch(context)</code>,遍历刚才准备好的内部链表中的<code>GSource</code>,调用每个<code>GSource</code>的<code>dispatch</code>函数。</p> 
<br> 
<p>ok,分析到此结束,总结一下,重点,首先是要先理解<code>poll</code>函数的使用方法,建立<strong>I/O多路复用</strong>的概念,然后,建议看一下<code>GMainContext</code>的源代码实现,这样才有助于理解。</p>
                </div><div><div></div></div>
                <link href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/editerView/markdown_views-3fd7f7a902.css" rel="stylesheet">
                <link href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/style-49037e4d27.css" rel="stylesheet">
        </div>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值