4. IPC信号

信号(Signals)

信号(Signals)是 Linux/UNIX 进程间通信(IPC)的一种方式(共七种方式)。信号由一个用户进程或者操作系统内核产生,通过 Linux 或 UNIX 操作系统内核传递给另外一个进程。信号在经过操作系统时,操作系统可以根据信号的意义做出相应的操作。

信号是一个 1~64 的整数,每一个整数代表不同的含义,这个含义由操作系统和用户定义。

我们可以使用 kill -l 命令列出所有的信号值和对应的类型,如下所示:

weimingze@mzstudio:~$ 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 由早期 UNIX 系统定义并延续至 Linux 系统,每个信号的用途已经明确定义,是标准信号。

其中 34 ~ 64 是 Linux 扩展的信号,用于用户自定义用途。

标准信号(1-31)

以下是传统的 UNIX 信号,具有明确的用途,如下表所示:

编号
信号名称
默认行为
说明
1
SIGHUP
终止
挂起信号,终端断开或控制进程终止时发送(常用于重新加载配置,如 nginx -s reload)。
2
SIGINT
终止
中断信号Control+C),由终端发送,请求进程终止。
3
SIGQUIT
终止+核心
退出信号Control+\),类似 SIGINT,但会生成核心转储(core dump)。
4
SIGILL
终止+核心
非法指令,进程尝试执行无效的 CPU 指令。
5
SIGTRAP
终止+核心
陷阱信号,调试器(如 gdb)用于断点调试。
6
SIGABRT
终止+核心
中止信号,由 abort() 触发,表示严重错误(如 assert 失败)。
7
SIGBUS
终止+核心
总线错误,非法内存访问(如未对齐的内存访问)。
8
SIGFPE
终止+核心
浮点异常,如除以零。
9
SIGKILL
终止
强制终止(不可捕获、阻塞或忽略),确保进程结束。
10
SIGUSR1
终止
用户自定义信号 1,用途由应用程序定义(如 nginx 日志重载)。
11
SIGSEGV
终止+核心
段错误,非法内存访问(如访问 NULL 指针)。
12
SIGUSR2
终止
用户自定义信号 2,用途由应用程序定义。
13
SIGPIPE
终止
管道破裂,写入无读取端的管道
14
SIGALRM
终止
定时器信号,由 alarm()setitimer() 触发(如 timeout 命令)。
15
SIGTERM
终止
终止信号(默认 kill),请求进程正常退出(可捕获)。
16
SIGSTKFLT
终止
协处理器栈错误(极少使用)。
17
SIGCHLD
忽略
子进程状态变更(子进程终止时发送给父进程)。
18
SIGCONT
继续
继续执行,恢复被 SIGSTOP/SIGTSTP 停止的进程(如 fg 命令)。
19
SIGSTOP
停止
强制停止(不可捕获、阻塞或忽略)。
20
SIGTSTP
停止
停止进程Control+Z),可捕获(如 vim 临时挂起)。
21
SIGTTIN
停止
后台进程尝试读取终端(如 cat & 后尝试输入)。
22
SIGTTOU
停止
后台进程尝试写入终端(如 echo "test" & 后尝试输出)。
23
SIGURG
忽略
紧急数据(如带外数据 OOB 到达 socket)。
24
SIGXCPU
终止+核心
CPU 时间超限(超过 ulimit -t 限制)。
25
SIGXFSZ
终止+核心
文件大小超限(超过 ulimit -f 限制)。
26
SIGVTALRM
终止
虚拟定时器超时(由 setitimer(ITIMER_VIRTUAL) 触发)。
27
SIGPROF
终止
性能分析定时器超时(由 setitimer(ITIMER_PROF) 触发)。
28
SIGWINCH
忽略
窗口大小变化(终端调整大小时发送,如 vim 自适应)。
29
SIGIO
终止
异步 I/O 事件(文件描述符准备就绪)。
30
SIGPWR
终止
电源故障(UPS 电池即将耗尽时发送)。
31
SIGSYS
终止+核心
无效系统调用(进程执行了不存在的系统调用)。

常用信号列表

编号
信号名称
说明
1
SIGHUP
挂起信号
2
SIGINT
中断信号
3
SIGQUIT
退出信号
9
SIGKILL
强制终止
11
SIGSEGV
段错误
13
SIGPIPE
管道破裂
14
SIGALRM
定时器信号
15
SIGTERM
终止信号
17
SIGCHLD
子进程状态变更
18
SIGCONT
继续执行
19
SIGSTOP
强制停止
20
SIGTSTP
停止进程

常见终端信号

快捷键
编号
信号名称
说明
Control + C
2
SIGINT
中断信号
Control + \
3
SIGQUIT
退出信号
Control + Z
20
SIGTSTP
停止进程

测试终端信号

写一个 Python 程序 always_run.py,内容如下:

import os

print("PID:", os.getpid())
while True:
    pass

注意: pass 前面要保留 4 个空格其他行前面不要留有空格。

具体 Python 的语法详见我的 《python教程》

此程序先打印当前进程的 PID ,然后进入死循环,一直运行不退出。

运行 always_run.py 程序

weimingze@mzstudio:~$ python3 always_run.py
PID: 8217

此时程序一直执行,一直占用终端,没有退出。因此在终端中不会看见 weimingze@mzstudio:~$ 这样的命令提示符。

再打开另外一个终端查看 启动命令为 python3(使用 -C python3 选项)的进程的详细信息(使用 -l 选项),如下:

weimingze@mzstudio:~$ ps -l -C python3
F S   UID     PID    PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 R  1000    8217    5889 99  80   0 -  7336 -      pts/0    00:04:42 python3

可见第二列(S 列)对应的状态是 R 说明程序在运行状态。

此时使用 top 命令查看,你会发现此进程将一个 CPU 占用 99.7% 的资源。 如下:

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
   8217 weiming+  20   0   29344   9116   6300 R  99.7   0.2  11:07.93 python3

在第一个终端中输入 Control + C 向程序发送 SIGINT 信号,则 always_run.py 程序停止运行,提示如下:

weimingze@mzstudio:~$ python3 always_run.py
PID: 8217
^CTraceback (most recent call last):
  File "/home/weimingze/always_run.py", line 4, in <module>
    while True:
          ^^^^
KeyboardInterrupt

weimingze@mzstudio:~$

此时程序停止,命令提示符 weimingze@mzstudio:~$ 出现,CPU 占有率降低。再用 ps 命令也查不到此进程。

再次运行 always_run.py 程序。

weimingze@mzstudio:~$ python3 always_run.py
PID: 8358

然后在终端中输入 Control + Z 向程序发送 SIGTSTP 信号,则 always_run.py 程序 停止运行,提示如下:

weimingze@mzstudio:~$ python3 always_run.py
PID: 8358
^Z
[1]+  Stopped                 python3 always_run.py

使用 ps 命令查看进程情况:

weimingze@mzstudio:~$ ps -l -C python3
F S   UID     PID    PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 T  1000    8358    5889 92  80   0 -  7336 do_sig pts/0    00:03:10 python3

此时 此进程的状态是 T(停止状态),此时 top 命令发现 CPU 占有率下降。

使用 fg 命令(后面再讲) 回复此进程为运行状态,如下:,

weimingze@mzstudio:~$ fg
python3 always_run.py

使用 ps 命令查看进程情况:

weimingze@mzstudio:~$ ps -l -C python3
F S   UID     PID    PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 R  1000    8358    5889 93  80   0 -  7336 -      pts/0    00:07:54 python3

状态为 R 运行状态。

然后再终端中输入 Control + \ 向程序发送 SIGQUIT 信号,则 always_run.py 程序 退出运行,提示如下:

weimingze@mzstudio:~$ fg
python3 always_run.py
^\Quit (core dumped)
weimingze@mzstudio:~$ ps -l -C python3
F S   UID     PID    PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD

练习:

  1. 编写上述示例中 always_run.py 文件。
  2. 使用 python3 always_run 并运行此程序,然后查看CPU占有率。
  3. 使用 Control + Z 让此程序停止并进入后台运行,然后查看CPU占有率。
  4. 使用 fg 命令回复程序的运行,然后查看CPU占有率。
  5. 使用 Control + \ 让此程序终止运行,然后查看CPU占有率。