<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Nginx on codedump notes</title>
    <link>https://www.codedump.info/zh/tags/nginx/</link>
    <description>Recent content in Nginx on codedump notes</description>
    <generator>Hugo</generator>
    <language>zh</language>
    <lastBuildDate>Wed, 01 May 2019 15:03:45 +0800</lastBuildDate>
    <atom:link href="https://www.codedump.info/zh/tags/nginx/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>OpenResty Lua Stream实现分析</title>
      <link>https://www.codedump.info/zh/post/20190501-lua-stream/</link>
      <pubDate>Wed, 01 May 2019 15:03:45 +0800</pubDate>
      <guid>https://www.codedump.info/zh/post/20190501-lua-stream/</guid>
      <description>&lt;h1 class=&#34;heading&#34; id=&#34;概述&#34;&gt;&#xA;  概述&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e6%a6%82%e8%bf%b0&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;OpenResty（以下简称OR）是Lua应用的典范，其最大的亮点在于，使用Lua协程搭配上异步非阻塞的IO，这样开发者可以使用同步方式来编写代码，而底层IO调度、唤醒等操作留给C编写的引擎层。&lt;/p&gt;&#xA;&lt;p&gt;实际上，使用类协程的技术，让异步操作同步化，已经有很多相关的技术了，比如腾讯的libco、百度的brpc都是自己在C层面实现了类协程的机制，不过这一类技术用的最广泛的还是OR。市面上分析OR内部实现的文章并不算很多，所以这段时间研究了一下OR的实现。&lt;/p&gt;&#xA;&lt;p&gt;OR内部，其实是分7层HTTP的ngx_lua模块，以及四层TCP的lua_stream实现，两者在很多部分都很相近，以下分析以4层的lua_stream来解释，对应的版本是openresty-1.13.6.1和ngx_stream_lua-0.0.3的实现。&lt;/p&gt;&#xA;&lt;p&gt;既然OR在这里选择了使用协程来将用户的异步操作同步化，那么实际上内部其实实现了一个简易版本的操作系统内核的“CPU调度”，其中一个一个的协程就是CPU调度单位，因此在这里分为几部分来分析：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;维护协程的数据结构。&lt;/li&gt;&#xA;&lt;li&gt;创建新协程的时候如何进行初始化？&lt;/li&gt;&#xA;&lt;li&gt;协程调度算法？&lt;/li&gt;&#xA;&lt;li&gt;如何将异步操作同步化？&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;在这里，先列举出来OR中与“调度”相关的核心数据结构和函数：&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;调度相关核心组件&lt;/th&gt;&#xA;          &lt;th&gt;数据结构或函数&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;调度单元&lt;/td&gt;&#xA;          &lt;td&gt;Lua协程（lua_State）&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;保存协程信息&lt;/td&gt;&#xA;          &lt;td&gt;ngx_stream_lua_co_ctx_t&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;当前调度协程信息&lt;/td&gt;&#xA;          &lt;td&gt;ngx_stream_lua_ctx_t.cur_co_ctx成员，指向一个ngx_stream_lua_co_ctx_t类型指针&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;调度函数&lt;/td&gt;&#xA;          &lt;td&gt;ngx_stream_lua_run_thread&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h1 class=&#34;heading&#34; id=&#34;协程的维护&#34;&gt;&#xA;  协程的维护&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e5%8d%8f%e7%a8%8b%e7%9a%84%e7%bb%b4%e6%8a%a4&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;OR中有以下两种场景能够创建出来一个协程：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;一个tcp请求自动对应一个协程。这种场景用户不能控制，即默认就是这么实现的，当收到一个TCP请求默认创建出来一个协程与之绑定。&lt;/li&gt;&#xA;&lt;li&gt;Lua代码内部显示调用thread.spawn函数创建一个用户线程时。与前者不同，这种场景就是用户可以自己控制的。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;lua stream内部，协程相关的数据结构存储在ngx_stream_lua_co_ctx_t中，既然OR里面使用协程来模拟用户线程，不难想象这个数据结构内部应该有以下的成员：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;维护协程内部栈关系的数据。由于OR采用了Lua协程，这部分当然就是留给Lua协程来处理了。&lt;/li&gt;&#xA;&lt;li&gt;保存协程状态的数据。&lt;/li&gt;&#xA;&lt;li&gt;维护协程之间关系的数据，比如父子协程、僵尸子协程，等等。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;下面简单的看一下其成员：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;void *data：存储用户相关数据。&lt;/li&gt;&#xA;&lt;li&gt;lua_State *co：存储Lua协程指针。&lt;/li&gt;&#xA;&lt;li&gt;ngx_stream_lua_co_ctx_t *parent_co_ctx：存储父协程指针。&lt;/li&gt;&#xA;&lt;li&gt;ngx_stream_lua_posted_thread_t *zombie_child_threads：将该协程管理的僵尸子进程放在这个队列中。&lt;/li&gt;&#xA;&lt;li&gt;int co_ref：在Lua的registry表中对应该协程指针的引用值。&lt;/li&gt;&#xA;&lt;li&gt;unsigned waited_by_parent：为1的情况下表示该协程的父协程在等待该协程的退出。&lt;/li&gt;&#xA;&lt;li&gt;unsigned co_status：当前协程状态。&lt;/li&gt;&#xA;&lt;li&gt;unsigned is_uthread：为1的情况下表示该协程是用户线程，即上面提到的场景2创建出来的协程。&lt;/li&gt;&#xA;&lt;li&gt;unsigned thread_spawn_yielded：为1的情况下表示当前协程是由于创建了用户线程（前面的场景2）才让出的执行权。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;ngx_stream_lua_co_ctx_t&#34; src=&#34;https://www.codedump.info/media/imgs/20190501-lua-stream/ngx_stream_lua_co_ctx_t.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;caption-container&#34;&gt;&#xA;        &lt;figcaption&gt; ngx_stream_lua_co_ctx_t &lt;/figcaption&gt;&#xA;    &lt;/div&gt;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;另外，还有一个全局变量ngx_stream_lua_ctx_t，其中的cur_co_ctx指针指向当前被调度执行的ngx_stream_lua_co_ctx_t指针。&lt;/p&gt;&#xA;&lt;h1 class=&#34;heading&#34; id=&#34;协程的初始化&#34;&gt;&#xA;  协程的初始化&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e5%8d%8f%e7%a8%8b%e7%9a%84%e5%88%9d%e5%a7%8b%e5%8c%96&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;上一部分提到了创建协程的两种场景，这里就来分析这两种场景下面协程的初始化。&lt;/p&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;新建立连接的协程&#34;&gt;&#xA;  新建立连接的协程&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e6%96%b0%e5%bb%ba%e7%ab%8b%e8%bf%9e%e6%8e%a5%e7%9a%84%e5%8d%8f%e7%a8%8b&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;OR通过在nginx配置文件中填写&amp;quot;content_by_lua_block&amp;quot;等，来配置新建一个连接时对应的Lua脚本，这种场景下OR会默认创建出来一个Lua协程来执行这段脚本代码。&lt;/p&gt;&#xA;&lt;p&gt;对应创建Lua协程的代码在函数ngx_stream_lua_new_thread中，下面来分析这个函数的流程。&lt;/p&gt;&#xA;&lt;p&gt;OR中需要在Registry表中存储每个创建出来的Lua协程的reference，这个存储协程的表在Registry表中对应的key是全局变量ngx_stream_lua_coroutines_key的指针，因此下面这段代码就是从Registry表中查询这个表返回到栈顶：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;lua_pushlightuserdata&lt;/span&gt;(L, &amp;amp;ngx_stream_lua_coroutines_key);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;lua_rawget&lt;/span&gt;(L, LUA_REGISTRYINDEX);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;接着下来就是创建了一个新的协程，同时初始化其全局表：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 创建Lua协程&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;co = &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;lua_newthread&lt;/span&gt;(L);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 创建该协程的全局表&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;ngx_stream_lua_create_new_globals_table&lt;/span&gt;(co, 0, 0);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 再创建一个新表&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;lua_createtable&lt;/span&gt;(co, 0, 1);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 拿到全局表&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;ngx_stream_lua_get_globals_table&lt;/span&gt;(co);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 全局表的__index指向新创建的表&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;lua_setfield&lt;/span&gt;(co, -2, &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;__index&amp;#34;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 全局表的meta table指向新创建的表&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;lua_setmetatable&lt;/span&gt;(co, -2);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// set 全局表回去&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;ngx_stream_lua_set_globals_table&lt;/span&gt;(co);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;从上面的代码可以看出，新创建的协程，其全局表目前是一个空表。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Nginx源码阅读笔记-内存池的设计</title>
      <link>https://www.codedump.info/zh/post/20190214-nginx-memory-pool/</link>
      <pubDate>Thu, 14 Feb 2019 14:48:24 +0800</pubDate>
      <guid>https://www.codedump.info/zh/post/20190214-nginx-memory-pool/</guid>
      <description>&lt;p&gt;nginx中所有请求都单独对应一个内存池，在这个请求的过程中，所有涉及到内存分配的地方，都到该请求相关的内存池中处理，而中间不会去释放回收内存，内存池的生命周期与请求一样，请求完毕则直接回收内存。这样的好处在于：统一分配和统一释放，降低了内存泄露问题的出现。&lt;/p&gt;&#xA;&lt;p&gt;nginx的内存池设计的比较简单了，一个内存池中分为两个部分：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;超过max大小的内存分配，走大块内存分配，这部分内存管理由ngx_pool_large_t结构体负责。&lt;/li&gt;&#xA;&lt;li&gt;否则就是在ngx_pool_t遍历符合要求的ngx_pool_t结构体，找到符合要求大小的pool直接返回，否则就申请一块新的内存pool。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h1 class=&#34;heading&#34; id=&#34;ngx_pool_data_t&#34;&gt;&#xA;  ngx_pool_data_t&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#ngx_pool_data_t&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;先来看结构体ngx_pool_data_t，它存储每个ngx_pool_t结构体的meta元数据：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;u_char *last：指向分配空间的可用空间。&lt;/li&gt;&#xA;&lt;li&gt;u_char *end：指向分配空间的最后位置。&lt;/li&gt;&#xA;&lt;li&gt;ngx_pool_t *next：指向下一个ngx_pool_t指针。&lt;/li&gt;&#xA;&lt;li&gt;ngx_uint_t failed：存储本ngx_pool_t结构体分配失败次数。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;ngx_pool_data_t&#34; src=&#34;https://www.codedump.info/media/imgs/20190214-nginx-memory-pool/ngx_pool_data_t.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;caption-container&#34;&gt;&#xA;        &lt;figcaption&gt; ngx_pool_data_t &lt;/figcaption&gt;&#xA;    &lt;/div&gt;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;failed成员的引入是为了避免某个pool虽然还有可用的空间，但是由于空间很小了所以经常性的分配空间失败，当累计失败的次数达到某个阈值时，下一次再次查找内存就直接跳过这个pool，而去寻找内存池链表中的下一个pool。&lt;/p&gt;&#xA;&lt;h1 class=&#34;heading&#34; id=&#34;ngx_pool_large_t&#34;&gt;&#xA;  ngx_pool_large_t&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#ngx_pool_large_t&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;ngx_pool_large_t结构体用于保存大内存块，这一块就比较简单粗暴了，直接分配一块大内存来使用。另外，多个大内存块之间也是以链表形式来组织数据。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 管理超大空间的结构体&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;&lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;struct&lt;/span&gt; ngx_pool_large_s {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 指向下一个指针&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_pool_large_t&lt;/span&gt;     *next;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 直接指向内存区域的指针&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;void&lt;/span&gt;                 *alloc;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 class=&#34;heading&#34; id=&#34;ngx_pool_t&#34;&gt;&#xA;  ngx_pool_t&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#ngx_pool_t&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;再来看ngx_pool_t结构体，该数据结构用于表示一个内存池，内存池内部以链表形式来组织数据。如下图：&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;ngx_pool_t&#34; src=&#34;https://www.codedump.info/media/imgs/20190214-nginx-memory-pool/ngx_pool_t.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;caption-container&#34;&gt;&#xA;        &lt;figcaption&gt; ngx_pool_t &lt;/figcaption&gt;&#xA;    &lt;/div&gt;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;需要说明的是：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;内存池内部以链表形式组织起来，完成这个工作的就是前面的ngx_pool_data_t的next成员。&lt;/li&gt;&#xA;&lt;li&gt;current指针，用于表示当前该内存池在使用的pool指针。除了内存池链表的头结点之外，内存池链表其他节点的该指针无效。之所以需要这个指针，就是前面提到的，在某个内存池多次失效的情况下，下一次直接跳过该内存池查找空间，current指针保存当前在内存池链表的哪一个内存池上面查找空间。&lt;/li&gt;&#xA;&lt;li&gt;large指针，指向ngx_pool_large_t结构体，管理大块内存。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;有了以上数据结构的了解，从内存池分配内存的流程就很简单了：&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;memory-pool&#34; src=&#34;https://www.codedump.info/media/imgs/20190214-nginx-memory-pool/memory-pool.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;caption-container&#34;&gt;&#xA;        &lt;figcaption&gt; memory-pool &lt;/figcaption&gt;&#xA;    &lt;/div&gt;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Nginx源码阅读笔记-处理HTTP请求</title>
      <link>https://www.codedump.info/zh/post/20190213-nginx-process-http-request/</link>
      <pubDate>Wed, 13 Feb 2019 09:09:19 +0800</pubDate>
      <guid>https://www.codedump.info/zh/post/20190213-nginx-process-http-request/</guid>
      <description>&lt;p&gt;前面分析了nginx&lt;a href=&#34;https://www.codedump.info/post/20190131-nginx-read-http-request/&#34;&gt;如何读取一个HTTP请求&lt;/a&gt;、&lt;a href=&#34;https://www.codedump.info/post/20190212-nginx-http-config/&#34;&gt;如何查询到HTTP对应的配置&lt;/a&gt;，本节分析如何处理HTTP请求。&lt;/p&gt;&#xA;&lt;h1 class=&#34;heading&#34; id=&#34;处理http请求的11个阶段&#34;&gt;&#xA;  处理HTTP请求的11个阶段&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e5%a4%84%e7%90%86http%e8%af%b7%e6%b1%82%e7%9a%8411%e4%b8%aa%e9%98%b6%e6%ae%b5&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;nginx将处理HTTP请求划分为了11个阶段，原因在于nginx是一个重度模块化的系统，划分为不同阶段以后，不同的模块可以根据自己的需求在相应的模块中添加自己的处理函数。&lt;/p&gt;&#xA;&lt;p&gt;简单看看这11个模块的定义：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;enum&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 在接收到完整的HTTP头部后处理的HTTP阶段&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  NGX_HTTP_POST_READ_PHASE = 0,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 在将请求的URI与location表达式匹配前，修改请求的&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// URI（所谓的重定向）是一个独立的HTTP阶段&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  NGX_HTTP_SERVER_REWRITE_PHASE,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 根据请求的URL寻找匹配的location表达式，这个阶段&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 只能由ngx_http_core_module模块实现，不建议其他HTTP&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 模块模块重新定义这一阶段的行为&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  NGX_HTTP_FIND_CONFIG_PHASE,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 在NGX_HTTP_FIND_CONFIG_PHASE阶段寻找到匹配的location&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 之后再修改请求的URI&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  NGX_HTTP_REWRITE_PHASE,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 这一阶段用于在rewrite重写URL后，防止错误的nginx配置导致&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 死循环（递归地修改URI），因此，这一阶段仅由ngx_http_core_module&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 模块处理。目前，控制死循环的方法就是看rewrite次数，超过一定阈值&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 就认为出现了死循环，返回500&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  NGX_HTTP_POST_REWRITE_PHASE,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 表示在处理NGX_HTTP_ACCESS_PHASE阶段决定请求的访问权限前，HTTP模块可以介入的处理阶段&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  NGX_HTTP_PREACCESS_PHASE,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 这个阶段用于让HTTP模块判断是否允许这个请求访问Nginx服务器&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  NGX_HTTP_ACCESS_PHASE,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 在NGX_HTTP_ACCESS_PHASE阶段中，当HTTP模块的handler处理函数返回不允许访问的错误码时（&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 实际就是NGX_HTTP_FORBIDDEN或者NGX_HTTP_UNAUTHORIZED），这里将负责向用户发送拒绝服务的&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 错误响应，因此这个阶段实际上用于给NGX_HTTP_ACCESS_PHASE阶段收尾&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  NGX_HTTP_POST_ACCESS_PHASE,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 这个阶段完全为try_files配置项而设立的，当HTTP访问静态文件资源时，try_files配置项可以&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 使这个请求顺序地访问多个静态文件资源，如果某一次访问失败，则继续访问try_files中指定的&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 下一个静态资源。&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  NGX_HTTP_PRECONTENT_PHASE,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 用于处理HTTP请求内容的阶段，这是大部分HTTP模块最愿意介入的阶段&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  NGX_HTTP_CONTENT_PHASE,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 处理完请求记录日志的阶段。&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  NGX_HTTP_LOG_PHASE&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} ngx_http_phases;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这11个阶段里，有一些是可以由模块开发者插入自己的处理函数，有一些只能使用nginx的http框架的实现。另外，每个阶段并不是一定只能有一个处理函数，有的可以提供多个处理函数，在同一个阶段中顺序被调用。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Nginx源码阅读笔记-查询HTTP配置流程</title>
      <link>https://www.codedump.info/zh/post/20190212-nginx-http-config/</link>
      <pubDate>Tue, 12 Feb 2019 09:54:25 +0800</pubDate>
      <guid>https://www.codedump.info/zh/post/20190212-nginx-http-config/</guid>
      <description>&lt;h1 class=&#34;heading&#34; id=&#34;概述&#34;&gt;&#xA;  概述&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e6%a6%82%e8%bf%b0&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;前面已经分析过&lt;a href=&#34;https://www.codedump.info/post/20190103-nginx-config-parse/#%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99&#34;&gt;nginx解析配置文件的整体流程&lt;/a&gt;，接下来看查询HTTP配置的流程。&lt;/p&gt;&#xA;&lt;p&gt;HTTP属于nginx的core顶层模块，下面又包括了三部分：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;main部分配置：即在HTTP块但是又不在任何server、location块中的配置，如下图中的sendfile配置指令。&lt;/li&gt;&#xA;&lt;li&gt;server块：在server块内部的配置。&lt;/li&gt;&#xA;&lt;li&gt;location块：在location块内部分配置。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;http_config&#34; src=&#34;https://www.codedump.info/media/imgs/20190212-nginx-http-config/http_config.jpg&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;caption-container&#34;&gt;&#xA;        &lt;figcaption&gt; http_config &lt;/figcaption&gt;&#xA;    &lt;/div&gt;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;解析HTTP模块的入口函数是ngx_http_block，这一点可以从http指令相关的配置看出：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{ &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;ngx_string&lt;/span&gt;(&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;http),&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ngx_http_block,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  0,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  0,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;NULL&lt;/span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这个解析函数的开始，就创建了ngx_http_conf_ctx_t结构体，所以看的出来这个结构体是HTTP模块的第一级配置，它的定义如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;void&lt;/span&gt;        **main_conf;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;void&lt;/span&gt;        **srv_conf;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;void&lt;/span&gt;        **loc_conf;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_http_conf_ctx_t&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;下面列举出来这几部分相关的函数以及数据结构：&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;块&lt;/th&gt;&#xA;          &lt;th&gt;入口函数&lt;/th&gt;&#xA;          &lt;th&gt;数据结构&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;http&lt;/td&gt;&#xA;          &lt;td&gt;ngx_http_block&lt;/td&gt;&#xA;          &lt;td&gt;ngx_http_conf_ctx_t&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;main&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;ngx_http_core_main_conf_t&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;server&lt;/td&gt;&#xA;          &lt;td&gt;ngx_http_core_server&lt;/td&gt;&#xA;          &lt;td&gt;ngx_http_core_srv_conf_t&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;location&lt;/td&gt;&#xA;          &lt;td&gt;ngx_http_core_location&lt;/td&gt;&#xA;          &lt;td&gt;ngx_http_core_loc_conf_t&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;ngx_http_module&#34; src=&#34;https://www.codedump.info/media/imgs/20190212-nginx-http-config/ngx_http_module.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;caption-container&#34;&gt;&#xA;        &lt;figcaption&gt; ngx_http_module &lt;/figcaption&gt;&#xA;    &lt;/div&gt;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;另外，由于HTTP块内的一些配置，作用域可以在多种块中，因此需要涉及到合并配置的流程，即：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;如果子作用域某配置项在解析过程中未被赋值，则将父作用域的 相同的配置项值拷贝至此配置项里；&lt;/li&gt;&#xA;&lt;li&gt;如果子作用域配置项在解析过程中被赋值了，则保留原 样；如果子作用域配置项和父作用域配置项都没有被初始化，则填入代码中预设的默认值。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;相关的合并配置函数列举如下：&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;块&lt;/th&gt;&#xA;          &lt;th&gt;合并函数&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;server&lt;/td&gt;&#xA;          &lt;td&gt;ngx_http_merge_servers&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;location&lt;/td&gt;&#xA;          &lt;td&gt;ngx_http_merge_locations&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;以下具体看看一次HTTP请求如何查找到相关HTTP配置的流程，分为两步：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;根据Host查找server块&lt;/li&gt;&#xA;&lt;li&gt;根据URI查找location块&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h1 class=&#34;heading&#34; id=&#34;根据host查找server块流程&#34;&gt;&#xA;  根据Host查找server块流程&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e6%a0%b9%e6%8d%aehost%e6%9f%a5%e6%89%beserver%e5%9d%97%e6%b5%81%e7%a8%8b&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;前面分析&lt;a href=&#34;https://www.codedump.info/post/20190131-nginx-read-http-request/&#34;&gt;nginx接收HTTP请求流程&lt;/a&gt;中分析到，nginx在接收HTTP请求流程中，将调用ngx_http_process_request_headers函数来处理请求头。&lt;/p&gt;&#xA;&lt;p&gt;nginx使用一个ngx_http_header_t结构体，定义了哪些请求头需要进行特定的函数回调处理，函数ngx_http_process_request_headers会根据这个表来查询接收到的请求头都需要哪些回调函数来处理：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_http_header_t&lt;/span&gt;  ngx_http_headers_in[] = {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  { &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;ngx_string&lt;/span&gt;(&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;Host&amp;#34;&lt;/span&gt;), &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;offsetof&lt;/span&gt;(&lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_http_headers_in_t&lt;/span&gt;, host),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ngx_http_process_host },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  { &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;ngx_string&lt;/span&gt;(&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;Connection&amp;#34;&lt;/span&gt;), &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;offsetof&lt;/span&gt;(&lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_http_headers_in_t&lt;/span&gt;, connection),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ngx_http_process_connection },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ....&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以看到，针对Host这个header，会调用ngx_http_process_host函数，这个函数最终会调用ngx_http_set_virtual_server函数来根据Host头确定对应的server块。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Nginx源码阅读笔记-接收HTTP请求流程</title>
      <link>https://www.codedump.info/zh/post/20190131-nginx-read-http-request/</link>
      <pubDate>Fri, 01 Feb 2019 17:50:37 +0800</pubDate>
      <guid>https://www.codedump.info/zh/post/20190131-nginx-read-http-request/</guid>
      <description>&lt;p&gt;前面已经描述过nginx的事件模块了，接下来具体分析nginx如何接收一个HTTP请求，下一部分接着解析nginx解析HTTP请求的流程。&lt;/p&gt;&#xA;&lt;h1 class=&#34;heading&#34; id=&#34;协议状态机编程模式&#34;&gt;&#xA;  协议状态机编程模式&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e5%8d%8f%e8%ae%ae%e7%8a%b6%e6%80%81%e6%9c%ba%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;TCP协议是一种流协议（stream protocol），这意味着数据是以字节流形式给数据接收者的，一次网络接收不一定能接收完毕，需要上面的应用层根据自己协议的情况来解析处理。它的数据没有边界，需要应用层自己根据协议来判断边界的存在。&lt;/p&gt;&#xA;&lt;p&gt;如果两次请求，分开为几次接收，但是某次接收的数据中，有跨两次请求的数据，这就是所谓的“粘包(sticky-package)”问题。如下图所示：&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;sticky-package-problem&#34; src=&#34;https://www.codedump.info/media/imgs/20190131-nginx-read-http-request/sticky-package-problem.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;caption-container&#34;&gt;&#xA;        &lt;figcaption&gt; sticky-package-problem &lt;/figcaption&gt;&#xA;    &lt;/div&gt;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;结合epoll之类的事件派发器来设计一个TCP协议的服务器时，因为并不能确保每一次接收数据，都能完整的接收到协议所需的所有数据。因此一般而言，写一个高性能服务器的协议解析部分，会以状态机的方式来实现，即定义了协议数据的每个部分，如下伪代码所示：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 定义协议头数据&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;&lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;header_t&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 协议版本号&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;int&lt;/span&gt; version;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 定义body部分大小&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;int&lt;/span&gt; size;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;header_t&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 定义协议数据&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;&lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;protocol_t&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;header_t&lt;/span&gt; header;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;char&lt;/span&gt; body[0];&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} protocol;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 定义接收数据的状态机类型&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;&lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;enum&lt;/span&gt; &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;state_t&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  RECV_HEADER,        &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 接收包头&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  RECV_BODY,          &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 接收包体&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  PROCESS_PROTOCOL,   &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 处理协议&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  SEND_RESPONSE       &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 发送回复&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;};&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 处理请求的状态机&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;&lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;statemachine&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;switch&lt;/span&gt; (state) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;case&lt;/span&gt; RECV_HEADER:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 接收协议包头数据&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 接收完毕之后，切换state到RECV_BODY&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;case&lt;/span&gt; RECV_BODY:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 接收协议包体数据&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 接收完毕之后，切换state到PROCESS_PROTOCOL&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;case&lt;/span&gt; PROCESS_PROTOCOL:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 处理协议&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 处理完毕之后，切换state到SEND_RESPONSE&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;case&lt;/span&gt; SEND_RESPONSE:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 发送应答&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如上面的伪代码所示，接收一个请求之后，会初始化一个变量state用于保存当前协议处理的状态类型，假如第一次接收数据时还不能接收完毕协议的数据，就将接收fd重新放入到事件派发器中，下一次被唤醒之后再根据当前的状态继续接收数据进行处理。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Nginx源码阅读笔记-事件处理模块</title>
      <link>https://www.codedump.info/zh/post/20190131-nginx-event/</link>
      <pubDate>Thu, 31 Jan 2019 21:12:01 +0800</pubDate>
      <guid>https://www.codedump.info/zh/post/20190131-nginx-event/</guid>
      <description>&lt;p&gt;大概做高性能服务器的，都绕不开事件处理模块来，一般一个事件模块，会分为以下几部分：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;如何定义一个描述事件的数据结构。&lt;/li&gt;&#xA;&lt;li&gt;如何在事件模块中支持定时器。&lt;/li&gt;&#xA;&lt;li&gt;如果需要支持多平台，事件模块需要考虑如何统一以及区分各平台的具体实现。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;下面就这三部分展开Nginx事件处理模块的分析。&lt;/p&gt;&#xA;&lt;h1 class=&#34;heading&#34; id=&#34;ngx_event_t&#34;&gt;&#xA;  ngx_event_t&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#ngx_event_t&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;描述事件的数据结构，一般至少需要以下几部分数据：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;用于保存用户相关的数据。&lt;/li&gt;&#xA;&lt;li&gt;用于保存事件触发之后的回调函数。&lt;/li&gt;&#xA;&lt;li&gt;用于表示事件状态、类型的数据。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;nginx中，描述事件采用的数据结构是ngx_event_t中，其内部成员就是按照前面的三部分来划分了。&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;void *data：事件相关的数据。&lt;/li&gt;&#xA;&lt;li&gt;ngx_event_handler_pt handler：事件被触发时的回调函数。&lt;/li&gt;&#xA;&lt;li&gt;第三类数据，ngx_event_t中划分的比较仔细：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;unsigned write:1：可写标志位&lt;/li&gt;&#xA;&lt;li&gt;unsigned active:1：活跃标志位&lt;/li&gt;&#xA;&lt;li&gt;unsigned disabled:1：禁用标志位&lt;/li&gt;&#xA;&lt;li&gt;unsigned eof:1：为1表示字节流已经结束&lt;/li&gt;&#xA;&lt;li&gt;unsigned error:1：处理事件出错&lt;/li&gt;&#xA;&lt;li&gt;unsigned timedout:1：事件超时&lt;/li&gt;&#xA;&lt;li&gt;unsigned timer_set:1：为1表示这是一个超时事件&lt;/li&gt;&#xA;&lt;li&gt;unsigned deferred_accept:1：为1表示需要延迟接收TCP连接&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;除了以上三部分，还有其他一些重要的数据：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;ngx_rbtree_node_t timer：红黑树节点，用于实现定时器的，下面讨论定时器再展开。&lt;/li&gt;&#xA;&lt;li&gt;ngx_queue_t queue：延迟队列，如果事件不在轮询循环中直接处理，而是之后被处理，就放在这个队列中。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;总体来看，event这个结构体为了涵盖所有可能的事件，做的大而全，不只是用来描述一般的IO事件，还包括了定时器事件，还包括了接收连接相关的数据。&lt;/p&gt;&#xA;&lt;h1 class=&#34;heading&#34; id=&#34;定时器的实现&#34;&gt;&#xA;  定时器的实现&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e5%ae%9a%e6%97%b6%e5%99%a8%e7%9a%84%e5%ae%9e%e7%8e%b0&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;Nginx内部使用红黑树来实现定时器，目的在于能够快速的查询到哪些定时器超时了。不同的事件结构中，这部分实现采用的数据结构不一样，libevent、libuv采用的是最小堆，redis比较挫，这部分采用的是链表。&lt;/p&gt;&#xA;&lt;p&gt;在一个事件循环中，因为既要考虑到一般的IO事件，又要考虑到定时器事件，所以都会以一个最近被触发的定时器来做为查询IO事件被触发的时间，即以下的伪代码：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;&#34;&gt;查询最近将被触发的定时器超时时间返回&lt;/span&gt;t&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;&#34;&gt;将&lt;/span&gt;t做为epoll_wait之类的查询IO事件的超时时间&lt;span style=&#34;&#34;&gt;，即最长等待&lt;/span&gt;t时间看有没有IO事件被触发&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;&#34;&gt;遍历定时器，查询已经超时的定时器进行回调处理&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;从这里可以看出，“迅速查询到距离当前最近被触发的定时器时间”以及“迅速查询到当前哪些定时器超时”，是这个定时器模块速度的关键。&lt;/p&gt;&#xA;&lt;p&gt;由于红黑树、最小堆这种平衡数据结构，每次查询都排除掉当前一半的元素，可以做到时间复杂度O(logn)，所以就常用来实现定时器了。&lt;/p&gt;&#xA;&lt;h1 class=&#34;heading&#34; id=&#34;事件模块的实现&#34;&gt;&#xA;  事件模块的实现&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e4%ba%8b%e4%bb%b6%e6%a8%a1%e5%9d%97%e7%9a%84%e5%ae%9e%e7%8e%b0&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;由于nginx需要跑在多个平台下面，而不同平台使用的事件机制又不一样，比如linux是epoll，bsd是kqueue等，需要实现事件模块的时候，既需要统一事件模块的共性部分，又需要区分不同平台的差异部分。&lt;/p&gt;&#xA;&lt;p&gt;这看上去又是一个面向对象的设计问题了：基类负责实现共性的部分，子类具体再来实现各平台相关的部分。&lt;/p&gt;&#xA;&lt;p&gt;前面&lt;a href=&#34;http://localhost:1313/post/20190123-libuv/#%E6%80%BB%E7%BB%93&#34;&gt;分析libuv&lt;/a&gt;的时候提到过，libuv多使用宏来模拟C++中的继承，不是很认可这个代码风格，来看看nginx类似场景的实现。&lt;/p&gt;&#xA;&lt;p&gt;nginx中，将事件相关的操作函数统一放在结构体ngx_event_actions_t中，可以把这部分类比于子类需要实现的函数接口：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_int_t&lt;/span&gt;  (*add)(&lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_event_t&lt;/span&gt; *ev, &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_int_t&lt;/span&gt; event, &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_uint_t&lt;/span&gt; flags);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_int_t&lt;/span&gt;  (*del)(&lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_event_t&lt;/span&gt; *ev, &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_int_t&lt;/span&gt; event, &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_uint_t&lt;/span&gt; flags);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_int_t&lt;/span&gt;  (*enable)(&lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_event_t&lt;/span&gt; *ev, &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_int_t&lt;/span&gt; event, &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_uint_t&lt;/span&gt; flags);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_int_t&lt;/span&gt;  (*disable)(&lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_event_t&lt;/span&gt; *ev, &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_int_t&lt;/span&gt; event, &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_uint_t&lt;/span&gt; flags);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_int_t&lt;/span&gt;  (*add_conn)(&lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_connection_t&lt;/span&gt; *c);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_int_t&lt;/span&gt;  (*del_conn)(&lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_connection_t&lt;/span&gt; *c, &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_uint_t&lt;/span&gt; flags);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_int_t&lt;/span&gt;  (*notify)(ngx_event_handler_pt handler);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_int_t&lt;/span&gt;  (*process_events)(&lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_cycle_t&lt;/span&gt; *cycle, &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_msec_t&lt;/span&gt; timer,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_uint_t&lt;/span&gt; flags);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_int_t&lt;/span&gt;  (*init)(&lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_cycle_t&lt;/span&gt; *cycle, &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_msec_t&lt;/span&gt; timer);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;void&lt;/span&gt;       (*done)(&lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_cycle_t&lt;/span&gt; *cycle);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;font-weight:bold;text-decoration:underline&#34;&gt;ngx_event_actions_t&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;前面在分析到nginx如何解析配置的时候提到过，nginx中的配置是分层次的，event模块做为一个顶层的core模块，内部又有子模块，而这里的事件模块就是event模块中的子模块：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Nginx源码阅读笔记-Master Woker进程模型</title>
      <link>https://www.codedump.info/zh/post/20190131-nginx-master-worker/</link>
      <pubDate>Thu, 31 Jan 2019 09:53:57 +0800</pubDate>
      <guid>https://www.codedump.info/zh/post/20190131-nginx-master-worker/</guid>
      <description>&lt;h1 class=&#34;heading&#34; id=&#34;master进程&#34;&gt;&#xA;  master进程&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#master%e8%bf%9b%e7%a8%8b&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;Nginx采用的模型是master-worker模型，即：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;由master进程负责创建worker进程，以及监控worker进程的情况，如需要更新配置的情况下发消息给worker进程重新加载配置等。&lt;/li&gt;&#xA;&lt;li&gt;master进程负责具体网络事件。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;将里面核心的流程和函数抽取出来，如下图所示：&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;master-worker&#34; src=&#34;https://www.codedump.info/media/imgs/20190131-nginx-master-worker/master-worker.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;caption-container&#34;&gt;&#xA;        &lt;figcaption&gt; master-worker &lt;/figcaption&gt;&#xA;    &lt;/div&gt;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;master进程的主循环在函数ngx_master_process_cycle中，主要负责：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;调用ngx_start_worker_processes函数创建worker子进程。&lt;/li&gt;&#xA;&lt;li&gt;监控各种信号量的变化做处理，比如需要停止进程、重新加载配置等。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;master进程最终会调用函数ngx_spawn_process函数来创建出worker子进程：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;使用共享内存创建出用于master-worker进程之间通信的channel。&lt;/li&gt;&#xA;&lt;li&gt;fork出子进程之后，进入worker进程的主函数ngx_worker_process_cycle。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;以下列举出几个相关的信号量：&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;信号&lt;/th&gt;&#xA;          &lt;th&gt;对应全局变量&lt;/th&gt;&#xA;          &lt;th&gt;处理&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;QUIT&lt;/td&gt;&#xA;          &lt;td&gt;ngx_quit&lt;/td&gt;&#xA;          &lt;td&gt;优雅关闭整个Nginx服务&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;TERM或者INT&lt;/td&gt;&#xA;          &lt;td&gt;ngx_terminate&lt;/td&gt;&#xA;          &lt;td&gt;强制关闭Nginx服务&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;USR1&lt;/td&gt;&#xA;          &lt;td&gt;ngx_reopen&lt;/td&gt;&#xA;          &lt;td&gt;重新打开文件&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;WINCH&lt;/td&gt;&#xA;          &lt;td&gt;ngx_noaccept&lt;/td&gt;&#xA;          &lt;td&gt;所有worker进程不再接受新的连接，相当于给子进程发送QUIT信号&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;USR2&lt;/td&gt;&#xA;          &lt;td&gt;ngx_change_binary&lt;/td&gt;&#xA;          &lt;td&gt;平滑升级到新的Nginx二进制文件&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;HUP&lt;/td&gt;&#xA;          &lt;td&gt;ng_reconfigure&lt;/td&gt;&#xA;          &lt;td&gt;重新加载配置文件&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;CHLD&lt;/td&gt;&#xA;          &lt;td&gt;ngx_reap&lt;/td&gt;&#xA;          &lt;td&gt;需要监控所有子进程&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h1 class=&#34;heading&#34; id=&#34;worker进程&#34;&gt;&#xA;  worker进程&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#worker%e8%bf%9b%e7%a8%8b&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;worker进程的函数入口在ngx_worker_process_cycle中，其主要做的工作分为两部分：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;调用ngx_process_events_and_timers处理IO事件以及定时器事件。&lt;/li&gt;&#xA;&lt;li&gt;判断ngx_terminate、ngx_quit、ngx_reopen这几个变量是否被置位来做相应的处理。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;下面主要谈网络IO事件的处理，即ngx_process_events_and_timers函数。&lt;/p&gt;&#xA;&lt;p&gt;先来介绍几个与接收连接相关的全局变量：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;ngx_use_accept_mutex：由配置项accept_mutex配置，表示是否需要使用accept锁，只有抢到该锁的worker才允许接收新的连接。&lt;/li&gt;&#xA;&lt;li&gt;ngx_accept_mutex_delay：由配置项accept_mutex_delay配置，在开启accept_mutex的情况下，一个worker进程在抢不到accept锁的情况下，最长多少时间才重新接收新的连接。&lt;/li&gt;&#xA;&lt;li&gt;ngx_accept_disabled：值为ngx_cycle-&amp;gt;connection_n / 8 - ngx_cycle-&amp;gt;free_connection_n，可以看到当链接数量到nginx.conf中配置的worker_connections的7/8以上时，这个变量ngx_accept_disabled为正数，此时不会接收新的连接，直到该值小于等于0为止。&lt;/li&gt;&#xA;&lt;li&gt;ngx_accept_mutex_held：表示是否抢到了accept锁，只有抢到的才能接收新连接。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;具体来看看ngx_process_events_and_timers函数中与接收连接相关的逻辑，伪代码如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;&#34;&gt;如果开启了&lt;/span&gt;accept_mutex配置&lt;span style=&#34;&#34;&gt;：&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;&#34;&gt;如果当前&lt;/span&gt;ngx_accept_disabled大于0&lt;span style=&#34;&#34;&gt;，表示不能接收新的连接，直接返回。&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;&#34;&gt;否则尝试获取&lt;/span&gt;accept mutex&lt;span style=&#34;&#34;&gt;。&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;&#34;&gt;如果获取&lt;/span&gt;accept mutex锁成功&lt;span style=&#34;&#34;&gt;：&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;&#34;&gt;将调用事件轮询函数的标志位加上&lt;/span&gt;NGX_POST_EVENTS标志&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;&#34;&gt;如果获取失败：&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;&#34;&gt;调用事件轮询函数的事件参数不得超过&lt;/span&gt;ngx_accept_mutex_delay值&lt;span style=&#34;&#34;&gt;。&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;&#34;&gt;调用&lt;/span&gt;ngx_process_events函数处理轮询事件&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;&#34;&gt;调用&lt;/span&gt;ngx_event_process_posted(cycle, &amp;amp;ngx_posted_accept_events)&lt;span style=&#34;&#34;&gt;函数处理&lt;/span&gt;accept事件&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;&#34;&gt;如果前面拿到了&lt;/span&gt;accept mutex锁&lt;span style=&#34;&#34;&gt;，则释放这个锁，好让其他&lt;/span&gt;worker也有机会接收新的连接&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;&#34;&gt;调用&lt;/span&gt;ngx_event_expire_timers处理定时器事件&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;&#34;&gt;调用&lt;/span&gt;ngx_event_process_posted(cycle, &amp;amp;ngx_posted_events);&lt;span style=&#34;&#34;&gt;函数处理除了&lt;/span&gt;accept事件以外的其他post事件&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在ngx_process_events处理函数中，当传入的flags有NGX_POST_EVENTS标志时，意味着并不马上在这个函数中调用事件的回调函数进行处理，而是放在一个队列中，回头在后面的ngx_event_process_posted函数中再进行处理。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Nginx源码阅读笔记-配置解析流程</title>
      <link>https://www.codedump.info/zh/post/20190103-nginx-config-parse/</link>
      <pubDate>Thu, 03 Jan 2019 08:41:44 +0800</pubDate>
      <guid>https://www.codedump.info/zh/post/20190103-nginx-config-parse/</guid>
      <description>&lt;p&gt;本系列文章基于openresty-1.13.6.1版本的代码做的笔记，其对应的nginx源码版本是nginx-1.13.6。&lt;/p&gt;&#xA;&lt;h1 class=&#34;heading&#34; id=&#34;模块与配置值解析相关数据结构&#34;&gt;&#xA;  模块与配置值解析相关数据结构&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e6%a8%a1%e5%9d%97%e4%b8%8e%e9%85%8d%e7%bd%ae%e5%80%bc%e8%a7%a3%e6%9e%90%e7%9b%b8%e5%85%b3%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;整个Nginx是以模块的方式来组织的，即使是核心的组件如epoll之类的，最终也是以模块的方式注册到nginx中的。所以先了解整个nginx模块的结构很有必要。&lt;/p&gt;&#xA;&lt;p&gt;与模块相关的核心数据结构有以下这几个。&lt;/p&gt;&#xA;&lt;p&gt;ngx_module_t结构体用于定义nginx模块相关的数据结构，其中包括几个核心的数据：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;void *ctx：用于存储每个模块相关的context数据。&lt;/li&gt;&#xA;&lt;li&gt;ngx_command_t *commands：用于存储与该模块相关的配置命令解析数据。所谓的配置命令，就是对应的nginx配置文件中的语句，如”event“、”include“等，每个配置语句最终一定有一个相关的ngx_command_t数据与之对应，负责解析这个命令。&lt;/li&gt;&#xA;&lt;li&gt;ngx_uint_t type：用于保存模块的类型，目前包括NGX_HTTP_MODULE，NGX_CORE_MODULE，NGX_CONF_MOULE，NGX_EVENT_MODULE，NGX_MAIL_MODULE这几种类型。&lt;/li&gt;&#xA;&lt;li&gt;一组回调函数：用于在解析配置的时候进行回调。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;而上面的ngx_command_t结构体又有以下成员：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;ngx_str_t name：配置文件里对应的配置项名称。如前面提到的nginx配置文件中的”event“、”include“等。&lt;/li&gt;&#xA;&lt;li&gt;ngx_uint_t type：配置项类型，这里会存储如该配置项应该出现在什么位置（http块、server块、location块等），以及配置项参数数量，以便于解析过程中进行合法性的判断。&#xA;&lt;ul&gt;&#xA;&lt;li&gt;命令的作用域，即该命令能够出现在什么位置（http块、server块、location块等），这些与作用域相关的类型有NGX_MAIN_CONF、NGX_EVENT_CONF、NGX_HTTP_MAIN_CONF、NGX_HTTP_SRV_CONF、 NGX_HTTP_LOC_CONF、NGX_MAIL_MAIN_CONF、NGX_MAIL_SRV_CONF 等。&lt;/li&gt;&#xA;&lt;li&gt;命令能够接受的参数数量，如NGX_CONF_NOARGS、NGX_CONF_TAKE1等。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;ngx_uint_t offset：该配置命令所要修改的配置项在该模块配置结构体中的偏移量。&lt;/li&gt;&#xA;&lt;li&gt;ngx_uint_t conf：该配置在子模块配置项中的索引。&lt;/li&gt;&#xA;&lt;li&gt;回调函数set：在解析到配置项的时候进行回调。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;下图中给出一个简单的nginx配置文件的作用域示意图：&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;ngx-conf-scope&#34; src=&#34;https://www.codedump.info/media/imgs/20190103-nginx-config-parse/ngx-conf-scope.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;caption-container&#34;&gt;&#xA;        &lt;figcaption&gt; ngx-conf-scope &lt;/figcaption&gt;&#xA;    &lt;/div&gt;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;有了以上两个核心数据结构，可以知道每个nginx模块注册时的方式：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;定义一组与本模块相关的ngx_command_t，用于定义本模块相关的配置项信息。&lt;/li&gt;&#xA;&lt;li&gt;定义一个与本模块相关的数据结构，注册为ngx_module_t的ctx指针，用于保存本模块相关的数据结构。&lt;/li&gt;&#xA;&lt;li&gt;最后，将上面的数据放到ngx_module_t中，nginx解析配置的时候会自动回调对应的处理函数了。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;以epoll模块来说，其ngx_module_t结构体是如下组织的。&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;epoll-module&#34; src=&#34;https://www.codedump.info/media/imgs/20190103-nginx-config-parse/epoll-module.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;caption-container&#34;&gt;&#xA;        &lt;figcaption&gt; epoll-module &lt;/figcaption&gt;&#xA;    &lt;/div&gt;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;根据上面的图示，不难想象，nginx在配置解析的时候是如何解析epoll相关的配置的：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;首先解析到event模块，也就是nginx配置文件中的event{}部分，此时会把对用的context指针指向ngx_epoll_module_ctx，开始进行event模块的解析工作。&lt;/li&gt;&#xA;&lt;li&gt;如果在event块中遇到名为“epoll_events”或者“worker_aio_requests”开始的配置，那么就知道是上面ngx_epoll_commands数组中定义的配置命令，nginx首先会根据这里定义的type来分析其出现的位置（是否出现在event块）以及参数数量（NGX_CONF_TAKE1）是否正确，都检测通过之后，才会调用ngx_command_t中set回调函数进行配置解析。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h1 class=&#34;heading&#34; id=&#34;解析配置流程&#34;&gt;&#xA;  解析配置流程&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e8%a7%a3%e6%9e%90%e9%85%8d%e7%bd%ae%e6%b5%81%e7%a8%8b&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;相关核心函数&#34;&gt;&#xA;  相关核心函数&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e7%9b%b8%e5%85%b3%e6%a0%b8%e5%bf%83%e5%87%bd%e6%95%b0&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;上面分析到ngx_module_t结构体的时候曾经提到过，当前的type有如下类型：NGX_HTTP_MODULE，NGX_CORE_MODULE，NGX_CONF_MOULE，NGX_EVENT_MODULE，NGX_MAIL_MODULE。&lt;/p&gt;&#xA;&lt;p&gt;实际上，nginx的模块类型是一个树形结构，最顶层的是NGX_CORE_MODULE，它下面的子类型是NGX_HTTP_MODULE、NGX_EVENT_MODULE等。&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;module-tree&#34; src=&#34;https://www.codedump.info/media/imgs/20190103-nginx-config-parse/module-tree.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;caption-container&#34;&gt;&#xA;        &lt;figcaption&gt; module-tree &lt;/figcaption&gt;&#xA;    &lt;/div&gt;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;如果一个结构体是使用树形结构来进行组织的，那么首先会想到的是“递归算法”。&lt;/p&gt;&#xA;&lt;p&gt;实际上nginx配置解析，确实也是以递归的方式来进行解析的。仍然是以前面的epoll模块解析为例来说明这个流程：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;首先解析类型为NGX_CORE_MODULE的顶层模块，由于event模块（即event{}配置块）也是NGX_CORE_MODULE，因此解析到event{}块时会进入event模块相关的配置解析中。&lt;/li&gt;&#xA;&lt;li&gt;下面开始进入NGX_EVENT_MODULE这个二层模块的解析中，当遇到epoll相关的指令时，进入epoll模块的解析。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;上面可以看到，既然是递归方式来解析的，那么意味着解析配置使用的解析函数是可以被递归调用的，在nginx中这个会被递归调用的核心函数是core/ngx_conf_file.c中的ngx_conf_parse函数。&lt;/p&gt;&#xA;&lt;p&gt;在ngx_conf_parse中，会根据传入的filename来区分几种情况：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;filename不为空：说明是首次调用该函数，此时会打开filename指定的文件名开始解析。&lt;/li&gt;&#xA;&lt;li&gt;cf-&amp;gt;conf_file-&amp;gt;file.fd != NGX_INVALID_FILE：说明此时是被递归调用的情况，用于分析一个{}block的内容。&lt;/li&gt;&#xA;&lt;li&gt;如果以上情况都不是，说明也是被递归调用的情况，而这时是分析一个参数的情况。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;另外，既然ngx_conf_parse会被递归调用，每次传入的参数就都是一个类型的，被递归调用的时候就需要相应的做修改。&lt;/p&gt;&#xA;&lt;p&gt;ngx_conf_parse的函数原型如下：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;char*&#xA;ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename);&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;可以看到其中有两个入参，其中之一的filename前面已经介绍过了，下面来介绍ngx_conf_t结构体。这个结构体可以认为是解析配置文件过程中为了保存数据的中间数据结构，其重要的几个成员是：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;ngx_uint_t module_type：模块类型，即前面提到的NGX_HTTP_MODULE，NGX_CORE_MODULE，NGX_CONF_MOULE，NGX_EVENT_MODULE，NGX_MAIL_MODULE。&lt;/li&gt;&#xA;&lt;li&gt;ngx_uint_t cmd_type：命令类型，表示指令的作用域。有NGX_MAIN_CONF、NGX_EVENT_CONF、NGX_HTTP_MAIN_CONF、NGX_HTTP_SRV_CONF、 NGX_HTTP_LOC_CONF、NGX_MAIL_MAIN_CONF、NGX_MAIL_SRV_CONF 等。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;因此，在每次递归调用ngx_conf_parse函数之前，调用方都要相应的设置ngx_conf_t结构体这两个成员，好让ngx_conf_parse函数知道当前解析的是哪个模块、在哪个配置作用域中。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
