进程信号
# 信号基础
使用
kill -l
命令列出所有信号
[cen@VM-4-9-opencloudos lesson03-进程通信]$ 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
# [1, 31]:普通信号
# [32, 64]:实时信号
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
信号的记录
进程接收到信号时可能有更重要的代码,所以信号不一定被立即处理,并且进程要有对信号的保存能力
【保存在task_struct中,用位图来保存,其中比特位的位置:代表信号编号,比特位的内容:是否接收到信号】
例如0000 0000 0000 0000 0000 0000 0000 0000
,接收6号信号时,修改为0000 0000 0000 0000 0000 0000 0010 0000
信号的产生
一个进程收到信号,本质就是该进程内的信号位图被修改了,也就是该进程的数据被修改了,而只有操作系统才有资格修改进程的数据,因为操作系统是进程的管理者。也就是说,信号的产生本质上就是操作系统直接去修改目标进程的task_struct中的信号位图,由OS完成(调用相关系统接口)
信号的处理
进程在处理信号时有三种动作:默认、自定义(也称捕捉一个信号)和忽略。
查看各个信号默认的处理动作man 7 signal
:
The entries in the "Action" column of the table below specify the default disposition for each signal, as follows:
Term Default action is to terminate the process.
Ign Default action is to ignore the signal.
Core Default action is to terminate the process and dump core (see core(5)).
Stop Default action is to stop the process.
Cont Default action is to continue the process if it is currently stopped.
2
3
4
5
6
7
8
9
10
11
# 产生信号
# 通过系统函数
- kill命令
#include <signal.h>
int kill(pid_t pid, int sig);
2
kill函数用于向进程ID为pid的进程发送sig号信号,如果信号发送成功,则返回0,否则返回-1
示例:
mysignal
#include <iostream>
#include <unistd.h>
#include <sys/ipc.h>
#include <signal.h>
void Usage() {
std::cout << "Usage: ./Mysignal pid signo" << std::endl;
}
int main(int argc, char *argv[]) {
if(argc != 3) {
Usage();
exit(1);
}
pid_t pid = atoi(argv[1]);
int signo = atoi(argv[2]);
int n = kill(pid, signo);
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
test.cpp
#include <iostream>
#include <unistd.h>
#include <sys/ipc.h>
int main(int argc, char *argv[]) {
while(true) {
std::cout << "一段运行的进程" << getpid() << ":print Hello World!" << std::endl;
sleep(2);
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
运行 窗口1:
[cen@VM-4-9-opencloudos mysignal]$ make
g++ -o test test.cpp -std=c++11 -g
g++ -o Mysignal mysignal.cpp -std=c++11
[cen@VM-4-9-opencloudos mysignal]$ ./test
一段运行的进程2018165:print Hello World!
一段运行的进程2018165:print Hello World!
一段运行的进程2018165:print Hello World!
一段运行的进程2018165:print Hello World!
一段运行的进程2018165:print Hello World!
一段运行的进程2018165:print Hello World!
一段运行的进程2018165:print Hello World!
Killed
2
3
4
5
6
7
8
9
10
11
12
窗口2:
[cen@VM-4-9-opencloudos mysignal]$ ./Mysignal
Usage: ./Mysignal pid signo
[cen@VM-4-9-opencloudos mysignal]$ ./Mysignal 2018165 9
[cen@VM-4-9-opencloudos mysignal]$
2
3
4
- raise函数
raise函数可以给当前进程发送指定信号,即自己给自己发送信号,raise函数的函数原型如下:
int raise(int sig);
- abort函数
abort函数可以给当前进程发送SIGABRT信号,使得当前进程异常终止,abort函数的函数原型如下:
void abort(void);
# 核心转储
Action为core时:Core Default action is to terminate the process and dump core[核心转储] (see core(5)).
核心转储是默认被关掉的,我们可以通过使用ulimit -a命令查看当前资源限制的设定。 我们可以通过ulimit -c size命令来设置core文件的大小,从而打开核心存储。
[cen@VM-4-9-opencloudos lesson03-进程通信]$ ulimit -a
real-time non-blocking time (microseconds, -R) unlimited
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 6680
max locked memory (kbytes, -l) 8192
max memory size (kbytes, -m) unlimited
open files (-n) 524288
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 6680
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
[cen@VM-4-9-opencloudos lesson03-进程通信]$ ulimit -c 1024
[cen@VM-4-9-opencloudos lesson03-进程通信]$ ulimit -a
real-time non-blocking time (microseconds, -R) unlimited
core file size (blocks, -c) 1024
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 6680
max locked memory (kbytes, -l) 8192
max memory size (kbytes, -m) unlimited
open files (-n) 524288
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 6680
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
使用:
当我们的程序在运行过程中崩溃了,我们一般会通过调试来进行逐步查找程序崩溃的原因。而在某些特殊情况下,我们会用到核心转储,核心转储指的是操作系统在进程收到某些信号而终止运行时,将该进程地址空间的内容以及有关进程状态的其他信息转而存储到一个磁盘文件当中,这个磁盘文件也叫做核心转储文件,一般命名为core.pid,方便问题的定位。
[cen@VM-4-9-opencloudos mysignal]$ make
g++ -o Mysignal mysignal.cpp -std=c++11
[cen@VM-4-9-opencloudos mysignal]$ ./Mysignal
3728615 Hello World!
3728615 Hello World!
3728615 Hello World!
^\Quit (core dumped)
# Ctrl + \ : SIGQUIT P1990 Core Quit from keyboard
[cen@VM-4-9-opencloudos mysignal]$ ll
total 32
-rw-r--r-- 1 cen cen 340 Apr 20 17:44 Makefile
-rwxr-xr-x 1 cen cen 17088 Apr 20 18:01 Mysignal
-rw-r--r-- 1 cen cen 549 Apr 20 17:48 mysignal.cpp
-rw-r--r-- 1 cen cen 250 Apr 16 20:15 test.cpp
[cen@VM-4-9-opencloudos mysignal]$ coredumpctl
Hint: You are currently not seeing messages from other users and the system.
Users in groups 'adm', 'systemd-journal', 'wheel' can see all messages.
Pass -q to turn off this notice.
TIME PID UID GID SIG COREFILE EXE > >
Sun 2025-04-20 18:01:51 CST 3728615 1001 1001 SIGQUIT present /home/cen/Linux/lesson03-进程通信/mysignal/Mysignal
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 阻塞信号
# 信号其他相关常见概念
- 实际执行信号的处理动作,称为信号递达(Delivery)。
- 信号从产生到递达之间的状态,称为信号未决(pending)。
- 进程可以选择阻塞(Block)某个信号。
- 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。
- 阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后的一种处理动作。
# 在内核中的表示
有两张位图pending
和block
和一个数组handler
表:
- unsigned int pending 比特位的位置表示信号编号, 比特位的内容表示是否收到对应信号
- unsigned int block 比特位的位置表示信号编号, 比特位的内容表示是否阻塞对应信号
- 函数指针数组handler表,数组的下标代表某一个信号,数组的内容代表该信号递达时的处理动作,处理动作包括默认、忽略以及自定义。
# 捕捉信号
# 内核空间和用户空间
信号在产生的时候不会被立即处理而是在合适的时候(从内核态到用户态的时候进行处理)
用户态为了访问内核或硬件资源,必须通过系统调用完成访问
每一个进程都有自己的进程地址空间,该进程地址空间由内核空间和用户空间组成:
- 用户所写的代码和数据位于用户空间,通过用户级页表与物理内存之间建立映射关系。
- 内核空间存储的实际上是操作系统代码和数据,通过内核级页表与物理内存之间建立映射关系。
- 内核级页表是一个全局的页表,它用来维护操作系统的代码与进程之间的关系。因此,在每个进程的进程地址空间中,用户空间是属于当前进程的,每个进程看到的代码和数据是完全不同的,但内核空间所存放的都是操作系统的代码和数据,所有进程看到的都是一样的内容。
# 内核态和用户态
内核态与用户态:
内核态通常用来执行操作系统的代码,是一种权限非常高的状态。 用户态是一种用来执行普通用户代码的状态,是一种受监管的普通状态。 进程收到信号之后,并不是立即处理信号,而是在合适的时候,这里所说的合适的时候实际上就是指,从内核态切换回用户态的时候。
P29 + P30