LV095-gdb-信号处理
一、信号简介
1. linux 下的信号
$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX每个信号代表着不同的含义,以 SIGINT 信号为例,它表示程序停止执行,该信号可以通过按 Ctrl+c 组合键发出。换句话说,对于正在执行的程序,通过按 Ctrl+c 键向程序发出 SIGINT 信号,可以使程序停止执行。
2. GDB info handles 命令
GDB 调试器提供了 info signals 指令,用于查看 GDB 可以处理的信号种类,以及各个信号的具体处理方式。例如:
(gdb) info signals
Signal Stop Print Pass to program Description
SIGHUP Yes Yes Yes Hangup
SIGINT Yes Yes No Interrupt
SIGQUIT Yes Yes Yes Quit
SIGILL Yes Yes Yes Illegal instruction
SIGTRAP Yes Yes No Trace/breakpoint trap
SIGABRT Yes Yes Yes Aborted
SIGEMT Yes Yes Yes Emulation trap
SIGFPE Yes Yes Yes Arithmetic exception
SIGKILL Yes Yes Yes Killed
# ...其中各列的含义分别为:
- Signal:各个信号的名称;
- Stop:当信号发生时,是否终止程序执行。Yes 表示终止,No 表示当信号发生时程序认可继续执行;
- Print:当信号发生时,是否要求 GDB 打印出一条提示信息。Yes 表示打印,No 表示不打印;
- Pass:当信号发生时,该信号是否对程序可见。Yes 表示程序可以捕捉到该信息,No 表示程序不会捕捉到该信息;
- Description:对信号所表示含义的简单描述。
显然,对于现有的所有信号,GDB 调试器会根据 Stop、Print 以及 Pass 列的值进行相应的处理。当然,GDB 调试器提供了 handle 命令,由此我们就可以通过修改目标信号 Stop、Print、Pass 列的值,调试 GDB 调试器对目标信号的处理方式。
二、GDB handle命令
1. 语法格式
handle 命令的语法格式如下:
(gdb) handle signal mode其中,signal 参数表示要设定的目标信号,它通常为某个信号的全名(SIGINT)或者简称(去除‘SIG’后的部分,如 INT);如果要指定所有信号,可以用 all 表示。
mode 参数用于明确 GDB 处理该目标信息的方式,其值可以是如下几个:
- nostop:当信号发生时,GDB 不会暂停程序,其可以继续执行,但会打印出一条提示信息,告诉我们信号已经发生;
- stop:当信号发生时,GDB 会暂停程序执行。
- noprint:当信号发生时,GDB 不会打印出任何提示信息;
- print:当信号发生时,GDB 会打印出必要的提示信息;
- nopass(或者 ignore):GDB 捕获目标信号的同时,不允许程序自行处理该信号;
- pass(或者 noignore):GDB 调试在捕获目标信号的同时,也允许程序自动处理该信号。
注意,当 GDB 捕获到信号并暂停程序执行的那一刻,程序是捕获不到信号的,只有等到程序继续执行时,信号才能被程序捕获。
2. 使用实例
2.1 测试程序
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void display() {
printf("hello world\n");
}
int main(int argc, const char *argv[]) {
signal(SIGINT, display);
while (1) {
sleep(1);
printf("main\n");
}
return 0;
}SIGINT 信号是可以通过Ctrl+c组合键发出的,因此上面程序执行时,除非我们手动发出 SIGINT 信号,程序会马上执行 display() 函数,否则一直输出 "main" 字符串。我们在 ubuntu 中测试,所以直接用下面的命令编译:
cd ~/workspace/c-learning/02-c-basic/21-debug
gcc 042-gdb-signal.c -g2.2 调试示例
✓ sumu@virtual-machine:~/workspace/c-learning/02-c-basic/21-debug [main ≡ +1 ~0 -0 !]
$ gdb ./a.out -q
Reading symbols from ./a.out...
(gdb) info signals SIGINT # <=== 1. 查看 SIGINT 信号信息
Signal Stop Print Pass to program Description
SIGINT Yes Yes No Interrupt
(gdb) r # <=== 2. 运行程序
Starting program: /home/sumu/workspace/c-learning/02-c-basic/21-debug/a.out
main
main
^C # <=== 3. 按下Ctrl+c
Program received signal SIGINT, Interrupt.
0x00007ffff7e9d1b4 in __GI___clock_nanosleep (clock_id=<optimized out>, clock_id@entry=0,
flags=flags@entry=0, req=req@entry=0x7fffffffcce0, rem=rem@entry=0x7fffffffcce0)
at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
78 ../sysdeps/unix/sysv/linux/clock_nanosleep.c: 没有那个文件或目录.
(gdb)可以看到,通过执行info signals SIGINT命令,我们调取出了当前 GDB 调试器对 SIGINT 信号处理方式的默认设定,即当 SIGINT 信号发生时,GDB 调试器会暂停程序执行,同时打印出必要的提示信息,并且不让程序捕获到该信号。由此,当程序执行过程中按下Ctrl+c组合键后,并没有执行 display() 函数,而是立即暂停了程序。
在此基础上,我们继续做如下调试:
main
^C # <=== 3. 按下Ctrl+c
Program received signal SIGINT, Interrupt.
0x00007ffff7e9d1b4 in __GI___clock_nanosleep (clock_id=<optimized out>, clock_id@entry=0,
flags=flags@entry=0, req=req@entry=0x7fffffffcce0, rem=rem@entry=0x7fffffffcce0)
at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
78 ../sysdeps/unix/sysv/linux/clock_nanosleep.c: 没有那个文件或目录.
(gdb) handle SIGINT nostop # <=== 4. 将 SIGINT 信号 Stop 选项改为 No
SIGINT is used by the debugger.
Are you sure you want to change it? (y or n) y
Signal Stop Print Pass to program Description
SIGINT No Yes No Interrupt
(gdb) handle SIGINT pass # <=== 5. 将 SIGINT 信号 Pass 选项改为 Yes
SIGINT is used by the debugger.
Are you sure you want to change it? (y or n) y
Signal Stop Print Pass to program Description
SIGINT No Yes Yes Interrupt
(gdb) c
Continuing.
hello world
main
main
main
main
main
main
^C
Program received signal SIGINT, Interrupt.
hello world
main
main
main
main
main
^Z
Program received signal SIGTSTP, Stopped (user).
0x00007ffff7e9d1b4 in __GI___clock_nanosleep (clock_id=<optimized out>, clock_id@entry=0,
flags=flags@entry=0, req=req@entry=0x7fffffffcce0, rem=rem@entry=0x7fffffffcce0)
at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
78 in ../sysdeps/unix/sysv/linux/clock_nanosleep.c
(gdb)通过将 SIGINT 信号 Stop 选项改为 No,将 Pass 选项改为 Yes,意味着当程序执行过程中发生 SIGINT 信号时,程序不再中断,并且可以捕获到此信号。因此,当程序执行过程中按下Ctrl+c组合键时,看到了 hello world 被打印出来。