明镜止水,长话短说。
目录
- 信号的概念、认识、处理动作
- 信号的基本概念
- 通过kill命令认识一些信号
- 进程状态
- 信号测试:
- 信号处理的相关动作
- 执行系统默认动作
- 忽略某个信号
- 捕捉某个信号
信号,在服务器编程和大型应用程序中即为常见。信号就是一个通知(事件通知),用来通知某个进程发生了某一件事。 很多时候,信号都是突然产生的,比如,一个程序某个地方出现异常,它给自己发送一个中断自己进程的信号,此时程序可能迎来结束,从而防止更大异常(来自游戏闪退的怨念),换一句话,信号是异步发生的,又称“软件中断”。
信号的产生:
(1)某个进程发送给另一个进程或者发送给自己
(2)由内核发送给某个进程
- 通过在键盘上输入一些命令动作,Ctrl+C组合键(终端信号),使用kill命令等。
- 内存访问异常。除数为0等,硬件会检测并通知内核等。
信号名字一般都为SIG开头的字母组成,但基于头文件源代码来看,却是数字。
//查看各signal.h 中文件SIGHUP信号的位置 root@ubuntu:/home/qiye/Desktop# sudo find / -name "signal.h" | xargs grep -in "SIGHUP" /usr/src/linux-headers-5.11.0-49/arch/mips/include/uapi/asm/signal.h:25:#define SIGHUP 1 /usr/src/linux-headers-5.11.0-49/arch/xtensa/include/uapi/asm/signal.h:35:#define SIGHUP 1 /usr/src/linux-headers-5.11.0-49/arch/s390/include/uapi/asm/signal.h:26:#define SIGHUP 1 /usr/src/linux-headers-5.11.0-49/arch/ia64/include/uapi/asm/signal.h:13:#define SIGHUP 1 /usr/src/linux-headers-5.11.0-49/arch/x86/include/uapi/asm/signal.h:23:#define SIGHUP 1 /usr/src/linux-headers-5.11.0-49/arch/sparc/include/uapi/asm/signal.h:12:#define SIGHUP 1 /usr/src/linux-headers-5.11.0-49/arch/arm/include/uapi/asm/signal.h:18:#define SIGHUP 1 /usr/src/linux-headers-5.11.0-49/arch/parisc/include/uapi/asm/signal.h:5:#define SIGHUP 1 /usr/src/linux-headers-5.11.0-49/arch/m68k/include/uapi/asm/signal.h:18:#define SIGHUP 1 .....
打开任意一个signal.h文件:
vim /usr/src/linux-headers-5.11.0-49/arch/x86/include/uapi/asm/signal.h
为了方便使用信号,通过使用define预处理器建立数字的别名。
kill命令常用来杀死了一个进程,我印象最深、用得最多的一句就是sudo kill -9 进程号,但现在,入这大坑后,kill就不再是单纯用来杀死进程这么简单,而是通过使用kill命令给一个进程发送信号来控制进程的中断、再续、终止来影响程序状态的一个命令。
常用的kill参数对应的信号
| kill的参数 | 该参数发出的信号以及操作系统动作 |
|---|---|
| -1 | SIGHUP(连接断开),终止进程(进程没了) |
| -2 | SIGINT(终端中断符,如Ctrl+C),终止进程(进程没了) |
| -3 | SIGQUIT(中断退出符, 如Ctrl+),终止进程(进程没了) |
| -9 | SIGKILL(终止),终止进程(进程没了) |
| -18 | SIGCONT(使暂停的进程继续),忽略(进程继续运行不受影响) |
| -19 | SIGSTOP(停止),可使用SIGCONT继续,任务放到后台,停止进程(不是终止,进程还在) |
| -20 | SIGTSTP(终端停止符,同Ctrl+Z),任务于后台,可使用SIGCONT继续,停止进程(不是终止,进程还在) |
查看进程状态(STAT选项)
ps -eo pid,ppid,sid,tty,pgrp,comm,stat | grep -E '程序检索字段'
PID PPID SID TT PGRP COMMAND STAT
1 0 1 ? 1 systemd Ss
2 0 0 ? 0 kthreadd S
3 2 0 ? 0 rcu_gp I<
4 2 0 ? 0 rcu_par_gp I<
6 2 0 ? 0 kworker/0:0H-ev I<
9 2 0 ? 0 mm_percpu_wq I<
10 2 0 ? 0 rcu_tasks_rude_ S
其中STAT,是这些进程的一个状态,进程状态字母对应的状态含义:
| 状态 | 含义 |
|---|---|
| D | 不可中断的休眠状态(通常是I/O枫如进程),可以处理信号,有延迟 |
| R | 可执行状态&运行状态(再运行队列里的状态) |
| S | 可中断的休眠状态之中(等待某事件完成),可以处理信号 |
| T | 停止或被追踪(被作业控制信号所停止) |
| Z | 僵尸进程 |
| X | 死掉的进程 |
| < | 高优先级的进程 |
| N | 低优先级的进程 |
| L | 有些页被锁进内存 |
| s | session leader(会话首进程),其下有子进程 |
| t | 追踪期间被调试器停止 |
| i | 空闲的任务(进程) |
| + | 位于前台的进程组 |
一样,ssh连接ubuntu,打开两个终端,一个终端负责程序的执行于=和结果,另一个负责进程的状态和kill命令的执行。
第一个终端:
C++简单代码编写:
#include#include #include using namespace std; int main() { pid_t pid; cout << "Linux C++" << endl; while (1){ sleep(1); cout << "子进程" << endl; } cout << "end!" << endl; return 0; }
C++文件编译和执行:
g++ -o t141 /mnt/hgfs/c/t141.cpp ./t141 Linux C++ 子进程 子进程 子进程 子进程
第二个终端:
#此时程序状态为置于前台的可中断休眠状态 root@ubuntu:/home/qiye# ps -eo pid,ppid,sid,tty,pgrp,comm,stat | grep -E 't141' 10095 6202 5942 pts/1 10095 t141 S+ #发送SIGSTOP(停止)信号 root@ubuntu:/home/qiye# kill -19 10095 /#此时程序为停止或被追踪的状态 root@ubuntu:/home/qiye# ps -eo pid,ppid,sid,tty,pgrp,comm,stat | grep -E 't141' 10095 6202 5942 pts/1 10095 t141 T
第一个终端出现stopped的提示:
继续使用第二个终端:
#发送SIGCONT(使暂停的进程继续)信号,恢复程序运行 root@ubuntu:/home/qiye# kill -18 10095 #可以发现恢复回S的状态,但是+号没了,说明已经不是前台运行了, #你会发现再第一个终端里使用Ctrl+C无法终止程序运行 root@ubuntu:/home/qiye# ps -eo pid,ppid,sid,tty,pgrp,comm,stat | grep -E 't141' 10095 6202 5942 pts/1 10095 t141 S
程序继续运行:
但是Ctrl+C无法终止它:
在第二个终端中使用kill -9 终止程序:
root@ubuntu:/home/qiye# kill -9 10095 root@ubuntu:/home/qiye# ps -eo pid,ppid,sid,tty,pgrp,comm,stat | grep -E 't141' root@ubuntu:/home/qiye#信号处理的相关动作 执行系统默认动作
当进程收到一个信号时,操作系统(内核)就会对该进程执行一个默认动作,
相应的信号会有相应的默认动作,但大多数时候操作系统的默认动作是终止。
当不希望一个进程收到某个信号时,就要使操作系统不对这个信号做出默认动作,常用的是signal(信号名, SIG_IGN)函数,作用是忽略某个信号,操作系统就会忽略这个信号名,但两个信号列外,SIGKILL信号和SIGSTOP信号,这俩不能被忽略,你也不希望一个程序无法被任何手段终止吧?
捕捉某个信号signal(信号名, 自定义函数),专门捕捉某个信号名,并且成功捕捉后可以送进自定义函数里实现自定义功能,SIGKILL信号和SIGSTOP信号这俩依旧列外。



