(一)生活角度的信号
我们要学习有关信号的相关知识,首先要知道什么是信号。接下来我先从生活中的例子来带大家逐步认识。
在日常生活中,"信号" 这个词可以涵盖多个领域,以下是一些常见的生活中的信号:
- 红绿灯、语言、姿态、表情 、鸟鸣、风声、情绪、闹钟、狼烟等等
就比如上诉的 红绿灯:我们都知道“红灯停,绿灯行”,对于不同的颜色都会匹配相应的动作。此时我就要问大家为什么会有这个动作呢?
- 其实很简单,因为曾经有人或者有事等 “培养”过你,使你脑海中产生了这种印象,以至于即使现在不是身处红绿灯处,我们也知道不同的颜色我们该怎么进行处理!!
同理,若此时存在一个进程,而信号就是一个数字(可以通过 kill -l 查看,稍后会讲),即使进程没有收到信号的时候,它也知道当收到信号的时候能够准备识别并知道该如何处理(因为程序员设计进程的时候,早就已经设计好了对信号识别处理的能力!!)
如果大家还是困惑我在给大家举个例子:
- 假设你在网上买了很多件商品,在等待不同商品快递的到来。但即便快递没有到来,你也知道快递来临时,你该怎么处理快递。也就是你能“识别快递”;
- 当快递员到了你楼下,你也收到快递到来的通知,但是你正在打游戏,需5min之后才能去取快递。那么在在这5min之内,你并没有下去去取快递,但是你是知道有快递到来了。也就是取快递的行为并不是一定要立即执行,可以理解成“在合适的时候去取”;
- 在收到通知,再到你拿到快递期间,是有一个时间窗口的,在这段时间,你并没有拿到快递,但是你知道有一个快递已经来了。本质上是你“记住了有一个快递要去取”;
- 当你时间合适,顺利拿到快递之后,就要开始处理快递了。而处理快递一般方式有三种:1. 执行默认动作(幸福的打开快递,使用商品)2. 执行自定义动作(快递是零食,你要送给你你的女朋友)3. 忽略快递(快递拿上来之后,扔掉床头,继续开一把游戏);
- 快递到来的整个过程,对你来讲是异步的,你不能准确断定快递员什么时候给你打电话.
(二)查看信号列表
我们可以使用kill -l命令可以察看系统定义的信号列表。具体如下:
【解释说明】
- 首先,在Linux系统中,【1-31】号的信号通常是标准的POSIX信号,大家也可以叫做普通信号(这是我们学习的主要);
- 其次,大家可以发现没有32、33号信号,31号之后就是34号。在POSIX标准中,并没有规定32和33号这样的信号,因此在通常的Linux系统中,这些数字对应的信号并没有特殊的含义。这并不是系统实际上没有定义更高编号的信号,而是在POSIX标准中未规定的部分给了系统一些灵活性,使得系统可以自定义和扩展信号的使用;
- 最后34号之后的统称为“实时信号”,对于这部分信号,大家可以不用关心;
- 最后大家也可以发现,每个都是以 数字开头紧跟一个大写的字符串。这里的数字就是“信号”,后面大写的字符串就是“宏”。
- SIGHUP (1): 挂起信号
- SIGINT (2): 中断信号
- SIGQUIT (3): 退出信号
- SIGILL (4): 非法指令信号
- SIGTRAP (5): 跟踪/断点陷阱信号
- SIGABRT (6): 中止信号
- SIGBUS (7): 总线错误信号
- SIGFPE (8): 浮点异常信号
- SIGKILL (9): 强制杀死进程信号
- SIGUSR1 (10): 用户自定义信号 1
- SIGSEGV (11): 段错误信号
- SIGUSR2 (12): 用户自定义信号 2
- SIGPIPE (13): 管道破裂信号
- SIGALRM (14): 超时信号
- SIGTERM (15): 终止信号
- SIGSTKFLT (16): 协程栈错误信号
- SIGCHLD (17): 子进程状态改变信号
- SIGCONT (18): 继续执行信号
- SIGSTOP (19): 停止进程信号
- SIGTSTP (20): 终端停止信号
- SIGTTIN (21): 后台进程尝试读取控制终端
- SIGTTOU (22): 后台进程尝试写入控制终端
- SIGURG (23): 紧急情况信号
- SIGXCPU (24): 超过CPU时间限制信号
- SIGXFSZ (25): 超过文件大小限制信号
- SIGVTALRM (26): 虚拟定时器信号
- SIGPROF (27): 用于分析的定时器信号
- SIGWINCH (28): 窗口大小改变信号
- SIGIO (29): 异步I/O事件通知信号
- SIGPWR (30): 电源故障信号
- SIGSYS (31): 非法系统调用信号
(三)技术应用角度的信号
有了上述的 知识储备之后,接下来通过简单的代码来理解信号!!!
演示如下:
除了上述方法之外,还可以通过【ctrl+c】的方式终止一个前台进程
【现象解释】
- 用户输入命令,在Shell下启动一个前台进程。
- 用户按下【Ctrl+C】,,这个键盘输入产生一个硬件中断,被OS获取,解释成信号,发送给目标前台进程。前台进程因为收到信号,进而引起进程退出
【解释说明】
- 前台进程是在终端中直接运行的进程,它会占用终端并接收来自用户的输入。通常,用户可以看到前台进程的输出,并且可以与之交互;
- 前台进程的执行会阻塞终端,直到该进程完成或被暂停。
相应的也有后台进程(使用 & 将进程放到后台执行)
- 后台进程是在终端中以非阻塞方式运行的进程。用户可以继续输入命令和执行其他任务,而后台进程在后台默默执行;
- 后台进程通常用于执行长时间运行的任务,而不占用用户的终端。
演示如下:
注意
- 1. Ctrl-C 产生的信号只能发给前台进程。一个命令后面加个&可以放到后台运行,这样Shell不必等待进程结束就可以接受新的命令,启动新的进程。
- 2. Shell可以同时运行一个前台进程和任意多个后台进程,只有前台进程才能接到像 Ctrl-C 这种控制键产生的信号。
- 3. 前台进程在运行过程中用户随时可能按下 Ctrl-C 而产生一个信号,也就是说该进程的用户空间代码执行到任何地方都有可能收到 SIGINT 信号而终止,所以信号相对于进程的控制流程来说是异步(Asynchronous)的。
而此时,大家是否想到一点。对于 Ctrl-C 指令,默认执行的便是终端信号的作用,与我们的 2号信号的效果类似,那么到底是不是呢?我简单的演示下,将 2 号信号的默认执行动作改了,看是否和我们预想的类似。
代码展示:
#include<iostream> #include<unistd.h> #include <sys/types.h> #include<signal.h> void handle(int signa){ std::cout << "get a siganl:" << signa << std::endl; } int main() { signal(2,handle); while(true){ std::cout << "我是一个进程,我正在运行...,pid:" << getpid() <<std::endl; sleep(1); } return 0; }
效果演示:
其次,除了上述终止进程的方法之外,还可以使用【ctrl+\】
【解释说明】
- 而这里的【ctrl+\】其实就是 3 号信号所属,我也可以给大家验证一哈(同样的让3号信号执行我们自定义的方法)
效果演示:
(四)信号的概念
综上所述,信号是进程之间事件异步通知的一种方式,属于软中断
理解信号是软中断需要考虑计算机系统中的中断和信号两个概念。
中断:
- 中断是计算机系统中的一种机制,用于处理与正常执行流程无关的事件。这些事件可能来自硬件,例如硬件故障、定时器到期、输入设备的输入等。当系统接收到中断信号时,当前执行的程序会被中断,系统会转而执行相应的中断处理程序。中断可以是硬中断(由硬件触发)或软中断(由软件触发)。
信号是软中断:
- 信号是一种用于在软件层面通知进程发生了某个事件的机制。这些事件可以是来自其他进程的通知、异常情况、用户的交互等。当系统检测到某个事件时,会向相应的进程发送一个信号。进程可以事先定义信号的处理方式,比如忽略、捕获、或执行默认操作。
因此综合起来,将信号称为软中断是因为它类似于硬中断的概念,但它是在软件层面实现的。信号提供了一种轻量级的通信和事件处理机制,允许进程在运行时响应各种事件,而不需要像硬中断那样涉及到硬件层面的处理。
(五)小结
- 每个信号都有一个唯一的编号。常见的信号有诸如SIGKILL(终止进程)、SIGTERM(终止进程,但可被捕获和忽略)等。
- 进程可以对不同的信号设置不同的处理方式,包括:
- 默认操作: 操作系统默认对信号执行的动作。
- 忽略信号: 进程可以选择忽略某个信号。
- 捕获信号: 进程可以注册一个信号处理函数,以在接收到信号时执行自定义操作。
- 常见信号:
- 一些常见的信号包括:
- SIGINT(2): 由中断字符(通常是Ctrl+C)发送,用于中断正在运行的程序。
- SIGTERM(15): 用于请求进程正常终止。
- SIGKILL(9): 用于强制终止进程。
- SIGHUP(1): 表示终端挂起或控制进程终止。
以上便是本期信号概念的基本介绍。下期我将给大家介绍“信号的产生”相关知识!!!