1 Star2 Stars3 Stars4 Stars5 Stars (还没有评分)
Loading...

使用DTrace调试进程不能收到信号的问题

前几天,同事提到商用系统上出现过使用kill命令给进程发送SIGKILL信号,进程无法退出的问题。尽管听起来有些不可思议,但是的确发生了。由于当时的环境没有保存下来,所以现在没法debug。我考虑了一下,如果再发生,可以使用DTrace去检查一下问题出现在哪里。

kill命令给进程发送信号可以分成3个步骤:
(1)kill命令成功发送信号;
(2)进程收到信号;
(3)进程开始处理信号。
我们可以用DTrace检查步骤(1)和(2),如果能证明进程的确收到信号,那么十之八九就是程序逻辑的问题了。

首先要做的是检查步骤(1),这个可以使用Brendan Gregg的DTraceToolkit里的kill.d脚本来检查:

dtrace:::BEGIN
{
    /* Print header */
    printf("%5s %12s %5s %-6s %s\n",
        "FROM", "COMMAND", "SIG", "TO", "RESULT");
}

syscall::kill:entry
{
    /* Record target PID and signal */
    self->target = arg0;
    self->signal = arg1;
}

syscall::kill:return
{
    /* Print source, target, and result */
    printf("%5d %12s %5d %-6d %d\n",
        pid, execname, self->signal, self->target, (int)arg0);

    /* Cleanup memory */
    self->target = 0;
    self->signal = 0;
}

脚本比较简单,就是检查整个系统里所有运行进程发送的kill命令是否成功。

接着检查步骤(2),可以用proc providersignal-handle probe。这个probe会在进程处理信号之前触发,所以可以用来检查进程是否收到了信号。我写了个简单的脚本catch.d

proc:::signal-handle
/pid == $target/
{
        trace(args[0]);
}

args[0]是收到的信号值。

接下来写个简单的测试程序,测试一下:

#include <stdio.h>
#include <signal.h>

void handler(int sig);

void handler(int sig)
{
        signal(sig, handler);
        printf("Receive signal: %d\n", sig);
}

int main(void) {
        // your code goes here
        signal(SIGHUP, SIG_IGN);
        signal(SIGINT, handler);

        while (1)
        {
                sleep(1);
        }
        return 0;
}

编译运行,用psig命令查看一下进程对信号的处理:

bash-3.2# psig 13297
13297: ./test
HUP ignored
INT caught handler RESETHAND,NODEFER
QUIT default

可以看到程序对SIGHUPSIGINTSIGQUIT处理分别是ignorecatchdefault,这样就覆盖了UNIX系统程序对信号处理的3种方式。

接下来分别在不同终端启动kill.dcatch.d。然后再在一个终端连续发送3个信号给测试程序:

bash-3.2# kill -HUP 13297
bash-3.2# kill -INT 13297
bash-3.2# kill -QUIT 13297 

再检查kill.d脚本输出:

bash-3.2# ./kill.d
FROM COMMAND SIG TO RESULT
18198 bash    1 13297 0
18198 bash    2 13297 0
18198 bash    3 13297 0

可以看到信号都已经成功发送。

最后检查catch.d脚本输出:

bash-3.2# ./catch.d -p 13297
dtrace: script './catch.d' matched 1 probe
CPU ID   FUNCTION:NAME
 14 1163 psig:signal-handle 2
 15 1163 psig:signal-handle 3
dtrace: pid 13297 has exited

可以看到进程也收到了信号。

所以如果下次商用系统再出现类似问题,就可以用这两个脚本分析了。

2 thoughts on “使用DTrace调试进程不能收到信号的问题

  1. Jinzhi Chen

    普通进程没法屏蔽SIGKILL,但是内核代码可以。有可能是程序的某个系统调用时,内核态代码屏蔽了信号处理,然后等待某些条件被满足(网络或者IO)。也许可以通过Dtrace脚本查看应用程序的相关代码路径或状态来定位问题。

    Reply
    1. nanxiao

      Jinzhi:你提到的这一点我之前的确没有了解,下次出现时可以考虑一下。非常感谢你的指点!

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *