<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>GDB - 她和她的猫</title>
    <link>https://her-cat.com/tags/gdb/</link>
    <description>GDB的文章列表 - 她和她的猫</description>
    <image>
      <title>她和她的猫</title>
      <url>https://her-cat.com/assets/favorite.jpeg</url>
      <link>https://her-cat.com/assets/favorite.jpeg</link>
    </image>
    <generator>Hugo -- 0.148.1</generator>
    <language>zh</language>
    <lastBuildDate>Wed, 15 Oct 2025 23:26:52 +0800</lastBuildDate>
    <atom:link href="https://her-cat.com/tags/gdb/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Yar 源码阅读笔记：客户端的并行调用</title>
      <link>https://her-cat.com/posts/2022/01/14/yar-internals-client-concurrent-call/</link>
      <pubDate>Fri, 14 Jan 2022 19:50:08 +0800</pubDate>
      <guid>https://her-cat.com/posts/2022/01/14/yar-internals-client-concurrent-call/</guid>
      <description>主要介绍实现并行调用相关的数据结构，并通过 GDB 调试代码的方式展示并行调用运行的过程。</description>
      <content:encoded><![CDATA[<h2 id="前言">前言</h2>
<p>在上一篇文章中，介绍了客户端同步调用的具体实现的，主要还是通过调用传输模块的相关函数，完成发送和接收远程调用的数据。</p>
<p>在调用多个远程方法时，同步调用是以串行的方式执行的，导致运行效率比较低，所以需要使用并行调用来提高调用多个远程方法的运行效率，减少整体运行的时间。</p>
<h2 id="并行传输模块">并行传输模块</h2>
<h3 id="yar_transport_multi_t">yar_transport_multi_t</h3>
<p>在数据传输模块中，我们提到了 yar_transport_multi_t *multi 就是用来实现并行调用的，先来看看该结构体的定义。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// source：yar_transport.h
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 并行传输器的工厂
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="n">_yar_transport_multi</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">_yar_transport_multi_interface</span> <span class="o">*</span> <span class="p">(</span><span class="o">*</span><span class="n">init</span><span class="p">)();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="kt">yar_transport_multi_t</span><span class="p">;</span>
</span></span></code></pre></div><p>可以将 yar_transport_multi_t 看作是并行传输器的工厂，使用 init 函数创建并行传输器的实例，也就是为 _yar_transport_multi_interface 结构体分配内存，并初始化相关配置。</p>
<h3 id="yar_transport_multi_interface_t">yar_transport_multi_interface_t</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// source：yar_transport.h
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 并行传输器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="n">_yar_transport_multi_interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="o">*</span><span class="n">data</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="p">(</span><span class="o">*</span><span class="n">add</span><span class="p">)(</span><span class="k">struct</span> <span class="n">_yar_transport_multi_interface</span> <span class="o">*</span><span class="n">self</span><span class="p">,</span>     <span class="kt">yar_transport_interface_t</span> <span class="o">*</span><span class="n">cp</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="p">(</span><span class="o">*</span><span class="n">exec</span><span class="p">)(</span><span class="k">struct</span> <span class="n">_yar_transport_multi_interface</span> <span class="o">*</span><span class="n">self</span><span class="p">,</span>     <span class="n">yar_concurrent_client_callback</span> <span class="o">*</span><span class="n">callback</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">close</span><span class="p">)(</span><span class="k">struct</span> <span class="n">_yar_transport_multi_interface</span> <span class="o">*</span><span class="n">self</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="kt">yar_transport_multi_interface_t</span><span class="p">;</span>
</span></span></code></pre></div><p>其实 yar_transport_multi_interface_t 结构体和 yar_transport_interface_t 结构体类似，都是用来定义在传输时使用的函数，下面是字段说明：</p>
<ul>
<li>data：存储并行传输器的数据指针，指向 yar_curl_multi_data_t 结构体。</li>
<li>add：函数指针，将一个同步传输器（yar_transport_interface_t）实例中的数据存储到并行传输器中。</li>
<li>exec：函数指针，执行请求，将所有的同步传输器实例中的数据发送出去。</li>
<li>close：函数指针，释放相关资源。</li>
</ul>
<p>从上面的说明可以看出来，并行传输器实际上是在管理多个同步传输器实例，先调用 add 函数将同步传输器实例存储到并行传输器中，然后通过 exec 函数将这些同步传输器实例中的数据发送出去，并对响应结果进行处理，最后调用 close 函数清理相关资源。</p>
<h3 id="yar_curl_multi_data_t">yar_curl_multi_data_t</h3>
<p>虽然 yar_curl_multi_data_t 结构体的字段比较少，但是通过这个结构体，可以知道并行传输器是如何存储同步传输器实例的数据的。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// source：transports/curl.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">_yar_curl_multi_data_t</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">CURLM</span> <span class="o">*</span><span class="n">cm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">yar_transport_interface_t</span> <span class="o">*</span><span class="n">chs</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="kt">yar_curl_multi_data_t</span><span class="p">;</span>
</span></span></code></pre></div><p>cm 字段用来存储 curl 批处理实例的指针，chs 则是同步传输器的指针，同步传输器中 data 字段指向的结构体中有一个 next 字段，用来指向下一个同步传输器：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// source：transports/curl.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">_yar_curl_data_t</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">yar_transport_interface_t</span> <span class="o">*</span><span class="n">next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="kt">yar_curl_data_t</span><span class="p">;</span>
</span></span></code></pre></div><p>看到这里你应该就明白了，Yar 通过单链表的结构将所有的同步传输器实例串起来， yar_curl_multi_data_t 中的 chs 字段相当于头指针。</p>
<p><img decoding="async" height="386" loading="lazy" sizes="(max-width: 768px) 100vw, (max-width: 1024px) 768px, 1024px" src="/posts/2022/01/14/yar-internals-client-concurrent-call/yar-concurrent-call-data.png" srcset="/posts/2022/01/14/yar-internals-client-concurrent-call/yar-concurrent-call-data_hu_95be10b6b4e6abfa.png 384w, /posts/2022/01/14/yar-internals-client-concurrent-call/yar-concurrent-call-data_hu_347ecb3b272b64de.png 768w, /posts/2022/01/14/yar-internals-client-concurrent-call/yar-concurrent-call-data_hu_be303216934272d.png 1024w" style="max-width: 100%; height: auto; aspect-ratio: 3.5104;" width="1355"></p>
<h2 id="并行客户端">并行客户端</h2>
<p>先来看看并行客户端的方法列表及属性。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// source：yar_client.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 方法列表
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">zend_function_entry</span> <span class="n">yar_concurrent_client_methods</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">PHP_ME</span><span class="p">(</span><span class="n">yar_concurrent_client</span><span class="p">,</span> <span class="n">call</span><span class="p">,</span> <span class="n">arginfo_client_async</span><span class="p">,</span> <span class="n">ZEND_ACC_PUBLIC</span><span class="o">|</span><span class="n">ZEND_ACC_STATIC</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nf">PHP_ME</span><span class="p">(</span><span class="n">yar_concurrent_client</span><span class="p">,</span> <span class="n">loop</span><span class="p">,</span> <span class="n">arginfo_client_loop</span><span class="p">,</span> <span class="n">ZEND_ACC_PUBLIC</span><span class="o">|</span><span class="n">ZEND_ACC_STATIC</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nf">PHP_ME</span><span class="p">(</span><span class="n">yar_concurrent_client</span><span class="p">,</span> <span class="n">reset</span><span class="p">,</span><span class="n">arginfo_client_void</span><span class="p">,</span> <span class="n">ZEND_ACC_PUBLIC</span><span class="o">|</span><span class="n">ZEND_ACC_STATIC</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">PHP_FE_END</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">INIT_CLASS_ENTRY</span><span class="p">(</span><span class="n">ce</span><span class="p">,</span> <span class="s">&#34;Yar_Concurrent_Client&#34;</span><span class="p">,</span> <span class="n">yar_concurrent_client_methods</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">yar_concurrent_client_ce</span> <span class="o">=</span> <span class="nf">zend_register_internal_class</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ce</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 声明并行客户端的属性
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">zend_declare_property_null</span><span class="p">(</span><span class="n">yar_concurrent_client_ce</span><span class="p">,</span> <span class="nf">ZEND_STRL</span><span class="p">(</span><span class="s">&#34;_callstack&#34;</span><span class="p">),</span> <span class="n">ZEND_ACC_PROTECTED</span><span class="o">|</span><span class="n">ZEND_ACC_STATIC</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">zend_declare_property_null</span><span class="p">(</span><span class="n">yar_concurrent_client_ce</span><span class="p">,</span> <span class="nf">ZEND_STRL</span><span class="p">(</span><span class="s">&#34;_callback&#34;</span><span class="p">),</span> <span class="n">ZEND_ACC_PROTECTED</span><span class="o">|</span><span class="n">ZEND_ACC_STATIC</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">zend_declare_property_null</span><span class="p">(</span><span class="n">yar_concurrent_client_ce</span><span class="p">,</span> <span class="nf">ZEND_STRL</span><span class="p">(</span><span class="s">&#34;_error_callback&#34;</span><span class="p">),</span> <span class="n">ZEND_ACC_PROTECTED</span><span class="o">|</span><span class="n">ZEND_ACC_STATIC</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">zend_declare_property_bool</span><span class="p">(</span><span class="n">yar_concurrent_client_ce</span><span class="p">,</span> <span class="nf">ZEND_STRL</span><span class="p">(</span><span class="s">&#34;_start&#34;</span><span class="p">),</span> <span class="mi">0</span><span class="p">,</span> <span class="n">ZEND_ACC_PROTECTED</span><span class="o">|</span><span class="n">ZEND_ACC_STATIC</span><span class="p">);</span>
</span></span></code></pre></div><p>由此我们可以知道并行客户端大概长这样：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">class</span> <span class="err">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">protected</span> <span class="k">static</span> <span class="nv">$_callstack</span> <span class="o">=</span> <span class="k">null</span><span class="p">;</span> <span class="c1">// 存储每个调用的数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">protected</span> <span class="k">static</span> <span class="nv">$_callback</span> <span class="o">=</span> <span class="k">null</span><span class="p">;</span> <span class="c1">// 并行客户端的回调函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">protected</span> <span class="k">static</span> <span class="nv">$_error_callback</span> <span class="o">=</span> <span class="k">null</span><span class="p">;</span> <span class="c1">// 并行客户端的异常回调函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">protected</span> <span class="k">static</span> <span class="nv">$_start</span> <span class="o">=</span> <span class="k">false</span><span class="p">;</span> <span class="c1">// 是否已启动
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    
</span></span><span class="line"><span class="cl">    <span class="c1">// 调用某个方法
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">public</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">call</span><span class="p">(</span><span class="nv">$uri</span><span class="p">,</span> <span class="nv">$method</span><span class="p">,</span> <span class="nv">$parameters</span> <span class="o">=</span> <span class="k">null</span><span class="p">,</span> <span class="nv">$callback</span> <span class="o">=</span> <span class="k">null</span><span class="p">,</span> <span class="nv">$error_callback</span> <span class="o">=</span> <span class="k">null</span><span class="p">,</span> <span class="nv">$options</span> <span class="o">=</span> <span class="k">array</span><span class="p">())</span> 
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 执行调用并等待响应
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">public</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">loop</span><span class="p">(</span><span class="nv">$callback</span> <span class="o">=</span> <span class="k">null</span><span class="p">,</span> <span class="nv">$error_callback</span> <span class="o">=</span> <span class="k">null</span><span class="p">)</span> 
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 重置并行客户端
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">public</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">reset</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Yar 定义了 yar_call_data_t 结构体将 call 方法的参数存起来，当作远程调用的数据。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// source：yar_transport.h
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">_yar_call_data</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">zend_long</span> <span class="n">sequence</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">zend_string</span> <span class="o">*</span><span class="n">uri</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">zend_string</span> <span class="o">*</span><span class="n">method</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">zval</span> <span class="n">callback</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">zval</span> <span class="n">ecallback</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">zval</span> <span class="n">parameters</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">zval</span> <span class="n">options</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="kt">yar_call_data_t</span><span class="p">;</span>
</span></span></code></pre></div><p>在 PHP 中使用该结构体前，需要在 Yar 启动时向 PHP 注册该资源类型，同时传入析构函数，用于 PHP 释放该资源的时候调用。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="nf">YAR_STARTUP_FUNCTION</span><span class="p">(</span><span class="n">transport</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ... 
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">    <span class="n">le_calldata</span> <span class="o">=</span> <span class="nf">zend_register_list_destructors_ex</span><span class="p">(</span><span class="n">php_yar_calldata_dtor</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="s">&#34;Yar Call Data&#34;</span><span class="p">,</span> <span class="n">module_number</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">SUCCESS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>zend_register_list_destructors_ex 函数会返回一个整数，表示注册的资源类型，在取回资源时需要用到该整数。</p>
<h2 id="并行调用">并行调用</h2>
<p>这次我们使用 gdb 调试的方式，一步步的展示并行调用的过程，在此之前需要编译好 Yar，编译时记得关闭编译优化，并在 php.ini 中配置好扩展，然后启动开篇中的 <a href="/posts/2021/10/17/yar-internals-start/#Yar-%E6%9C%8D%E5%8A%A1%E7%AB%AF">Yar 服务端示例</a>。</p>
<h3 id="yar_concurrent_clientcall">Yar_Concurrent_Client::call</h3>
<p>在<a href="/posts/2021/10/17/yar-internals-start/#%E5%B9%B6%E8%A1%8C%E8%B0%83%E7%94%A8">客户端的并行调用示例</a>中，先调用了 call 方法，然后调用了 loop 方法，所以我们分别在这两个方法的上打断点。通过上面并行客户端的介绍可以知道，实现 call 方法的函数是 <strong>zim_yar_concurrent_client_call</strong>，我们在该函数上打断点即可。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="err">$</span> <span class="n">gdb</span> <span class="nf">php</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="n">gdb</span><span class="p">)</span> <span class="n">b</span> <span class="n">zim_yar_concurrent_client_call</span>
</span></span><span class="line"><span class="cl"><span class="n">Function</span> <span class="s">&#34;zim_yar_concurrent_client_call&#34;</span> <span class="n">not</span> <span class="n">defined</span><span class="p">.</span>
</span></span><span class="line"><span class="cl"><span class="n">Make</span> <span class="n">breakpoint</span> <span class="n">pending</span> <span class="n">on</span> <span class="n">future</span> <span class="n">shared</span> <span class="n">library</span> <span class="n">load</span><span class="o">?</span> <span class="p">(</span><span class="n">y</span> <span class="n">or</span> <span class="p">[</span><span class="n">n</span><span class="p">])</span> <span class="n">y</span>
</span></span><span class="line"><span class="cl"><span class="n">Breakpoint</span> <span class="mi">1</span> <span class="p">(</span><span class="n">zim_yar_concurrent_client_call</span><span class="p">)</span> <span class="n">pending</span><span class="p">.</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="n">gdb</span><span class="p">)</span>
</span></span></code></pre></div><p>开始运行 PHP 并指定运行的 PHP 脚本。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="p">(</span><span class="n">gdb</span><span class="p">)</span> <span class="n">r</span> <span class="n">concurrent_client</span><span class="p">.</span><span class="n">php</span>
</span></span><span class="line"><span class="cl"><span class="n">Starting</span> <span class="nl">program</span><span class="p">:</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">php</span> <span class="n">concurrent_client</span><span class="p">.</span><span class="n">php</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="n">Thread</span> <span class="n">debugging</span> <span class="n">using</span> <span class="n">libthread_db</span> <span class="n">enabled</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">Using</span> <span class="n">host</span> <span class="n">libthread_db</span> <span class="n">library</span> <span class="s">&#34;/lib/x86_64-linux-gnu/libthread_db.so.1&#34;</span><span class="p">.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Breakpoint</span> <span class="mi">1</span><span class="p">,</span> <span class="nf">zim_yar_concurrent_client_call</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">execute_data</span><span class="o">=</span><span class="mh">0x5555557de9ee</span> <span class="o">&lt;</span><span class="n">zend_restore_lexical_state</span><span class="o">+</span><span class="mi">222</span><span class="o">&gt;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">return_value</span><span class="o">=</span><span class="mh">0x555555a2b240</span> <span class="o">&lt;</span><span class="n">language_scanner_globals</span><span class="o">&gt;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">at</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">vagrant</span><span class="o">/</span><span class="n">code</span><span class="o">/</span><span class="n">her</span><span class="o">-</span><span class="n">cat</span><span class="o">/</span><span class="n">yar</span><span class="o">/</span><span class="n">yar_client</span><span class="p">.</span><span class="nl">c</span><span class="p">:</span><span class="mi">645</span>
</span></span><span class="line"><span class="cl"><span class="mi">645</span>    <span class="nf">PHP_METHOD</span><span class="p">(</span><span class="n">yar_concurrent_client</span><span class="p">,</span> <span class="n">call</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="n">gdb</span><span class="p">)</span>
</span></span></code></pre></div><p>zim_yar_concurrent_client_call 函数作用是将 call 方法的参数组装为 yar_call_data_t 结构体，并存储到并行客户端的 _callstack 数组中，然后返回本次调用的序号。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// source：yar_client.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nf">PHP_METHOD</span><span class="p">(</span><span class="n">yar_concurrent_client</span><span class="p">,</span> <span class="n">call</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">zend_string</span> <span class="o">*</span><span class="n">uri</span><span class="p">,</span> <span class="o">*</span><span class="n">method</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">zend_string</span> <span class="o">*</span><span class="n">name</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">zval</span> <span class="o">*</span><span class="n">callstack</span><span class="p">,</span> <span class="n">item</span><span class="p">,</span> <span class="o">*</span><span class="n">status</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">zval</span> <span class="o">*</span><span class="n">error_callback</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">,</span> <span class="o">*</span><span class="n">callback</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">,</span> <span class="o">*</span><span class="n">parameters</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">,</span> <span class="o">*</span><span class="n">options</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">yar_call_data_t</span> <span class="o">*</span><span class="n">entry</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 解析 call 方法中的参数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="nf">zend_parse_parameters</span><span class="p">(</span><span class="nf">ZEND_NUM_ARGS</span><span class="p">(),</span> <span class="s">&#34;SS|a!z!za&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="o">&amp;</span><span class="n">uri</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">method</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">parameters</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">callback</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">error_callback</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">options</span><span class="p">)</span> <span class="o">==</span> <span class="n">FAILURE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 省略一些对参数校验的代码...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 校验并行客户端的状态
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">status</span> <span class="o">=</span> <span class="nf">zend_read_static_property</span><span class="p">(</span><span class="n">yar_concurrent_client_ce</span><span class="p">,</span> <span class="nf">ZEND_STRL</span><span class="p">(</span><span class="s">&#34;_start&#34;</span><span class="p">),</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nf">UNEXPECTED</span><span class="p">(</span><span class="nf">Z_TYPE_P</span><span class="p">(</span><span class="n">status</span><span class="p">)</span> <span class="o">==</span> <span class="n">IS_TRUE</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">php_error_docref</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">E_WARNING</span><span class="p">,</span> <span class="s">&#34;concurrent client has already started&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">RETURN_FALSE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 分配调用数据的内存
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">entry</span> <span class="o">=</span> <span class="nf">ecalloc</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">yar_call_data_t</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 将参数赋值到调用数据上
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">entry</span><span class="o">-&gt;</span><span class="n">uri</span> <span class="o">=</span> <span class="nf">zend_string_copy</span><span class="p">(</span><span class="n">uri</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">entry</span><span class="o">-&gt;</span><span class="n">method</span> <span class="o">=</span> <span class="nf">zend_string_copy</span><span class="p">(</span><span class="n">method</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">callback</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="nf">Z_ISNULL_P</span><span class="p">(</span><span class="n">callback</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ZVAL_COPY</span><span class="p">(</span><span class="o">&amp;</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">callback</span><span class="p">,</span> <span class="n">callback</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">error_callback</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="nf">Z_ISNULL_P</span><span class="p">(</span><span class="n">error_callback</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ZVAL_COPY</span><span class="p">(</span><span class="o">&amp;</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">ecallback</span><span class="p">,</span> <span class="n">error_callback</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">parameters</span> <span class="o">&amp;&amp;</span> <span class="n">IS_ARRAY</span> <span class="o">==</span> <span class="nf">Z_TYPE_P</span><span class="p">(</span><span class="n">parameters</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ZVAL_COPY</span><span class="p">(</span><span class="o">&amp;</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">parameters</span><span class="p">,</span> <span class="n">parameters</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">options</span> <span class="o">&amp;&amp;</span> <span class="n">IS_ARRAY</span> <span class="o">==</span> <span class="nf">Z_TYPE_P</span><span class="p">(</span><span class="n">options</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ZVAL_COPY</span><span class="p">(</span><span class="o">&amp;</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">options</span><span class="p">,</span> <span class="n">options</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 初始化调用栈数组
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">callstack</span> <span class="o">=</span> <span class="nf">zend_read_static_property</span><span class="p">(</span><span class="n">yar_concurrent_client_ce</span><span class="p">,</span> <span class="nf">ZEND_STRL</span><span class="p">(</span><span class="s">&#34;_callstack&#34;</span><span class="p">),</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nf">Z_ISNULL_P</span><span class="p">(</span><span class="n">callstack</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">zval</span> <span class="n">rv</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">array_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">rv</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">zend_update_static_property</span><span class="p">(</span><span class="n">yar_concurrent_client_ce</span><span class="p">,</span> <span class="nf">ZEND_STRL</span><span class="p">(</span><span class="s">&#34;_callstack&#34;</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">rv</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ZVAL_ARR</span><span class="p">(</span><span class="n">callstack</span><span class="p">,</span> <span class="nf">Z_ARRVAL</span><span class="p">(</span><span class="n">rv</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="nf">Z_DELREF</span><span class="p">(</span><span class="n">rv</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 将调用数据注册到 zend 中，并返回 zend_resouce 类型的数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">ZVAL_RES</span><span class="p">(</span><span class="o">&amp;</span><span class="n">item</span><span class="p">,</span> <span class="nf">zend_register_resource</span><span class="p">(</span><span class="n">entry</span><span class="p">,</span> <span class="n">le_calldata</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 设置调用数据的序号
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">entry</span><span class="o">-&gt;</span><span class="n">sequence</span> <span class="o">=</span> <span class="nf">zend_hash_num_elements</span><span class="p">(</span><span class="nf">Z_ARRVAL_P</span><span class="p">(</span><span class="n">callstack</span><span class="p">))</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 将 zend_resource 类型的调用数据存储到 callstack 中
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">zend_hash_next_index_insert</span><span class="p">(</span><span class="nf">Z_ARRVAL_P</span><span class="p">(</span><span class="n">callstack</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">item</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">RETURN_LONG</span><span class="p">(</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">sequence</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>让代码执行到最后一行，并打印 entry 中的数据。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">(gdb) u 733
</span></span><span class="line"><span class="cl">zim_yar_concurrent_client_call (execute_data=0x7ffff54130d0,
</span></span><span class="line"><span class="cl">    return_value=0x7fffffffaa80)
</span></span><span class="line"><span class="cl">    at /home/vagrant/code/her-cat/yar/yar_client.c:733
</span></span><span class="line"><span class="cl">733        RETURN_LONG(entry-&gt;sequence);
</span></span><span class="line"><span class="cl">(gdb) p *entry-&gt;uri.val@32
</span></span><span class="line"><span class="cl">$5 = &#34;http://127.0.0.1:3000/server.php&#34;
</span></span><span class="line"><span class="cl">(gdb) p *entry-&gt;method.val@5
</span></span><span class="line"><span class="cl">$6 = &#34;login&#34;
</span></span><span class="line"><span class="cl">(gdb) p entry-&gt;sequence
</span></span><span class="line"><span class="cl">$7 = 1
</span></span></code></pre></div><h3 id="yar_concurrent_clientloop">Yar_Concurrent_Client::loop</h3>
<p>先在 loop 方法上打断点，然后让代码执行到断点处。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">(gdb) b zim_yar_concurrent_client_loop
</span></span><span class="line"><span class="cl">Breakpoint 2 at 0x7ffff56e97f7: file /home/vagrant/code/her-cat/yar/yar_client.c, line 751.
</span></span><span class="line"><span class="cl">(gdb) c
</span></span><span class="line"><span class="cl">Continuing.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Breakpoint 1, zim_yar_concurrent_client_call (execute_data=0x7ffff54130d0, return_value=0x7fffffffaa80)
</span></span><span class="line"><span class="cl">    at /home/vagrant/code/her-cat/yar/yar_client.c:645
</span></span><span class="line"><span class="cl">645    PHP_METHOD(yar_concurrent_client, call) {
</span></span><span class="line"><span class="cl">(gdb)
</span></span></code></pre></div><p>在 zim_yar_concurrent_client_loop 函数中，先校验 loop 方法的参数及并行客户端的运行状态，然后从并行客户端中读取 _callstack 数组，调用 php_yar_concurrent_client_handle 函数。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// source：yar_client.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nf">PHP_METHOD</span><span class="p">(</span><span class="n">yar_concurrent_client</span><span class="p">,</span> <span class="n">loop</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">zend_string</span> <span class="o">*</span><span class="n">name</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">zval</span> <span class="o">*</span><span class="n">callstack</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">zval</span> <span class="o">*</span><span class="n">callback</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">,</span> <span class="o">*</span><span class="n">error_callback</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">zval</span> <span class="o">*</span><span class="n">status</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="n">ret</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 解析参数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="nf">zend_parse_parameters</span><span class="p">(</span><span class="nf">ZEND_NUM_ARGS</span><span class="p">(),</span> <span class="s">&#34;|zz&#34;</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">callback</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">error_callback</span><span class="p">)</span> <span class="o">==</span> <span class="n">FAILURE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 判断运行状态
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">status</span> <span class="o">=</span> <span class="nf">zend_read_static_property</span><span class="p">(</span><span class="n">yar_concurrent_client_ce</span><span class="p">,</span> <span class="nf">ZEND_STRL</span><span class="p">(</span><span class="s">&#34;_start&#34;</span><span class="p">),</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nf">UNEXPECTED</span><span class="p">(</span><span class="nf">Z_TYPE_P</span><span class="p">(</span><span class="n">status</span><span class="p">)</span> <span class="o">==</span> <span class="n">IS_TRUE</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">php_error_docref</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">E_WARNING</span><span class="p">,</span> <span class="s">&#34;concurrent client has already started&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">RETURN_FALSE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 省略一些对参数校验的代码...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 读取所有的调用数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">callstack</span> <span class="o">=</span> <span class="nf">zend_read_static_property</span><span class="p">(</span><span class="n">yar_concurrent_client_ce</span><span class="p">,</span> <span class="nf">ZEND_STRL</span><span class="p">(</span><span class="s">&#34;_callstack&#34;</span><span class="p">),</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nf">Z_ISNULL_P</span><span class="p">(</span><span class="n">callstack</span><span class="p">)</span> <span class="o">||</span> <span class="nf">zend_hash_num_elements</span><span class="p">(</span><span class="nf">Z_ARRVAL_P</span><span class="p">(</span><span class="n">callstack</span><span class="p">))</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">RETURN_TRUE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 更新为运行中的状态
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">ZVAL_BOOL</span><span class="p">(</span><span class="n">status</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">ret</span> <span class="o">=</span> <span class="nf">php_yar_concurrent_client_handle</span><span class="p">(</span><span class="n">callstack</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">ZVAL_BOOL</span><span class="p">(</span><span class="n">status</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">RETURN_BOOL</span><span class="p">(</span><span class="n">ret</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="php_yar_concurrent_client_handle">php_yar_concurrent_client_handle</h3>
<p>让代码执行到调用 php_yar_concurrent_client_handle 的前一行，然后进入函数内部。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">(gdb) u 801
</span></span><span class="line"><span class="cl">zim_yar_concurrent_client_loop (execute_data=0x7ffff54130d0, return_value=0x7fffffffaa80)
</span></span><span class="line"><span class="cl">    at /home/vagrant/code/her-cat/yar/yar_client.c:801
</span></span><span class="line"><span class="cl">801        ZVAL_BOOL(status, 1);
</span></span><span class="line"><span class="cl">(gdb) s
</span></span><span class="line"><span class="cl">802        ret = php_yar_concurrent_client_handle(callstack);
</span></span><span class="line"><span class="cl">(gdb) s
</span></span><span class="line"><span class="cl">php_yar_concurrent_client_handle (callstack=0x0) at /home/vagrant/code/her-cat/yar/yar_client.c:456
</span></span><span class="line"><span class="cl">456    int php_yar_concurrent_client_handle(zval *callstack) /* {{{ */ {
</span></span><span class="line"><span class="cl">(gdb)
</span></span></code></pre></div><p>php_yar_concurrent_client_handle 函数的主要逻辑：</p>
<ul>
<li>遍历 callstack 数组
<ul>
<li>为每一个调用数据创建同步传输器。</li>
<li>分别调用同步传输器中的 open、send、calldata 等函数初始化调用请求。</li>
<li>调用 multi-&gt;add 函数将这些同步传输器存储到并行调用器中。</li>
</ul>
</li>
<li>调用 multi-&gt;exec 函数将这些请求发送出去。</li>
<li>收到响应结果后调用 php_yar_concurrent_client_callback 函数进行处理。</li>
<li>最后调用 multi-&gt;close 函数释放相关资源。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// source：yar_client.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">php_yar_concurrent_client_handle</span><span class="p">(</span><span class="n">zval</span> <span class="o">*</span><span class="n">callstack</span><span class="p">)</span> <span class="cm">/* </span><span class="p">{{{</span> <span class="err">*/</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">char</span> <span class="o">*</span><span class="n">msg</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">zval</span> <span class="o">*</span><span class="n">calldata</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">yar_request_t</span> <span class="o">*</span><span class="n">request</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">yar_transport_t</span> <span class="o">*</span><span class="n">factory</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">yar_transport_interface_t</span> <span class="o">*</span><span class="n">transport</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">yar_transport_multi_interface_t</span> <span class="o">*</span><span class="n">multi</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 获取 curl 传输器工厂实例
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">factory</span> <span class="o">=</span> <span class="nf">php_yar_transport_get</span><span class="p">(</span><span class="nf">ZEND_STRL</span><span class="p">(</span><span class="s">&#34;curl&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 创建并行传输器实例
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">multi</span> <span class="o">=</span> <span class="n">factory</span><span class="o">-&gt;</span><span class="n">multi</span><span class="o">-&gt;</span><span class="nf">init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 遍历 callstack 数组
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">ZEND_HASH_FOREACH_VAL</span><span class="p">(</span><span class="nf">Z_ARRVAL_P</span><span class="p">(</span><span class="n">callstack</span><span class="p">),</span> <span class="n">calldata</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">yar_call_data_t</span> <span class="o">*</span><span class="n">entry</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kt">long</span> <span class="n">flags</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">entry</span> <span class="o">=</span> <span class="p">(</span><span class="kt">yar_call_data_t</span> <span class="o">*</span><span class="p">)</span><span class="nf">zend_fetch_resource</span><span class="p">(</span><span class="nf">Z_RES_P</span><span class="p">(</span><span class="n">calldata</span><span class="p">),</span> <span class="s">&#34;Yar Call Data&#34;</span><span class="p">,</span> <span class="n">le_calldata</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">entry</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">continue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nf">Z_ISUNDEF</span><span class="p">(</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">parameters</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">array_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">parameters</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> 
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// 为每一个调用数据创建同步传输器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">transport</span> <span class="o">=</span> <span class="n">factory</span><span class="o">-&gt;</span><span class="nf">init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">Z_ISUNDEF</span><span class="p">(</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">options</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">zval</span> <span class="o">*</span><span class="n">flag</span> <span class="o">=</span> <span class="nf">php_yar_client_get_opt</span><span class="p">(</span><span class="o">&amp;</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">options</span><span class="p">,</span> <span class="n">YAR_OPT_PERSISTENT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">flag</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="nf">Z_TYPE_P</span><span class="p">(</span><span class="n">flag</span><span class="p">)</span> <span class="o">==</span> <span class="n">IS_TRUE</span> <span class="o">||</span> <span class="p">(</span><span class="nf">Z_TYPE_P</span><span class="p">(</span><span class="n">flag</span><span class="p">)</span> <span class="o">==</span> <span class="n">IS_LONG</span> <span class="o">&amp;&amp;</span> <span class="nf">Z_LVAL_P</span><span class="p">(</span><span class="n">flag</span><span class="p">))))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">flags</span> <span class="o">|=</span> <span class="n">YAR_PROTOCOL_PERSISTENT</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// 创建调用请求
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">request</span> <span class="o">=</span> <span class="nf">php_yar_request_instance</span><span class="p">(</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">method</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="o">&amp;</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">parameters</span><span class="p">,</span> <span class="nf">Z_ISUNDEF</span><span class="p">(</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">options</span><span class="p">)</span><span class="o">?</span> <span class="nb">NULL</span><span class="o">:</span> <span class="o">&amp;</span> <span class="n">entry</span><span class="o">-&gt;</span><span class="n">options</span><span class="p">)))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">transport</span><span class="o">-&gt;</span><span class="nf">close</span><span class="p">(</span><span class="n">transport</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">factory</span><span class="o">-&gt;</span><span class="nf">destroy</span><span class="p">(</span><span class="n">transport</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// 创建一个连接
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">msg</span> <span class="o">=</span> <span class="p">(</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">entry</span><span class="o">-&gt;</span><span class="n">options</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">transport</span><span class="o">-&gt;</span><span class="nf">open</span><span class="p">(</span><span class="n">transport</span><span class="p">,</span> <span class="n">entry</span><span class="o">-&gt;</span><span class="n">uri</span><span class="p">,</span> <span class="n">flags</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">msg</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">php_yar_client_trigger_error</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">YAR_ERR_TRANSPORT</span><span class="p">,</span> <span class="n">msg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">transport</span><span class="o">-&gt;</span><span class="nf">close</span><span class="p">(</span><span class="n">transport</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">factory</span><span class="o">-&gt;</span><span class="nf">destroy</span><span class="p">(</span><span class="n">transport</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nf">efree</span><span class="p">(</span><span class="n">msg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// 对请求数据进行编码、组装等操作，并将处理后的数据保存到连接中
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">transport</span><span class="o">-&gt;</span><span class="nf">send</span><span class="p">(</span><span class="n">transport</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">msg</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">php_yar_client_trigger_error</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">YAR_ERR_TRANSPORT</span><span class="p">,</span> <span class="n">msg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">transport</span><span class="o">-&gt;</span><span class="nf">close</span><span class="p">(</span><span class="n">transport</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">factory</span><span class="o">-&gt;</span><span class="nf">destroy</span><span class="p">(</span><span class="n">transport</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nf">efree</span><span class="p">(</span><span class="n">msg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 将调用数据保存到同步传输器中
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">transport</span><span class="o">-&gt;</span><span class="nf">calldata</span><span class="p">(</span><span class="n">transport</span><span class="p">,</span> <span class="n">entry</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 将同步传输器存储到并行调用器中。
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">multi</span><span class="o">-&gt;</span><span class="nf">add</span><span class="p">(</span><span class="n">multi</span><span class="p">,</span> <span class="n">transport</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 释放请求
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">php_yar_request_destroy</span><span class="p">(</span><span class="n">request</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="nf">ZEND_HASH_FOREACH_END</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 执行所有请求
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">multi</span><span class="o">-&gt;</span><span class="nf">exec</span><span class="p">(</span><span class="n">multi</span><span class="p">,</span> <span class="n">php_yar_concurrent_client_callback</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">multi</span><span class="o">-&gt;</span><span class="nf">close</span><span class="p">(</span><span class="n">multi</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 释放资源
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">multi</span><span class="o">-&gt;</span><span class="nf">close</span><span class="p">(</span><span class="n">multi</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>我们看下并行传输器中函数指针对应的函数。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">(gdb) p *multi
</span></span><span class="line"><span class="cl">$10 = {data = 0x7ffff5461050, add = 0x7ffff56eea4e &lt;php_yar_curl_multi_add_handle&gt;,
</span></span><span class="line"><span class="cl">  exec = 0x7ffff56ef1e4 &lt;php_yar_curl_multi_exec&gt;, close = 0x7ffff56ef6af &lt;php_yar_curl_multi_close&gt;}
</span></span></code></pre></div><p>可以看到 add、exec、close 分别对应 php_yar_curl_multi_add_handle、php_yar_curl_multi_exec、php_yar_curl_multi_close 等函数，分别在这些函数上打断点，然后继续运行程序。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">(gdb) b php_yar_curl_multi_add_handle
</span></span><span class="line"><span class="cl">Breakpoint 3 at 0x7ffff56eea4e: file /home/vagrant/code/her-cat/yar/transports/curl.c, line 604.
</span></span><span class="line"><span class="cl">(gdb) b php_yar_curl_multi_exec
</span></span><span class="line"><span class="cl">Breakpoint 4 at 0x7ffff56ef1e4: file /home/vagrant/code/her-cat/yar/transports/curl.c, line 748.
</span></span><span class="line"><span class="cl">(gdb) b php_yar_curl_multi_close
</span></span><span class="line"><span class="cl">Breakpoint 5 at 0x7ffff56ef6af: file /home/vagrant/code/her-cat/yar/transports/curl.c, line 951.
</span></span><span class="line"><span class="cl">(gdb) c
</span></span><span class="line"><span class="cl">Continuing.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Breakpoint 3, php_yar_curl_multi_add_handle (self=0x7ffff5402428, handle=0x7ffff548e060)
</span></span><span class="line"><span class="cl">    at /home/vagrant/code/her-cat/yar/transports/curl.c:604
</span></span><span class="line"><span class="cl">warning: Source file is more recent than executable.
</span></span><span class="line"><span class="cl">604    int php_yar_curl_multi_add_handle(yar_transport_multi_interface_t *self, yar_transport_interface_t *handle) /* {{{ */ {
</span></span><span class="line"><span class="cl">(gdb)
</span></span></code></pre></div><h3 id="php_yar_curl_multi_add_handle">php_yar_curl_multi_add_handle</h3>
<p>php_yar_curl_multi_add_handle 函数的作用：将同步传输器实例以头插法保存到 chs 单链表中。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// source：transports/curl.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">php_yar_curl_multi_add_handle</span><span class="p">(</span><span class="kt">yar_transport_multi_interface_t</span> <span class="o">*</span><span class="n">self</span><span class="p">,</span> <span class="kt">yar_transport_interface_t</span> <span class="o">*</span><span class="n">handle</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">yar_curl_multi_data_t</span> <span class="o">*</span><span class="n">multi</span> <span class="o">=</span> <span class="p">(</span><span class="kt">yar_curl_multi_data_t</span> <span class="o">*</span><span class="p">)</span><span class="n">self</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">yar_curl_data_t</span> <span class="o">*</span><span class="n">data</span> <span class="o">=</span> <span class="p">(</span><span class="kt">yar_curl_data_t</span> <span class="o">*</span><span class="p">)</span><span class="n">handle</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 预处理，将传输数据保存到 curl 中
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">php_yar_curl_prepare</span><span class="p">(</span><span class="n">handle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 将同步传输器的 curl 实例添加到 curl 批处理实例中
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">curl_multi_add_handle</span><span class="p">(</span><span class="n">multi</span><span class="o">-&gt;</span><span class="n">cm</span><span class="p">,</span> <span class="n">data</span><span class="o">-&gt;</span><span class="n">cp</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 头插法保存
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">multi</span><span class="o">-&gt;</span><span class="n">chs</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">data</span><span class="o">-&gt;</span><span class="n">next</span> <span class="o">=</span> <span class="n">multi</span><span class="o">-&gt;</span><span class="n">chs</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">multi</span><span class="o">-&gt;</span><span class="n">chs</span> <span class="o">=</span> <span class="n">handle</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">multi</span><span class="o">-&gt;</span><span class="n">chs</span> <span class="o">=</span> <span class="n">handle</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="php_yar_curl_multi_exec">php_yar_curl_multi_exec</h3>
<p>php_yar_curl_multi_exec 函数的作用：通过 epoll 或 select 多路复用机制监控文件描述符的状态，并配合 curl_multi_* 系列函数对请求进行处理。</p>
<p>以下是主要逻辑：</p>
<ul>
<li>从 curl 批处理实例中取出所有请求的文件描述符。</li>
<li>调用 select 函数监听文件描述符的状态，阻塞当前进程。</li>
<li>进程从休眠状态恢复后，调用 curl_multi_perform 处理响应数据。</li>
<li>调用 php_yar_curl_multi_parse_response 函数解析响应数据。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// source：transports/curl.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">php_yar_curl_multi_exec</span><span class="p">(</span><span class="kt">yar_transport_multi_interface_t</span> <span class="o">*</span><span class="n">self</span><span class="p">,</span> <span class="n">yar_concurrent_client_callback</span> <span class="o">*</span><span class="n">f</span><span class="p">)</span> <span class="cm">/* </span><span class="p">{{{</span> <span class="err">*/</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">running_count</span><span class="p">,</span> <span class="n">rest_count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">yar_curl_multi_data_t</span> <span class="o">*</span><span class="n">multi</span> <span class="o">=</span> <span class="p">(</span><span class="kt">yar_curl_multi_data_t</span> <span class="o">*</span><span class="p">)</span><span class="n">self</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 尝试让 libcurl 处理数据，并获取当前正在运行中的数量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">while</span> <span class="p">(</span><span class="n">CURLM_CALL_MULTI_PERFORM</span> <span class="o">==</span> <span class="nf">curl_multi_perform</span><span class="p">(</span><span class="n">multi</span><span class="o">-&gt;</span><span class="n">cm</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">running_count</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 第一次调用回调函数，让调用方执行自己的逻辑。
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">f</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">YAR_ERR_OKEY</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">goto</span> <span class="n">bailout</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">running_count</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">rest_count</span> <span class="o">=</span> <span class="n">running_count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">do</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">int</span> <span class="n">max_fd</span><span class="p">,</span> <span class="n">return_code</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">struct</span> <span class="n">timeval</span> <span class="n">tv</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">fd_set</span> <span class="n">readfds</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">fd_set</span> <span class="n">writefds</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">fd_set</span> <span class="n">exceptfds</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="nf">FD_ZERO</span><span class="p">(</span><span class="o">&amp;</span><span class="n">readfds</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nf">FD_ZERO</span><span class="p">(</span><span class="o">&amp;</span><span class="n">writefds</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nf">FD_ZERO</span><span class="p">(</span><span class="o">&amp;</span><span class="n">exceptfds</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">            <span class="c1">// 从 curl 批处理实例中取出所有请求的文件描述符
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">curl_multi_fdset</span><span class="p">(</span><span class="n">multi</span><span class="o">-&gt;</span><span class="n">cm</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">readfds</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">writefds</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">exceptfds</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">max_fd</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">max_fd</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// max_fd 为 -1 说明 libcurl 正在处理一些不能使用套接字监控的事情，
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="c1">// 所以需要等待一会再调用 curl_multi_perform
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="kt">long</span> <span class="n">timeout</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="nf">curl_multi_timeout</span><span class="p">(</span><span class="n">multi</span><span class="o">-&gt;</span><span class="n">cm</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">timeout</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="p">(</span><span class="n">timeout</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">timeout</span> <span class="o">=</span> <span class="mi">50</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="p">(</span><span class="n">timeout</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">tv</span><span class="p">.</span><span class="n">tv_sec</span> <span class="o">=</span> <span class="n">timeout</span> <span class="o">/</span> <span class="mi">1000</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                    <span class="n">tv</span><span class="p">.</span><span class="n">tv_usec</span> <span class="o">=</span> <span class="p">(</span><span class="n">timeout</span> <span class="o">%</span> <span class="mi">1000</span><span class="p">)</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                    <span class="nf">select</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">readfds</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">writefds</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">exceptfds</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">tv</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 让 libcurl 处理数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="k">while</span> <span class="p">(</span><span class="n">CURLM_CALL_MULTI_PERFORM</span> <span class="o">==</span> <span class="nf">curl_multi_perform</span><span class="p">(</span><span class="n">multi</span><span class="o">-&gt;</span><span class="n">cm</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">running_count</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">                <span class="k">goto</span> <span class="n">process</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// 计算 select 超时时间
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">tv</span><span class="p">.</span><span class="n">tv_sec</span> <span class="o">=</span> <span class="p">(</span><span class="n">zend_ulong</span><span class="p">)(</span><span class="nf">YAR_G</span><span class="p">(</span><span class="n">timeout</span><span class="p">)</span> <span class="o">/</span> <span class="mi">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">tv</span><span class="p">.</span><span class="n">tv_usec</span> <span class="o">=</span> <span class="p">(</span><span class="n">zend_ulong</span><span class="p">)((</span><span class="nf">YAR_G</span><span class="p">(</span><span class="n">timeout</span><span class="p">)</span> <span class="o">%</span> <span class="mi">1000</span><span class="p">)</span><span class="o">?</span> <span class="p">(</span><span class="nf">YAR_G</span><span class="p">(</span><span class="n">timeout</span><span class="p">)</span> <span class="o">%</span> <span class="mi">1000</span><span class="p">)</span> <span class="o">*</span> <span class="mi">1000</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// 等待请求返回数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">return_code</span> <span class="o">=</span> <span class="nf">select</span><span class="p">(</span><span class="n">max_fd</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">readfds</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">writefds</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">exceptfds</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">tv</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">return_code</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 说明某些文件描述符可读/可写，让 libcurl 进行处理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="k">while</span> <span class="p">(</span><span class="n">CURLM_CALL_MULTI_PERFORM</span> <span class="o">==</span> <span class="nf">curl_multi_perform</span><span class="p">(</span><span class="n">multi</span><span class="o">-&gt;</span><span class="n">cm</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">running_count</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span> <span class="o">==</span> <span class="n">return_code</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 发生异常
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="nf">php_error_docref</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">E_WARNING</span><span class="p">,</span> <span class="s">&#34;select error &#39;%s&#39;&#34;</span><span class="p">,</span> <span class="nf">strerror</span><span class="p">(</span><span class="n">errno</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">                <span class="k">goto</span> <span class="n">onerror</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 等待超时
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="nf">php_error_docref</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">E_WARNING</span><span class="p">,</span> <span class="s">&#34;select timeout %ldms reached&#34;</span><span class="p">,</span> <span class="nf">YAR_G</span><span class="p">(</span><span class="n">timeout</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">                <span class="k">goto</span> <span class="n">onerror</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nl">process</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">rest_count</span> <span class="o">&gt;</span> <span class="n">running_count</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 解析响应的数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="kt">int</span> <span class="n">ret</span> <span class="o">=</span> <span class="nf">php_yar_curl_multi_parse_response</span><span class="p">(</span><span class="n">multi</span><span class="p">,</span> <span class="n">f</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="k">goto</span> <span class="n">bailout</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="k">goto</span> <span class="n">onerror</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="n">rest_count</span> <span class="o">=</span> <span class="n">running_count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="n">running_count</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 第一次尝试让 libcurl 处理数据后，
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="c1">// 如果运行中的数量为 0 ，说明已经全部处理完了，
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="c1">// 直接进行解析
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="kt">int</span> <span class="n">ret</span> <span class="o">=</span> <span class="nf">php_yar_curl_multi_parse_response</span><span class="p">(</span><span class="n">multi</span><span class="p">,</span> <span class="n">f</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">goto</span> <span class="n">bailout</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">goto</span> <span class="n">onerror</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nl">onerror</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nl">bailout</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">self</span><span class="o">-&gt;</span><span class="nf">close</span><span class="p">(</span><span class="n">self</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">zend_bailout</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="php_yar_curl_multi_parse_response">php_yar_curl_multi_parse_response</h3>
<p>在 php_yar_curl_multi_parse_response 函数上打断点，然后继续运行程序。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">(gdb) b php_yar_curl_multi_parse_response
</span></span><span class="line"><span class="cl">Breakpoint 6 at 0x7ffff56eeadb: file /home/vagrant/code/her-cat/yar/transports/curl.c, line 622.
</span></span><span class="line"><span class="cl">(gdb) c
</span></span><span class="line"><span class="cl">Continuing.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Breakpoint 6, php_yar_curl_multi_parse_response (multi=0x20b666f03b932500, f=0x16fb)
</span></span><span class="line"><span class="cl">    at /home/vagrant/code/her-cat/yar/transports/curl.c:622
</span></span><span class="line"><span class="cl">622    static int php_yar_curl_multi_parse_response(yar_curl_multi_data_t *multi, yar_concurrent_client_callback *f) /* {{{ */ {
</span></span><span class="line"><span class="cl">(gdb) 
</span></span></code></pre></div><p>php_yar_curl_multi_parse_response 函数的主要逻辑：</p>
<ul>
<li>从 curl 批处理实例中取出所有收到的响应数据。</li>
<li>遍历 multi-&gt;chs 找到数据对应的同步传输器。</li>
<li>检查响应数据是否正确。</li>
<li>解析响应数据，得到 Yar 协议中的头部及 payload 部分的内容。</li>
<li>调用 php_yar_concurrent_client_callback 函数，执行对应的回调函数。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// source：transports/curl.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="nf">php_yar_curl_multi_parse_response</span><span class="p">(</span><span class="kt">yar_curl_multi_data_t</span> <span class="o">*</span><span class="n">multi</span><span class="p">,</span> <span class="n">yar_concurrent_client_callback</span> <span class="o">*</span><span class="n">f</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">msg_in_sequence</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">CURLMsg</span> <span class="o">*</span><span class="n">msg</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">do</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 从 curl 批处理实例中读取一条消息，并返回剩余消息数量（msg_in_sequence）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">msg</span> <span class="o">=</span> <span class="nf">curl_multi_info_read</span><span class="p">(</span><span class="n">multi</span><span class="o">-&gt;</span><span class="n">cm</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">msg_in_sequence</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">msg</span> <span class="o">&amp;&amp;</span> <span class="n">msg</span><span class="o">-&gt;</span><span class="n">msg</span> <span class="o">==</span> <span class="n">CURLMSG_DONE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 标记是否找到了对应的同步传输器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="kt">unsigned</span> <span class="n">found</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="kt">yar_transport_interface_t</span> <span class="o">*</span><span class="n">handle</span> <span class="o">=</span> <span class="n">multi</span><span class="o">-&gt;</span><span class="n">chs</span><span class="p">,</span> <span class="o">*</span><span class="n">q</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// 遍历单链表
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">while</span> <span class="p">(</span><span class="n">handle</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// curl 实例的指针地址相等，说明找到了
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="k">if</span> <span class="p">(</span><span class="n">msg</span><span class="o">-&gt;</span><span class="n">easy_handle</span> <span class="o">==</span> <span class="p">((</span><span class="kt">yar_curl_data_t</span><span class="o">*</span><span class="p">)</span><span class="n">handle</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">cp</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="c1">// 从单链表中移除
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="k">if</span> <span class="p">(</span><span class="n">q</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="p">((</span><span class="kt">yar_curl_data_t</span> <span class="o">*</span><span class="p">)</span><span class="n">q</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">next</span> <span class="o">=</span> <span class="p">((</span><span class="kt">yar_curl_data_t</span><span class="o">*</span><span class="p">)</span><span class="n">handle</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="n">multi</span><span class="o">-&gt;</span><span class="n">chs</span> <span class="o">=</span> <span class="p">((</span><span class="kt">yar_curl_data_t</span><span class="o">*</span><span class="p">)</span><span class="n">handle</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                    <span class="n">found</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                    <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="n">q</span> <span class="o">=</span> <span class="n">handle</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="n">handle</span> <span class="o">=</span> <span class="p">((</span><span class="kt">yar_curl_data_t</span><span class="o">*</span><span class="p">)</span><span class="n">handle</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">found</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="kt">long</span> <span class="n">http_code</span> <span class="o">=</span> <span class="mi">200</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="kt">yar_response_t</span> <span class="o">*</span><span class="n">response</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="kt">yar_curl_data_t</span> <span class="o">*</span><span class="n">data</span> <span class="o">=</span> <span class="p">(</span><span class="kt">yar_curl_data_t</span> <span class="o">*</span><span class="p">)</span><span class="n">handle</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                <span class="c1">// 创建响应实例
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="n">response</span> <span class="o">=</span> <span class="nf">php_yar_response_instance</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="p">(</span><span class="n">msg</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">.</span><span class="n">result</span> <span class="o">==</span> <span class="n">CURLE_OK</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nf">curl_multi_remove_handle</span><span class="p">(</span><span class="n">multi</span><span class="o">-&gt;</span><span class="n">cm</span><span class="p">,</span> <span class="n">data</span><span class="o">-&gt;</span><span class="n">cp</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                    
</span></span><span class="line"><span class="cl">                    <span class="c1">// 获取 HTTP 状态码
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="k">if</span><span class="p">(</span><span class="nf">curl_easy_getinfo</span><span class="p">(</span><span class="n">data</span><span class="o">-&gt;</span><span class="n">cp</span><span class="p">,</span> <span class="n">CURLINFO_RESPONSE_CODE</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">http_code</span><span class="p">)</span> <span class="o">==</span> <span class="n">CURLE_OK</span> <span class="o">&amp;&amp;</span> <span class="n">http_code</span> <span class="o">!=</span> <span class="mi">200</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="c1">// 非 200 说明出现异常
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                        <span class="c1">// 返回异常响应
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                        <span class="k">continue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="c1">// 状态码为 200 
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                        <span class="k">if</span> <span class="p">(</span><span class="n">data</span><span class="o">-&gt;</span><span class="n">buf</span><span class="p">.</span><span class="n">s</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                            <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                            
</span></span><span class="line"><span class="cl">                            <span class="c1">// 从响应数据中解析出 Yar 协议的头部信息
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                            <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">header</span> <span class="o">=</span> <span class="nf">php_yar_protocol_parse</span><span class="p">(</span><span class="n">payload</span><span class="p">)))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nf">php_yar_error</span><span class="p">(</span><span class="n">response</span><span class="p">,</span> <span class="n">YAR_ERR_PROTOCOL</span><span class="p">,</span> <span class="s">&#34;malformed response header &#39;%.32s&#39;&#34;</span><span class="p">,</span> <span class="n">payload</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="c1">// 跳过 Yar 协议的头部信息后的内容，就是真正的响应结果
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                                <span class="n">payload</span> <span class="o">+=</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">yar_header_t</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                                <span class="n">payload_len</span> <span class="o">-=</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">yar_header_t</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                                
</span></span><span class="line"><span class="cl">                                <span class="c1">// 通过 payload 中的编码方式对 payload 进行解码
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                                <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">retval</span> <span class="o">=</span> <span class="nf">php_yar_packager_unpack</span><span class="p">(</span><span class="n">payload</span><span class="p">,</span> <span class="n">payload_len</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">msg</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ret</span><span class="p">)))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                    <span class="nf">php_yar_response_set_error</span><span class="p">(</span><span class="n">response</span><span class="p">,</span> <span class="n">YAR_ERR_PACKAGER</span><span class="p">,</span> <span class="n">msg</span><span class="p">,</span> <span class="nf">strlen</span><span class="p">(</span><span class="n">msg</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">                                <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                    <span class="c1">// 将解码后的数据存储到 response 中
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                                    <span class="nf">php_yar_response_map_retval</span><span class="p">(</span><span class="n">response</span><span class="p">,</span> <span class="n">retval</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                                    <span class="nf">DEBUG_C</span><span class="p">(</span><span class="n">ZEND_ULONG_FMT</span><span class="s">&#34;: server response content packaged by &#39;%.*s&#39;, len &#39;%ld&#39;, content &#39;%.32s&#39;&#34;</span><span class="p">,</span> <span class="n">response</span><span class="o">-&gt;</span><span class="n">id</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">header</span><span class="o">-&gt;</span><span class="n">body_len</span><span class="p">,</span> <span class="n">payload</span> <span class="o">+</span> <span class="mi">8</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                                    <span class="nf">zval_ptr_dtor</span><span class="p">(</span><span class="n">retval</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                                <span class="k">if</span> <span class="p">(</span><span class="n">msg</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                    <span class="nf">efree</span><span class="p">(</span><span class="n">msg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                            <span class="p">}</span>
</span></span><span class="line"><span class="cl">                        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                            <span class="c1">// 响应内容为空，设置错误信息
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                            <span class="nf">php_yar_response_set_error</span><span class="p">(</span><span class="n">response</span><span class="p">,</span> <span class="n">YAR_ERR_EMPTY_RESPONSE</span><span class="p">,</span> <span class="nf">ZEND_STRL</span><span class="p">(</span><span class="s">&#34;empty response&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">                        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                        <span class="c1">// 调用 php_yar_concurrent_client_callback 函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">f</span><span class="p">(</span><span class="n">data</span><span class="o">-&gt;</span><span class="n">calldata</span><span class="p">,</span> <span class="n">response</span><span class="o">-&gt;</span><span class="n">status</span><span class="p">,</span> <span class="n">response</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                            <span class="n">handle</span><span class="o">-&gt;</span><span class="nf">close</span><span class="p">(</span><span class="n">handle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                            <span class="nf">php_yar_response_destroy</span><span class="p">(</span><span class="n">response</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                            <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                        <span class="p">}</span>
</span></span><span class="line"><span class="cl">                        <span class="k">if</span> <span class="p">(</span><span class="nf">EG</span><span class="p">(</span><span class="n">exception</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                            <span class="n">handle</span><span class="o">-&gt;</span><span class="nf">close</span><span class="p">(</span><span class="n">handle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                            <span class="nf">php_yar_response_destroy</span><span class="p">(</span><span class="n">response</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                            <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                        <span class="p">}</span>
</span></span><span class="line"><span class="cl">                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="c1">// 执行请求失败，返回失败原因
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="kt">char</span> <span class="o">*</span><span class="n">err</span> <span class="o">=</span> <span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="nf">curl_easy_strerror</span><span class="p">(</span><span class="n">msg</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">.</span><span class="n">result</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                    <span class="nf">php_yar_response_set_error</span><span class="p">(</span><span class="n">response</span><span class="p">,</span> <span class="n">YAR_ERR_TRANSPORT</span><span class="p">,</span> <span class="n">err</span><span class="p">,</span> <span class="nf">strlen</span><span class="p">(</span><span class="n">err</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">                    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">f</span><span class="p">(</span><span class="n">data</span><span class="o">-&gt;</span><span class="n">calldata</span><span class="p">,</span> <span class="n">YAR_ERR_TRANSPORT</span><span class="p">,</span> <span class="n">response</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="n">handle</span><span class="o">-&gt;</span><span class="nf">close</span><span class="p">(</span><span class="n">handle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                        <span class="nf">php_yar_response_destroy</span><span class="p">(</span><span class="n">response</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                    <span class="k">if</span> <span class="p">(</span><span class="nf">EG</span><span class="p">(</span><span class="n">exception</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="n">handle</span><span class="o">-&gt;</span><span class="nf">close</span><span class="p">(</span><span class="n">handle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                        <span class="nf">php_yar_response_destroy</span><span class="p">(</span><span class="n">response</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="n">handle</span><span class="o">-&gt;</span><span class="nf">close</span><span class="p">(</span><span class="n">handle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="nf">php_yar_response_destroy</span><span class="p">(</span><span class="n">response</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nf">php_error_docref</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">E_WARNING</span><span class="p">,</span> <span class="s">&#34;unexpected transport info missed&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="n">msg_in_sequence</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="php_yar_concurrent_client_callback">php_yar_concurrent_client_callback</h3>
<p>该函数的作用：通过响应状态判断本次远程调用的结果，执行相应的回调函数。</p>
<p>主要逻辑：</p>
<ul>
<li>通过响应状态获取要执行的 PHP 回调函数。</li>
<li>组装被调用函数的参数。</li>
<li>执行 PHP 回调函数。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// source：yar_client.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">php_yar_concurrent_client_callback</span><span class="p">(</span><span class="kt">yar_call_data_t</span> <span class="o">*</span><span class="n">calldata</span><span class="p">,</span> <span class="kt">int</span> <span class="n">status</span><span class="p">,</span> <span class="kt">yar_response_t</span> <span class="o">*</span><span class="n">response</span><span class="p">)</span> <span class="cm">/* </span><span class="p">{{{</span> <span class="err">*/</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">zval</span> <span class="n">code</span><span class="p">,</span> <span class="n">retval</span><span class="p">,</span> <span class="n">retval_ptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">zval</span> <span class="n">callinfo</span><span class="p">,</span> <span class="o">*</span><span class="n">callback</span><span class="p">,</span> <span class="n">func_params</span><span class="p">[</span><span class="mi">3</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">zend_bool</span> <span class="n">bailout</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="n">params_count</span><span class="p">,</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">calldata</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 通过响应状态获取要执行的 PHP 回调函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="p">(</span><span class="n">status</span> <span class="o">==</span> <span class="n">YAR_ERR_OKEY</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">Z_ISUNDEF</span><span class="p">(</span><span class="n">calldata</span><span class="o">-&gt;</span><span class="n">callback</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">callback</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">calldata</span><span class="o">-&gt;</span><span class="n">callback</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">callback</span> <span class="o">=</span> <span class="nf">zend_read_static_property</span><span class="p">(</span><span class="n">yar_concurrent_client_ce</span><span class="p">,</span> <span class="nf">ZEND_STRL</span><span class="p">(</span><span class="s">&#34;_callback&#34;</span><span class="p">),</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="n">params_count</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">Z_ISUNDEF</span><span class="p">(</span><span class="n">calldata</span><span class="o">-&gt;</span><span class="n">ecallback</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">callback</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">calldata</span><span class="o">-&gt;</span><span class="n">ecallback</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">callback</span> <span class="o">=</span> <span class="nf">zend_read_static_property</span><span class="p">(</span><span class="n">yar_concurrent_client_ce</span><span class="p">,</span> <span class="nf">ZEND_STRL</span><span class="p">(</span><span class="s">&#34;_error_callback&#34;</span><span class="p">),</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="n">params_count</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// 没获取到回调函数就提示错误信息
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="p">(</span><span class="nf">Z_ISNULL_P</span><span class="p">(</span><span class="n">callback</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">status</span> <span class="o">!=</span> <span class="n">YAR_ERR_OKEY</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">Z_ISUNDEF</span><span class="p">(</span><span class="n">response</span><span class="o">-&gt;</span><span class="n">err</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nf">php_yar_client_handle_error</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">response</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nf">php_error_docref</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">E_WARNING</span><span class="p">,</span> <span class="s">&#34;[%d]:unknown Error&#34;</span><span class="p">,</span> <span class="n">status</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">Z_ISUNDEF</span><span class="p">(</span><span class="n">response</span><span class="o">-&gt;</span><span class="n">retval</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nf">zend_print_zval</span><span class="p">(</span><span class="o">&amp;</span><span class="n">response</span><span class="o">-&gt;</span><span class="n">retval</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">status</span> <span class="o">==</span> <span class="n">YAR_ERR_OKEY</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 响应状态是成功但是返回值为空，提示错误信息。
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">if</span> <span class="p">(</span><span class="nf">Z_ISUNDEF</span><span class="p">(</span><span class="n">response</span><span class="o">-&gt;</span><span class="n">retval</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nf">php_yar_client_trigger_error</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">YAR_ERR_REQUEST</span><span class="p">,</span> <span class="s">&#34;%s&#34;</span><span class="p">,</span> <span class="s">&#34;server responsed empty response&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 复制返回值
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">ZVAL_COPY</span><span class="p">(</span><span class="o">&amp;</span><span class="n">retval</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">response</span><span class="o">-&gt;</span><span class="n">retval</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 复制状态及错误信息
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">ZVAL_LONG</span><span class="p">(</span><span class="o">&amp;</span><span class="n">code</span><span class="p">,</span> <span class="n">status</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nf">ZVAL_COPY</span><span class="p">(</span><span class="o">&amp;</span><span class="n">retval</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">response</span><span class="o">-&gt;</span><span class="n">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// 初始化调用信息
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">array_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">callinfo</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nf">add_assoc_long_ex</span><span class="p">(</span><span class="o">&amp;</span><span class="n">callinfo</span><span class="p">,</span> <span class="s">&#34;sequence&#34;</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="s">&#34;sequence&#34;</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">calldata</span><span class="o">-&gt;</span><span class="n">sequence</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">add_assoc_str_ex</span><span class="p">(</span><span class="o">&amp;</span><span class="n">callinfo</span><span class="p">,</span> <span class="s">&#34;uri&#34;</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="s">&#34;uri&#34;</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="nf">zend_string_copy</span><span class="p">(</span><span class="n">calldata</span><span class="o">-&gt;</span><span class="n">uri</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="nf">add_assoc_str_ex</span><span class="p">(</span><span class="o">&amp;</span><span class="n">callinfo</span><span class="p">,</span> <span class="s">&#34;method&#34;</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="s">&#34;method&#34;</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="nf">zend_string_copy</span><span class="p">(</span><span class="n">calldata</span><span class="o">-&gt;</span><span class="n">method</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 调用数据为空则获取并行客户端的回调函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">callback</span> <span class="o">=</span> <span class="nf">zend_read_static_property</span><span class="p">(</span><span class="n">yar_concurrent_client_ce</span><span class="p">,</span> <span class="nf">ZEND_STRL</span><span class="p">(</span><span class="s">&#34;_callback&#34;</span><span class="p">),</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nf">Z_ISNULL_P</span><span class="p">(</span><span class="n">callback</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="n">params_count</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">calldata</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">status</span> <span class="o">!=</span> <span class="n">YAR_ERR_OKEY</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 调用失败
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">ZVAL_COPY_VALUE</span><span class="p">(</span><span class="o">&amp;</span><span class="n">func_params</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="o">&amp;</span><span class="n">code</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ZVAL_COPY_VALUE</span><span class="p">(</span><span class="o">&amp;</span><span class="n">func_params</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="o">&amp;</span><span class="n">retval</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ZVAL_COPY_VALUE</span><span class="p">(</span><span class="o">&amp;</span><span class="n">func_params</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="o">&amp;</span><span class="n">callinfo</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">calldata</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 调用成功
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">ZVAL_COPY_VALUE</span><span class="p">(</span><span class="o">&amp;</span><span class="n">func_params</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="o">&amp;</span><span class="n">retval</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ZVAL_COPY_VALUE</span><span class="p">(</span><span class="o">&amp;</span><span class="n">func_params</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="o">&amp;</span><span class="n">callinfo</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 调用数据为空
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">ZVAL_NULL</span><span class="p">(</span><span class="o">&amp;</span><span class="n">func_params</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ZVAL_NULL</span><span class="p">(</span><span class="o">&amp;</span><span class="n">func_params</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">zend_try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 执行回调函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="p">(</span><span class="nf">call_user_function</span><span class="p">(</span><span class="nf">EG</span><span class="p">(</span><span class="n">function_table</span><span class="p">),</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">callback</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">retval_ptr</span><span class="p">,</span> <span class="n">params_count</span><span class="p">,</span> <span class="n">func_params</span><span class="p">)</span> <span class="o">!=</span> <span class="n">SUCCESS</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">params_count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nf">zval_ptr_dtor</span><span class="p">(</span><span class="o">&amp;</span><span class="n">func_params</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>    
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">calldata</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nf">php_error_docref</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">E_WARNING</span><span class="p">,</span> <span class="s">&#34;call to callback failed for request: &#39;%s&#39;&#34;</span><span class="p">,</span> <span class="nf">ZSTR_VAL</span><span class="p">(</span><span class="n">calldata</span><span class="o">-&gt;</span><span class="n">method</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nf">php_error_docref</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">E_WARNING</span><span class="p">,</span> <span class="s">&#34;call to initial callback failed&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="n">zend_catch</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">bailout</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="nf">zend_end_try</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 释放返回值及参数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">Z_ISUNDEF</span><span class="p">(</span><span class="n">retval_ptr</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">zval_ptr_dtor</span><span class="p">(</span><span class="o">&amp;</span><span class="n">retval_ptr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">params_count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">zval_ptr_dtor</span><span class="p">(</span><span class="o">&amp;</span><span class="n">func_params</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>    
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">bailout</span><span class="o">?</span> <span class="mi">0</span> <span class="o">:</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>打印 retval 可以看到服务端的返回值：success。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">(gdb) p *retval.value.str.val@7
</span></span><span class="line"><span class="cl">$28 = &#34;success&#34;
</span></span></code></pre></div><h2 id="总结">总结</h2>
<p>本文介绍了实现并行调用相关的数据结构，并通过 GDB 调试代码的方式展示了并行调用运行的过程。</p>
]]></content:encoded>
    </item>
    <item>
      <title>PHP 源码阅读笔记：编译与调试 PHP</title>
      <link>https://her-cat.com/posts/2021/08/06/php-internals-compile-and-debugging/</link>
      <pubDate>Fri, 06 Aug 2021 18:52:15 +0800</pubDate>
      <guid>https://her-cat.com/posts/2021/08/06/php-internals-compile-and-debugging/</guid>
      <description>工欲善其事必先利其器。</description>
      <content:encoded><![CDATA[<h2 id="基本信息">基本信息</h2>
<ul>
<li>PHP 版本：7.1.0</li>
<li>调试环境：Ubuntu（WSL）</li>
<li>调试工具：GDB、Clion</li>
</ul>
<h2 id="编译-php">编译 PHP</h2>
<p>下载并安装 PHP：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ wget http://cn2.php.net/distributions/php-7.1.0.tar.gz
</span></span><span class="line"><span class="cl">$ tar -xzvf php-7.1.0.tar.gz
</span></span><span class="line"><span class="cl">$ <span class="nb">cd</span> php-7.1.0
</span></span><span class="line"><span class="cl">$ ./configure --prefix<span class="o">=</span><span class="nv">$HOME</span>/php-7.1.0/build --enable-fpm
</span></span></code></pre></div><p>注意：<code>$HOME/php-7.1.0/build</code> 是 PHP 执行文件和库文件安装的目录，可以自定义。<code>--enable-fpm</code> 表示同时安装 php-fpm。</p>
<p>接下来修改编译优化等级，方便我们在调试的过程中查看变量的内容。</p>
<p>首先在 <code>php-7.1.0/Makefile</code> 文件中搜索 <code>CFLAGS_CLEAN</code>，然后将 <code>-02</code> 改为 <code>-o0</code>，表示不需要优化。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">CFLAGS_CLEAN = -I/usr/include -g -O0 -fvisibility=hidden -DZEND_SIGNALS $(PROF_FLAGS)
</span></span></code></pre></div><p>执行 make 命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ make <span class="o">&amp;&amp;</span> make install
</span></span></code></pre></div><p>执行完成后，就可以在 <code>php-7.1.0/build/bin</code> 目录下看到 PHP 相关的可执行文件了。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">pear  peardev  pecl  phar  phar.phar  php  php-cgi  php-config  phpdbg  phpize
</span></span></code></pre></div><h2 id="调试-php">调试 PHP</h2>
<h3 id="使用-clion-进行调试">使用 Clion 进行调试</h3>
<p>首先用 Clion 打开 <code>php-7.1.0</code> 目录，Clion 会在根目录下自动创建 CMakeLists.txt 文件，复制下面的内容覆盖 CMakeLists.txt 中的内容：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">cmake_minimum_required(VERSION 3.10)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">project(php_7_1_0)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">set(CMAKE_CXX_STANDARD 14)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">set(PHP_SOURCE .)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">file(GLOB_RECURSE HEAD_FILES FOLLOW_SYMLINKS ${PHP_SOURCE})
</span></span><span class="line"><span class="cl">file(GLOB_RECURSE HEAD_FILES FOLLOW_SYMLINKS ${PHP_SOURCE}/ext)
</span></span><span class="line"><span class="cl">file(GLOB_RECURSE HEAD_FILES FOLLOW_SYMLINKS ${PHP_SOURCE}/Zend)
</span></span><span class="line"><span class="cl">file(GLOB_RECURSE HEAD_FILES FOLLOW_SYMLINKS ${PHP_SOURCE}/sapi)
</span></span><span class="line"><span class="cl">file(GLOB_RECURSE HEAD_FILES FOLLOW_SYMLINKS ${PHP_SOURCE}/pear)
</span></span><span class="line"><span class="cl">file(GLOB_RECURSE HEAD_FILES FOLLOW_SYMLINKS ${PHP_SOURCE}/TSRM)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">file(GLOB_RECURSE SRC_LIST FOLLOW_SYMLINKS
</span></span><span class="line"><span class="cl">        ${PHP_SOURCE}/*.c
</span></span><span class="line"><span class="cl">        ${PHP_SOURCE}/ext/*.c
</span></span><span class="line"><span class="cl">        ${PHP_SOURCE}/main/*.c
</span></span><span class="line"><span class="cl">        ${PHP_SOURCE}/Zend/*.c
</span></span><span class="line"><span class="cl">        ${PHP_SOURCE}/sapi/*.c
</span></span><span class="line"><span class="cl">        ${PHP_SOURCE}/pear/*.c
</span></span><span class="line"><span class="cl">        ${PHP_SOURCE}/TSRM/*.c
</span></span><span class="line"><span class="cl">)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">include_directories(${PHP_SOURCE})
</span></span><span class="line"><span class="cl">include_directories(${PHP_SOURCE}/ext)
</span></span><span class="line"><span class="cl">include_directories(${PHP_SOURCE}/main)
</span></span><span class="line"><span class="cl">include_directories(${PHP_SOURCE}/Zend)
</span></span><span class="line"><span class="cl">include_directories(${PHP_SOURCE}/sapi)
</span></span><span class="line"><span class="cl">include_directories(${PHP_SOURCE}/pear)
</span></span><span class="line"><span class="cl">include_directories(${PHP_SOURCE}/TSRM)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">add_executable(${PROJECT_NAME} ${SRC_LIST})
</span></span><span class="line"><span class="cl">add_custom_target(makefile COMMAND make &amp;&amp; make install WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
</span></span></code></pre></div><p>注意：<code>/home/ubuntu/php-7.1.0</code> 要替换成你自己的 PHP 所在的路径。</p>
<p>点击右上角绿色锤子旁边的下拉框 =&gt; Edit Configurations =&gt; makefile，按照下面的步骤进行操作：</p>
<ul>
<li><strong>Target：makefile</strong>（一般不用动）</li>
<li><strong>Executable</strong>：点击右边的 <code>...</code> 按钮，选择 php-7.1.0/bin/php 文件</li>
<li><strong>Program arguments</strong>：输入你要调试的 PHP 脚本的文件名，比如 index.php</li>
<li><strong>Working directory</strong>：选择上面 PHP 脚本所在的目录</li>
<li>删除 <strong>Before launch</strong> 中的 <strong>Build</strong>，避免每次调试都重新构建 PHP</li>
</ul>
<p>点击右上角绿色的小虫子或者使用快捷键 <code>Shift + F9</code> 开始调试</p>
<h3 id="使用-gdb-进行调试">使用 GDB 进行调试</h3>
<p>用来测试的 index.php ：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$a</span> <span class="o">=</span> <span class="s1">&#39;hello&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$a</span><span class="p">);</span>
</span></span></code></pre></div><p>开始 GDB 调试：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">gdb /php-7.1.0/build/bin/php
</span></span></code></pre></div><p>使用 b 命令在 var_dump 处打断点：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="o">(</span>gdb<span class="o">)</span> b zif_var_dump
</span></span><span class="line"><span class="cl">Breakpoint <span class="m">1</span> at 0x48f3bc: file /home/ubuntu/php-7.1.0/ext/standard/var.c, line 200.
</span></span></code></pre></div><p>为啥这里是 zif_var_dump？</p>
<p>因为 zif_var_dump 是 var_dump 函数的底层实现， 是由 PHP_FUNCITON(var_dump) 宏展开后的名称，所以直接搜 zif_var_dump 是搜不到的。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define ZEND_FN(name)   zif_##name
</span></span></span><span class="line"><span class="cl"><span class="cp">#define ZEND_NAMED_FUNCTION(name)   void name(INTERNAL_FUNCTION_PARAMETERS)
</span></span></span><span class="line"><span class="cl"><span class="cp">#define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name))
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cm">/* PHP-named Zend macro wrappers */</span>
</span></span><span class="line"><span class="cl"><span class="cp">#define PHP_FUNCTION			ZEND_FUNCTION
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="nf">PHP_FUNCTION</span><span class="p">(</span><span class="n">var_dump</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="n">zval</span> <span class="o">*</span><span class="n">args</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="kt">int</span> <span class="n">argc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="kt">int</span>	<span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="p">(</span><span class="nf">zend_parse_parameters</span><span class="p">(</span><span class="nf">ZEND_NUM_ARGS</span><span class="p">(),</span> <span class="s">&#34;+&#34;</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">args</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">argc</span><span class="p">)</span> <span class="o">==</span> <span class="n">FAILURE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">argc</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nf">php_var_dump</span><span class="p">(</span><span class="o">&amp;</span><span class="n">args</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>开始运行 PHP 文件：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="o">(</span>gdb<span class="o">)</span> r index.php
</span></span><span class="line"><span class="cl">Starting program: /home/ubuntu/php-7.1.0/build/bin/php index.php
</span></span><span class="line"><span class="cl"><span class="o">[</span>Thread debugging using libthread_db enabled<span class="o">]</span>
</span></span><span class="line"><span class="cl">Using host libthread_db library <span class="s2">&#34;/lib/x86_64-linux-gnu/libthread_db.so.1&#34;</span>.
</span></span><span class="line"><span class="cl">Breakpoint 1, zif_var_dump <span class="o">(</span><span class="nv">execute_data</span><span class="o">=</span>0x7ffffb6130d0, <span class="nv">return_value</span><span class="o">=</span>0x7ffffffea400<span class="o">)</span>
</span></span><span class="line"><span class="cl">    at /home/ubuntu/php-7.1.0/ext/standard/var.c:200
</span></span><span class="line"><span class="cl"><span class="m">200</span>     <span class="o">{</span>
</span></span></code></pre></div><p>代码执行到 zif_var_dump 就停止了，说明命中了断点。我们可以使用 l 命令查看附近的源码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="o">(</span>gdb<span class="o">)</span> l
</span></span><span class="line"><span class="cl"><span class="m">195</span>     /* <span class="o">}}}</span> */
</span></span><span class="line"><span class="cl"><span class="m">196</span>
</span></span><span class="line"><span class="cl"><span class="m">197</span>     /* <span class="o">{{{</span> proto void var_dump<span class="o">(</span>mixed var<span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="m">198</span>        Dumps a string representation of variable to output */
</span></span><span class="line"><span class="cl"><span class="m">199</span>     PHP_FUNCTION<span class="o">(</span>var_dump<span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="m">200</span>     <span class="o">{</span>
</span></span><span class="line"><span class="cl"><span class="m">201</span>             zval *args<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="m">202</span>             int argc<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="m">203</span>             int     i<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="m">204</span>
</span></span></code></pre></div><p>使用 bt 命令查看调用栈：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="o">(</span>gdb<span class="o">)</span> bt
</span></span><span class="line"><span class="cl"><span class="c1">#0  zif_var_dump (execute_data=0x7ffffb6130d0, return_value=0x7ffffffea400)</span>
</span></span><span class="line"><span class="cl">    at /home/ubuntu/php-7.1.0/ext/standard/var.c:200
</span></span><span class="line"><span class="cl"><span class="c1">#1  0x0000000008642151 in ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER ()</span>
</span></span><span class="line"><span class="cl">    at /home/ubuntu/php-7.1.0/Zend/zend_vm_execute.h:628
</span></span><span class="line"><span class="cl"><span class="c1">#2  0x000000000864038e in execute_ex (ex=0x7ffffb613030) at /home/ubuntu/php-7.1.0/Zend/zend_vm_execute.h:429</span>
</span></span><span class="line"><span class="cl"><span class="c1">#3  0x0000000008640d05 in zend_execute (op_array=0x7ffffb67e000, return_value=0x0)</span>
</span></span><span class="line"><span class="cl">    at /home/ubuntu/php-7.1.0/Zend/zend_vm_execute.h:474
</span></span><span class="line"><span class="cl"><span class="c1">#4  0x00000000085a2ca6 in zend_execute_scripts (type=8, retval=0x0, file_count=3)</span>
</span></span><span class="line"><span class="cl">    at /home/ubuntu/php-7.1.0/Zend/zend.c:1474
</span></span><span class="line"><span class="cl"><span class="c1">#5  0x00000000084eb1ce in php_execute_script (primary_file=0x7ffffffecd00) at /home/ubuntu/php-7.1.0/main/main.c:2533</span>
</span></span><span class="line"><span class="cl"><span class="c1">#6  0x0000000008743462 in do_cli (argc=2, argv=0x90c1ef0) at /home/ubuntu/php-7.1.0/sapi/cli/php_cli.c:990</span>
</span></span><span class="line"><span class="cl"><span class="c1">#7  0x000000000874483e in main (argc=2, argv=0x90c1ef0) at /home/ubuntu/php-7.1.0/sapi/cli/php_cli.c:1378</span>
</span></span></code></pre></div><p>使用 u 命令让代码运行到指定的行：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="o">(</span>gdb<span class="o">)</span> u <span class="m">209</span>
</span></span><span class="line"><span class="cl">zif_var_dump <span class="o">(</span><span class="nv">execute_data</span><span class="o">=</span>0x7ffffb6130d0, <span class="nv">return_value</span><span class="o">=</span>0x7ffffffea400<span class="o">)</span>
</span></span><span class="line"><span class="cl">    at /home/ubuntu/php-7.1.0/ext/standard/var.c:209
</span></span><span class="line"><span class="cl"><span class="m">209</span>             <span class="k">for</span> <span class="o">(</span><span class="nv">i</span> <span class="o">=</span> 0<span class="p">;</span> i &lt; argc<span class="p">;</span> i++<span class="o">)</span> <span class="o">{</span>
</span></span></code></pre></div><p>使用 p 命令打印变量 $a 的值：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="o">(</span>gdb<span class="o">)</span> p args<span class="o">[</span>0<span class="o">]</span>.value.str.val
</span></span><span class="line"><span class="cl"><span class="nv">$1</span> <span class="o">=</span> <span class="s2">&#34;h&#34;</span>
</span></span></code></pre></div><p>使用 @ 指定输出长度：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="o">(</span>gdb<span class="o">)</span> p args<span class="o">[</span>0<span class="o">]</span>.value.str.val@5
</span></span><span class="line"><span class="cl"><span class="nv">$2</span> <span class="o">=</span> <span class="o">{</span><span class="s2">&#34;h&#34;</span>, <span class="s2">&#34;e&#34;</span>, <span class="s2">&#34;l&#34;</span>, <span class="s2">&#34;l&#34;</span>, <span class="s2">&#34;o&#34;</span><span class="o">}</span>
</span></span></code></pre></div>]]></content:encoded>
    </item><follow_challenge>
      <feedId>58021783493571598</feedId>
      <userId>56882619875632128</userId>
    </follow_challenge>
  </channel>
</rss>
