进程信号
Posted cuckoo-
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了进程信号相关的知识,希望对你有一定的参考价值。
进程信号:
信号概念:信号就是一个软件中断,通知进程发生了某件事情(打断当前阻塞操作,选择一个合适的时机去处理信号)
功能:通知事件的发生
能够识别信号
信号有不同种类:
查看种类:(共有62种)
命令:kill -l
1号—31号(非实时信号):linux继承unix的信号(非可靠信号,表示信号可能会丢失)
当有多个信号一起来的时候,只会处理一个信号
34号—64号(实时信号):可靠信号,表示信号不会丢失
信号有生命周期:
信号的产生——>信号在进程中的注册——>信号在进程中的注销——>信号的处理(消亡)
信号的产生:
硬件: ctrl + c; ctrl + |; ctrl + z(中断一个前台进程)
软件: kill命令; kill(); raise(); abort(); alarm();
接口函数:
int kill(int pid, int sig)
给指定的 pid 发送指定的信号 sig
int raise(int sig)
给调用进程发送 sig 信号
void abort(void)
给调用进程发送 SIGABRT 信号
unsigned int alarm(unsigned int seconds)
seconds 秒后给调用进程发送一个 SIGALRM 信号
若 seconds = 0, 则取消上一个定时器
alarm(1);//表示取消上一个定时器,在设定 seconds = 1 的定时器
返回值: 若第一次使用 alarm 则返回 0; 若不是则返回上一个定时器剩余的时间
信号在进程中的注册:修改未决信号集合对应位图,添加sigqueue结点
sigset_t 信号集合(位图)
pcb->struct sigpending->sigset_t signal
未决信号:注册了但是没有被处理的信号
未决:是一个信号从产生到信号处理之间所处的状态
sigset_t结构体的认识:unsigned long int_val[_SIGSET_NWORDS];
是一个128位的位图,用于对信号是否到来做标记
sigqueue是一个链表,信号到来后会组织一个对应信号的节点,添加到链表中
非可靠信号:判断如果位图已经标记,将什么都不做(每一个信号只添加一个节点,
信号同时到来时,有可能丢失事件)
可靠信信号:不管位图是否标记,都会为每个到来的信号组织一个节点,添加到链表中,标记位图
可靠/非可靠信号:最大区别:是否位每个到来的信号添加isgqueue节点
在进程中注销信号:删除信号的sigqueue节点,修改位图
可靠信号的注销:删除结点,判断链表中是否还有相同节点,如果没有在位图置0,否则位图不变
非可靠信号的注销:删除节点,修改位图位0
信号的处理:
1.默认处理方式
SIG_DFL
2.忽略处理方式
SIG_IGN
3.自定义处理方式
signal()
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signum:信号编号 handler:信号的处理方式
sigaction()
使用act 动作替换signum信号的处理方式,将原来的处理方式放到oldact中
int sigaction(int signum, const struct sigaction* act, struct sigaction*oldact);
signum:信号编号 act:新的信号动作 oldact:保存旧的信号动作
int sigemptyset(sigset_t *set);———>清空信号集合
自定义处理信号的捕捉流程:
什么时候才会处理信号的事件:
main()经过系统调用/中断/异常来完成 “ 内核功能从内核态返回用户态的到时候” 判断是否有信号待处理,
如果有,并且这个信号处理方式是忽略/默认方式,则直接在内核态完成。
如果这个信号处理方式是自定义处理方式则返回一个sig_return,执行完毕后返回内核态,没有处理信号,则返回主控流程
进程从用户态运行切换到内核态运行:系统调用,中断,异常
信号的阻塞:阻止信号被递达
递达:一个动作——表示信号处理
阻止信号表示在进程pcb 的 block 信号集合中标记信号并且暂时不处理,不代表信号不会被传递,
直到被解除阻塞(从 block 集合中解除)
信号集合 sigset_t -> long int_val[16],使用位图作为信号的位图
对信号阻塞集合进行操作的接口:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
how:
SIG_BLOK blocked = blocked | set //添加
SIG_UNBLOCK blocked = blocked & ~set //移除
SIG_SETMASK blocked = set //直接设置
set: 要阻塞/解除阻塞的信号集合
oldset:被用于保存修改前,blocked集合原有数据
int sigemptyset(sigset_t *set);———>清空信号集合
int sigfillset(sigset_t *set);———>将所有信号添加到set的集合中
int sigaddset(sigset_t *set, int signum);———>将指定信号,添加到指定集合中
int sigpending(sigset_t *set);———>获取未决信号集合
int sigismember(const sigset_t *set, int signum);———>判断是否在信号集合中
int sigdelset(sigset_t *set, int signum);———>从集合中删除信号
哪些信号是不能被阻塞的:
带有unblockable表示不能被阻塞
所有信号中:SIGKILL(强杀), SIGSTOP()信号无法被阻塞,无法被自定义处理,无法被忽略。
可靠信号不会丢失,非可靠信号可能会丢失
竞态条件:程序运行时序的竞争执行(体会竞态条件/理解函数的可重入与不可重入)
函数的可重入/不可重入:
一个函数在不同的执行流下是否可以被重复调用,并且不会出现问题,有可能出问题,这个函数
就是不可重入函数;否则是可重入函数
原子操作:操作不可被打断
函数的可重入/不可重入的关键点在:
是否对全局数据进行了非原子操作
当用户自己设计接口时,如果有可能在多个执行流中被调用,那么就要考虑函数中对全局数据操作的保护
在多个执行流中调用相同的函数,也要考虑这个函数是否可能重入
比如:malloc/free
关键字:volatile 用于修饰一个变量——保持变量内存可见性
每次对变量进行操作都要从内存中重新获取数据
功能:防止编译器过度优化代码
-O2
C语言中常用的关键字:
变量类型关键字(解释内存中数据的特性):
static ->1.正对变量(作用域,证明周期),2.正对函数
const->
volatile->
SIGCHILD信号:
子进程退出时操作系统给父进程发送的信号。(默认处理方式是忽略,导致父进程没有处理子进程的退出,导致僵尸进程)
避免僵尸进程:
1.死等: wait waitpid
2.修改信号处理方式:在信号的回调函数中调用wait/waitpid 处理子进程推出事件
因为SIGCHILD 信号是一个非可靠信号,在大量子进程同时退出的情况下,可能会丢失信号,因此在一次回调时,
把能处理的僵尸子进程全部处理。
sigcb()
以上是关于进程信号的主要内容,如果未能解决你的问题,请参考以下文章
Linux_进程信号(进程信号生命周期_Core Dump调试_进程信号捕捉_系统调用向进程发送信号_阻塞信号_信号集函数_处理信号内核态与用户态_C语言volatile关键字_SIGCHLD信号)(