/* * In POSIX a signal is sent either to a specific thread (Linux task) * or to the process as a whole (Linux thread group). How the signal * is sent determines whether it's to one thread or the whole group, * which determines which signal mask(s) are involved in blocking it * from being delivered until later. When the signal is delivered, * either it's caught or ignored by a user handler or it has a default * effect that applies to the whole thread group (POSIX process). * * The possible effects an unblocked signal set to SIG_DFL can have are: * ignore - Nothing Happens * terminate - kill the process, i.e. all threads in the group, * similar to exit_group. The group leader (only) reports * WIFSIGNALED status to its parent. * coredump - write a core dump file describing all threads using * the same mm and then kill all those threads * stop - stop all the threads in the group, i.e. TASK_STOPPED state * * SIGKILL and SIGSTOP cannot be caught, blocked, or ignored. * Other signals when not blocked and set to SIG_DFL behaves as follows. * The job control signals also have other special effects. * * +--------------------+------------------+ * | POSIX signal | default action | * +--------------------+------------------+ * | SIGHUP | terminate | * | SIGINT | terminate | * | SIGQUIT | coredump | * | SIGILL | coredump | * | SIGTRAP | coredump | * | SIGABRT/SIGIOT | coredump | * | SIGBUS | coredump | * | SIGFPE | coredump | * | SIGKILL | terminate(+) | * | SIGUSR1 | terminate | * | SIGSEGV | coredump | * | SIGUSR2 | terminate | * | SIGPIPE | terminate | * | SIGALRM | terminate | * | SIGTERM | terminate | * | SIGCHLD | ignore | * | SIGCONT | ignore(*) | * | SIGSTOP | stop(*)(+) | * | SIGTSTP | stop(*) | * | SIGTTIN | stop(*) | * | SIGTTOU | stop(*) | * | SIGURG | ignore | * | SIGXCPU | coredump | * | SIGXFSZ | coredump | * | SIGVTALRM | terminate | * | SIGPROF | terminate | * | SIGPOLL/SIGIO | terminate | * | SIGSYS/SIGUNUSED | coredump | * | SIGSTKFLT | terminate | * | SIGWINCH | ignore | * | SIGPWR | terminate | * | SIGRTMIN-SIGRTMAX | terminate | * +--------------------+------------------+ * | non-POSIX signal | default action | * +--------------------+------------------+ * | SIGEMT | coredump | * +--------------------+------------------+ * * (+) For SIGKILL and SIGSTOP the action is "always", not just "default". * (*) Special job control effects: * When SIGCONT is sent, it resumes the process (all threads in the group) * from TASK_STOPPED state and also clears any pending/queued stop signals * (any of those marked with "stop(*)"). This happens regardless of blocking, * catching, or ignoring SIGCONT. When any stop signal is sent, it clears * any pending/queued SIGCONT signals; this happens regardless of blocking, * catching, or ignored the stop signal, though (except for SIGSTOP) the * default action of stopping the process may happen later or never. */
RETURN VALUE signal() returns the previous value of the signal handler, or SIG_ERR on error. In the event of an error, errno is set to indicate the cause.
[muxue@bt-7274:~/git/linux/code/22-11-16_signal]$ ./tsig 进程信号已经设置完了 process running: 7779 process running: 7779 process running: 7779 process running: 7779 process running: 7779
[muxue@bt-7274:~/git/linux/code/22-11-16_signal]$ ./tsig 进程信号已经设置完了 process running: 5846 process running: 5846 process 5846 get signal: 15 process running: 5846 process running: 5846
2.3 raise
这个系统接口的作用是给自己发信号
1 2
#include<signal.h> intraise(int sig);
返回0代表调用成功,非0代表失败
1 2
RETURN VALUE raise() returns 0 on success, and nonzero for failure.
[muxue@bt-7274:~/git/linux/code/22-11-16_signal]$ ./tsig 进程信号已经设置完了 process get signal: 2 process get signal: 2 process get signal: 2 process get signal: 2 process get signal: 2
2.4 abort
向自己发送6) SIGABRT信号
1 2
#include<stdlib.h> voidabort(void);
还是2.3中的代码,将raise(2)修改为abort(),同时捕捉6号信号。
此时能观察到我们自己写的handler方法的确被调用了,但是进程依旧终止了
1 2 3 4 5
[muxue@bt-7274:~/git/linux/code/22-11-16_signal]$ ./tsig 进程信号已经设置完了 process get signal: 6 Aborted [muxue@bt-7274:~/git/linux/code/22-11-16_signal]$
RETURN VALUE alarm() returns the number of seconds remaining until any previously scheduled alarm was due to be delivered, or zero if there was no previously scheduled alarm.
[muxue@bt-7274:~/git/linux/code/22-11-16_signal]$ ./tsig 进程信号已经设置完了 set alarm, sleep process get signal: 14 sleep finish [muxue@bt-7274:~/git/linux/code/22-11-16_signal]$
如果我们不对14号信号自定义捕捉,则会直接退出进程
1 2 3 4
[muxue@bt-7274:~/git/linux/code/22-11-16_signal]$ ./tsig set alarm, sleep Alarm clock [muxue@bt-7274:~/git/linux/code/22-11-16_signal]$
SIG_BLOCK The set of blocked signals is the union of the current set and the set argument.
SIG_UNBLOCK The signals in set are removed from the current set of blocked signals. It is permissible to attempt to unblock a signal which is not blocked.
SIG_SETMASK The set of blocked signals is set to the argument set. If oldset is non-NULL, the previous value of the signal mask is stored in oldset.
RETURN VALUE sigprocmask() returns 0 on success and -1 on error. In the event of an error, errno is set to indicate the cause.
[muxue@bt-7274:~/git/linux/code/22-11-16_signal]$ ./tsig process running: 15029 process running: 15029 ^Cprocess 15029 get signal: 2 process running: 15029 process running: 15029 ^Cprocess 15029 get signal: 2 process running: 15029 process running: 15029 process running: 15029 ^\Quit [muxue@bt-7274:~/git/linux/code/22-11-16_signal]$
voidTestSignal() { //对所有的进程信号都设置一个回调 for (int sig = 1; sig <= 31; sig++) { signal(sig, handler); } cout << "进程信号已经设置完了" << endl; sleep(3); }
intmain(int argc, char *argv[]) { TestSignal(); int a = 10; int b = 0; try { int c = a / b; // C++的除0不是异常,不会抛出 //所以会直接linux系统运行报错 } catch (const exception &e) { cerr << "a/0 err" << endl; abort(); } catch (...) { cout << "base catch" << endl; abort(); } return0; }
运行了之后,该进程会一直收到8号信号,直到我们手动kill掉这个进程
1 2 3 4 5 6
process 3947 get signal: 8 process 3947 get signal: 8 process 3947 get signal: 8 process 3947 get signal: 8 process 3947 get signal: 8 process 3947 get signal: 8Killed
intmain(int argc, char *argv[]) { int status; int id = fork(); if(id == 0) { //子进程 int b=0; int a = 10/b; } int ret = waitpid(id,&status,0); //打印子进程的退出信息 printf("exitcode:%d signo:%d coredump: %d\n",(status>>8)&&0xff,status&0x7f,(status>>7)&0x1);
[muxue@bt-7274:~/git/linux/code/22-11-16_signal]$ gdb test GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7 Copyright (C) 2013 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty"for details. This GDB was configured as "x86_64-redhat-linux-gnu". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>... Reading symbols from /home/muxue/git/linux/code/22-11-16_signal/test...done. (gdb) core-file core.31997 [New LWP 31997]
Core was generated by `./test'. Program terminated with signal 8, Arithmetic exception. #0 0x000000000040065c in main () at test.cc:13 13 int a=10/0; Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.x86_64 libgcc-4.8.5-44.el7.x86_64 (gdb) (gdb)
这可比我们手动debug找错误方便多了
4.3 为什么默认关闭?
你可能会觉得,这个功能不挺好的吗,为啥默认没有开启呢?
先来看看这个文件的大小,足足有580KB
1 2 3 4
[muxue@bt-7274:~/git/linux/code/22-11-16_signal]$ ls -lht total 292K -rw-rw-r-- 1 muxue muxue 1.8K Nov 20 12:37 tsignal.cpp -rw------- 1 muxue muxue 580K Nov 20 12:34 core.27908
[muxue@bt-7274:~/git/linux/code/22-11-21_volatile]$ gcc test.c -o test -O2 [muxue@bt-7274:~/git/linux/code/22-11-21_volatile]$ ./test process start 23086 ^C 更改flag: 0->1 process exit! [muxue@bt-7274:~/git/linux/code/22-11-21_volatile]$
去掉gcc编译器的优化参数,去掉volatile关键字,会发现进程也能正常退出
1 2 3 4 5 6 7
[muxue@bt-7274:~/git/linux/code/22-11-21_volatile]$ gcc test.c -o test [muxue@bt-7274:~/git/linux/code/22-11-21_volatile]$ ./test process start 23224 ^C 更改flag: 0->1 process exit! [muxue@bt-7274:~/git/linux/code/22-11-21_volatile]$
这就是编译器优化不同的影响!加上volatile关键字能避免这个问题,使代码运行能有唯一结果!
8.子进程发送信号
当子进程的状态变化的时候,会向父进程发送17号信号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
voidtestfork() { int status; int id = fork(); if(id == 0) { //子进程 cout << "chlid process: " <<getpid()<<endl; int b=0; int a = 10/b; } TestSignal(); int ret = waitpid(id,&status,0); //打印子进程的退出信息 printf("exitcode:%d signo:%d coredump: %d\n",(status>>8)&&0xff,status&0x7f,(status>>7)&0x1); }
观察结果,可以看到父进程收到了子进程的17号信号,此时子进程因为错误退出
1 2 3 4 5 6
[muxue@bt-7274:~/git/linux/code/22-11-16_signal]$ ./tsig 进程信号已经设置完了 chlid process: 25319 process 25318 get signal: 17 exitcode:0 signo:8 coredump: 0 [muxue@bt-7274:~/git/linux/code/22-11-16_signal]$