为什么在附加到被追踪进程时,GDB可以屏蔽追踪进程的SIGKILL信号?

4
signal(7)页面上说,SIGKILL不能被捕获、阻塞或忽略。但是我刚刚观察到,在使用GDB附加到进程后,我无法再向该进程发送SIGKILL(同样,其他信号也不能传递)。但是在我解除附加并退出GDB后,SIGKILL会像往常一样被发送。
在我看来,当GDB附加时,它代表追踪者阻止了该信号(代表被追踪者),并在解除附加后启用了该信号。但是,ptrace(2)中说道:

在被跟踪时,即使忽略了信号,被跟踪者也会停止每次发送信号。 (一个例外是SIGKILL,它具有其通常的效果。)

那么为什么会这样呢?GDB使用了哪些技巧?
以下是一个简单的演示例子:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <string.h>

/* Simple error handling functions */

#define handle_error_en(en, msg) \
    do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)

struct sigaction act;

void sighandler(int signum, siginfo_t *info, void *ptr) {
    printf("Received signal: %d\n", signum);
    printf("signal originate from pid[%d]\n", info->si_pid);
}

int
main(int argc, char *argv[])
{
    printf("Pid of the current process: %d\n", getpid());

    memset(&act, 0, sizeof(act));

    act.sa_sigaction = sighandler;
    act.sa_flags = SA_SIGINFO;

    sigaction(SIGQUIT, &act, NULL);

    while(1) {
        ;
    }

    return 0;
}

如果您尝试使用SIGKILL(即使用kill -KILL ${pid})杀死该程序,它将按预期死亡。 如果您尝试发送SIGQUIT(即使用kill -QUIT ${pid}),则会执行那些printf语句,如预期的那样。 但是,如果在发送信号之前使用GDB附加了它,则什么也不会发生:

$ ##### in shell 1 #####
$ gdb
(gdb) attach ${pid}
(gdb)

/* 现在gdb已经成功附加,打开另一个shell: */
$ #### in shell 2 ####
$ kill -QUIT ${pid}    # nothing happen
$ kill -KILL ${pid}    # again, nothing happen!

/* 现在 gdb 已分离 */

##### in shell 1 ####
(gdb) quit

/* 进程将会收到 SIGKILL 信号 */

##### in shell 2 ####
$ Killed    # the tracee receive **SIGKILL** eventually...

提供信息,我正在使用CentOS-6u3并且uname -r的结果是2.6.32_1-16-0-0。我的GDB版本是:GNU gdb (GDB) Red Hat Enterprise Linux (7.2-56.el6),我的GCC版本是:gcc (GCC) 3.4.6 20060404 (Red Hat 3.4.6-19.el6)。这是一台旧机器...

任何想法将不胜感激 ;-)


“eventually”指的是gdb分离和shell报告进程被杀死之间存在一些延迟吗? - Mark Plotnick
我相当确定GDB没有使用任何技巧。你可能有一个损坏的内核。我怀疑你的结果在不同的系统上无法复现。 - Employed Russian
@EmployedRussian 我正在使用ptrace(2)实现类似GDB的功能,但信号处理变得混乱,因为每次被跟踪的进程收到信号时,它会被中断并且跟踪进程会受到虚假通知...所以我认为GDB在这里使用了一些技巧。你为什么那么确定?能否澄清一下? - walkerlala
@MarkPlotnick 没有延迟。在 gdb 分离和 shell 报告“killed”之间没有延迟。但我还没有实际测量过,所以很难说是否有延迟... - walkerlala
@EmployedRussian 我在使用4.4.0内核(Ubuntu 16.04 LTS)尝试了这个方法,但是结果还是一样,gdb仍然会屏蔽那些信号... - walkerlala
哦,我明白了。答案马上就来… - Employed Russian
1个回答

3

$ ##### in shell 1 ##### $ gdb (gdb) attach ${pid} (gdb)

问题在于一旦GDB附加到${pid},被调试的进程就不再运行,它被停止了

内核将不会对其进行任何操作,直到它被继续执行(使用(gdb) continue命令)或者不再被跟踪((gdb) detachquit命令)。

如果你在kill -QUIT之前或之后发出continue命令,你会看到这个:

(gdb) c
Continuing.

在另一个shell中执行了kill -QUIT $pid
Program received signal SIGQUIT, Quit.
main (argc=1, argv=0x7ffdcc9c1518) at t.c:35
35      }

(gdb) c
Continuing.
Received signal: 3
signal originate from pid[123419]

在另一个窗口执行了kill -KILL
Program terminated with signal SIGKILL, Killed.
The program no longer exists.
(gdb)

你知道GDB使用哪个信号来实现控制转移吗?所谓的控制转移是指,在被跟踪程序执行完一个函数并返回后,生成了哪个信号,以便GDB可以在其上等待()并再次掌控?虽然许多人声称不是SIGTRAP... - walkerlala
@walkerlala 这可能最好作为一个单独的问题提出。我知道,但是这个评论栏太短了;-) - Employed Russian
请看这里:https://stackoverflow.com/questions/47503036/what-signal-does-gdb-use-to-implement-control-transfer-between-tracee-and-tracer - walkerlala

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接