<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>fightForPG</title>
    <link>https://ruby-china.org/fightForPG</link>
    <description/>
    <language>en-us</language>
    <item>
      <title>调用 DLL 接口，接口里面 C 代码的 recv 函数返回了中断错误</title>
      <description>&lt;p&gt;项目里面调用 DLL 的接口，接口里面的 C 代码建立了与底层驱动程序的 socket 连接，发送一个取数据的请求以后，用 recv 函数等待返回值，在正确的值返回以前，发生了中断，recv 函数返回了 EINTR 的错误 errno(中断之后的 log 显示，正确的驱动结果值返回了，只不过 recv 还没等到，就被别的信号还是线程调度中断了)，在项目代码跟系统版本（CentOS7.6）不变的情况下（最开始是 Ruby2.6.5 出的错），用各个 Ruby 版本实验，将罪魁祸首定位，就是下面这个变更导致的：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hijack SIGCHLD handler for internal use 

Use a global SIGCHLD handler to guard all callers of rb_waitpid.
To work safely with multi-threaded programs, we introduce a
VM-wide waitpid_lock to be acquired BEFORE fork/vfork spawns the
process.  This is to be combined with the new ruby_waitpid_locked
function used by mjit.c in a non-Ruby thread.

Ruby-level SIGCHLD handlers registered with Signal.trap(:CHLD)
continues to work as before and there should be no regressions
in any existing use cases.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://github.com/ruby/ruby/commit/054a412d540e7ed2de63d68da753f585ea6616c3" rel="nofollow" target="_blank" title=""&gt;超级链接&lt;/a&gt;
（ruby2.6.0-preview2 和 ruby2.6.0preview3 之间的途中开发版）&lt;/p&gt;

&lt;p&gt;这里面针对 SIGCHLD 信号的默认处理函数做了修正，原来是默认忽略信号 (原来是 SIGCHLD 信号的 SIG_DFL 信号处理函数，应该是忽略)，现在是无论怎样，都会去执行它新增加的一个信号处理函数，这个函数里面其实也没做啥，就是把一个标志 sigchld_hit 设置为 1，然后在 timer_thread(处于等待 GIL 锁的等待线程会唤醒 timer_thread，去给当前持有 GIL 锁的线程置一个中断位，&lt;strong&gt;而并不执行中断&lt;/strong&gt;，具体的可以参照站里这篇文章：&lt;a href="https://ruby-china.org/topics/28415" title=""&gt;超级链接&lt;/a&gt;) 每隔 0.1S 去给 GIL 上锁的时候，检查下 sigchld_hit 是否为 1，如果为 1，就将&amp;amp;vm-&amp;gt;waiting_pids 或者&amp;amp;vm-&amp;gt;waiting_grps 遍历，每个 pid 都做 waitpid 的操作 (no-blocking，没等到对象 pid 进程结束，就返回 0)，如果等到其中一个结束了，就把条件变量 cond 解除 blocking 的信号发出去，激发阻塞在条件变量 cond 上而挂起的众多线程，当中的一个进程（这个线程也在&amp;amp;vm-&amp;gt;waiting_pids 或&amp;amp;vm-&amp;gt;waiting_grps 里）。---&amp;gt;这个过程会每个进程都做，激发动作不会打断这个循环&lt;/p&gt;

&lt;p&gt;而阻塞在条件变量 cond 的方式有两个：一个是 mjit.c 里面的 exec_process（for no-Ruby level thread），它调用了 ruby_waitpid_locked（pid...），如果当前 pid 线程并没有结束，里面用 rb_native_cond_wait(w.cond, &amp;amp;vm-&amp;gt;waitpid_lock);挂起了当前线程，这个也是这次变更新加进去的 (ruby_waitpid_locked 函数)，还有一个就是诸如 Process.wait/Process.waitall/Process.detach/system('') 在调用时候，会调用 waitpid(WNOHANG)：非阻塞 waitpid，但是如果 pid&amp;lt;0并且&amp;amp;vm-&amp;gt;waiting_pids 里面有等待的 pid 的时候，同样要进入等待条件变量 cond 的解除信号，例如：Process.waitpid(-1,&amp;amp;status,0)。&lt;/p&gt;

&lt;p&gt;综上所述，我觉得是隔 0.1S 一执行的 timer thread 里面增加了对 wait_pid 中是否结束的检查，如果结束了，就解除 cond，唤醒一个挂起线程，造成对 DLL 侧等待结果 recv 函数的中断，而增加的这个检查必须是执行新的 SIGCHLD 信号处理才有效，而且，我在 recv 函数处加了如果出现 EINTR 错误，那么就再次执行 recv 的 retry 处理，并且加了 retry 时候的 log，发现有一次，打印出了一连串的 retry log，而且每个 retry 间隔是 0.1S，仿佛更印证了我的猜测，（但是。。。）
所以我尝试了在 Ruby 源码里面（signal.c）里面把默认的处理函数改回忽略这个信号，但是程序执行的时候，发生了某些进程无故中断（抑或是长时间的等待？）的现象，后来我一分析，觉得如果我变了处理函数，忽略这个信号，那么阻塞在条件变量 cond，挂起的进程就没有办法唤醒了，应该是不行，那我看到这次变更的说明里面有---&amp;gt;Ruby-level SIGCHLD handlers registered with Signal.trap(:CHLD) continues to work as before and there should be no regressions in any existing use cases.，意思也就是说 Signal.trap(:CHLD) 的动作不会受影响，我就尝试着在 Ruby 端，也就是项目的服务启动时候（尝试全局修改 CHLD 的信号处理函数），或者是在我认为有可能跟中断有关系的子进程在启动之前（尝试只影响当前要启动的子进程的 CHLD 信号处理），去把信号处理函数设置为忽略信号，都没有起作用。还是出 EINTR 的错误。&lt;/p&gt;

&lt;p&gt;所以，我的问题是：
①我怎么才能将 CHLD 和 CLD 信号的处理函数在项目的 Ruby 代码里面改成忽略信号（SIG_IGN），也就是下面这个代码应该放在哪儿能起作用？&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"CHLD"&lt;/span&gt;&lt;span class="err"&gt;，&lt;/span&gt;&lt;span class="s2"&gt;"IGNORE"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;Signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"CLD"&lt;/span&gt;&lt;span class="err"&gt;，&lt;/span&gt;&lt;span class="s2"&gt;"IGNORE"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;②还是我对这个变更的理解还是有问题，代码的流程理解有误，并没有找到 EINTR 发生的根本原因，希望大神们指正~~~~&lt;/p&gt;

&lt;p&gt;希望有大神帮小可一忙，再次感谢！！！
PS：还有一点忘了记上去了，最后还有一种可能是我并没有正确的用好 Ruby，所以也就没有出来 Ruby 源代码里面处理 SIGCHLD 信号所预期的那个样子，如果是这样，这个问题就变得更困难了，就是这个变更本身是正确的，是我哪些不正确的利用，原来的 Ruby 版本"容错"掉了它，但现在，这个错误暴露了出来~~~如果大神们看完这个变更觉得人家没有问题，也请狠狠地给我指出来，羞愧我一下。。&lt;/p&gt;</description>
      <author>fightForPG</author>
      <pubDate>Tue, 28 Apr 2020 13:10:28 +0800</pubDate>
      <link>https://ruby-china.org/topics/39810</link>
      <guid>https://ruby-china.org/topics/39810</guid>
    </item>
  </channel>
</rss>
