目录
信号的概念:
信号表的继承:
信号的分类与编号:
特殊的信号:
信号的产生:
1.键盘输入:
2.系统调用:
3.异常或硬件错误:
4.总结:
信号的处理:
1.进程如何得知收到的是哪个信号?
2.进程处理信号的方式:
信号的概念:
-
信号是进程间通信的一种异步通知机制,用于向目标进程发送通知。
-
信号的处理是异步的,意味着信号可以在任何时候产生,而进程会在适当时机对信号作出处理。
异步(Asynchronous)是指在编程或系统设计中,任务的执行不需要立即等待其他任务完成,而是可以在不阻塞的情况下继续进行。
信号表的继承:
-
每个进程都有一张信号表,本质是一个函数指针数组,数组的下标就对应每一个信号。
-
当使用fork()系统调用创建子进程时,子进程会复制父进程的信号处理表。这意味着父进程如何处理信号,子进程刚被创建时也会如何处理信号。
-
当一个进程通过 exec() 系列函数进程程序替换时,信号表中的自定义处理方式会恢复为默认处理方式(操作系统定义的默认行为)。
信号的分类与编号:
-
系统内置了多种信号,每种信号有特定编号。
-
信号在是线上是以宏的方式实现的,所以使用信号,可以通过信号的宏名也可以通过信号的值。
-
使用 kill -l 可以查看当前系统支持的所有信号。
-
使用man 7 signal查看信号详细介绍。
-
信号分为两类:普通信号(1~31)和实时信号(34~64)。
特殊的信号:
-
没有 0 号信号,0 常用于表示进程的正常退出。
-
32 和 33 号信号是为操作系统内部保留的,用户进程不会使用这些信号。
-
9 号信号 (SIGKILL) 不能被进程捕获或忽略,确保进程可以被强制终止。
信号的产生:
-
信号可以通过软件、硬件、或者组合键产生。
1.键盘输入:
-
通过键盘组合键可以产生信号:键盘被按下后,会向 CPU 发送中断,CPU读取到键盘的数据为组合键后,形成具体的按键事件,这个事件包含处理的逻辑,比如发送信号给前台进程。
2.系统调用:
-
通过 int kill(pid_t pid, int sig); 系统调用可以向任意进程发送任意信号。
-
通过 int raise(int sig); 函数用于给自身发送任意信号。
-
通过 void abort(void); 会给自身发送 SIGABRT 信号,6号信号,用于强终止进程,即使使用signal重写了信号的处理方法,进程也会被强制结束。
3.异常或硬件错误:
-
当出现如段错误或非法指令时,系统会向进程发送相应的信号(如 SIGSEGV),终止进程并清理上下文。
4.总结:
-
信号的产生方式有很多种,但是信号只能由操作系统发送,进程的管理者OS才有资格修改进程pcb中位图的权限。
信号的处理:
1.进程如何得知收到的是哪个信号?
-
操作系统中的信号机制类似于软件中的“中断”,当进程收到信号时,操作系统并不会立刻打断进程的执行,它会通过进程的pid查找到该进程的PCB,通过修改进程控制块(PCB)中的一个位图来记录信号,并在进程准备好时再处理这些信号。
-
在进程的PCB中,通常会包含一个信号位图字段(如uint32_t),用于表示进程当前接收到的信号状态。每一位代表一个特定的信号。
-
位图中的每个比特位表示一个信号的状态。如果某个信号被发送到进程,则对应信号的比特位被置为1。
-
位图中的比特位数通常比系统支持的信号数量多出1位,因为信号编号从1开始(位图的第0位通常不用)。
2.进程处理信号的方式:
-
信号有三种处理方式:默认处理,忽略,自定义处理。
-
自定义信号处理时,signal函数并不会“截获”信号再调用自定义处理函数,而是直接修改信号表中对应信号的处理函数指针,使得该信号到达时调用自定义的处理函数。
-
具体来说,操作系统提供的函数,如 signal(int signum, sighandler_t handler);或 sigaction(),允许进程修改信号表,将默认的信号处理函数替换为自定义的处理函数。