关于自定义信号的捕捉:
系统调用、中断、异常时从用户态变为内核态工作(库函数调用时在用户态);当完成内核功能后,从内核态返回用户态;在返回用户态之前判断是否有信号待处理,如果有,并且这个信号处理方式是自定义处理方式时,调用自己写的信号处理函数,调用结束时sig_return再次返回内核态并进行函数中的系统调用;当没有带处理信号时再返回用户态主流程。
信号的阻塞:阻止信号被递达(意为暂时不处理的信号) ;递达:动作——信号处理
block:可以读取或更改进程的阻塞信号集
对block集合进行操作:
int sigprocmask(int how,const sigset_t* set,sigset_t* oldset);
how:
SIG_BLOCK blocked=blocked | set 添加set阻塞
SIG_UNBLOCK blocked=blocked & ~set 解除set阻塞
SIG_SETMASK blocked=set 直接将set设置为阻塞集合
set:要阻塞/解除阻塞的信号集合
oldset:保存修改前block集合原有的数据
(阻塞所有信号后,若再次按下回车,则对所有信号解除阻塞)
在64中信号中SIGKILL、SIGSTOP信号无法被阻塞,无法被自定义修改处理,也无法被忽略
可靠信号不会丢失,不可靠信号有可能会丢失(当函数已被占用时,不再处理)
sigset_t类型对于每种信号用一个bit表示“有效”或“无效”状态,至于这个类型内部如何存储这些bit则依赖于系统 实现,从使用者的角度是不必关心的,使用者只能调用以下函数来操作sigset_ t变量,而不应该对它的内部数据做 任何解释,比如用printf直接打印sigset_t变量是没有意义的
sigset_t类型的函数:
#include <signal.h>
int sigemptyset(sigset_t *set); 清空信号集合
int sigfillset(sigset_t *set); 向集合中添加所有信号
int sigaddset (sigset_t *set, int signo); 向集合中添加指定信号
int sigdelset(sigset_t *set, int signo); 删除指定信号
int sigismember(const sigset_t *set, int signo); 判断信号是否在集合中
sigsuspend();临时充当阻塞集合
int sigpending(sigset_t *set); 获取未决信号集合
竞态条件:程序竞争执行 原子操作:操作不可被打断
可重入与不可重入:一个函数在不同的执行流下是否可以被重复调用并且不会出新问题,有可能出问题就是不可重入函数
函数的可重入与不可重入区分关键:是否对全局数据进行了非原子性操作
volatile:修饰变量,保持变量内存可见性,每次都从内存中重新获取变量数据防止斌一起过度优化
SIGCHLD:忽略子进程。父进程无法得知子进程何时退出,浪费父进程,所以子进程退出后,操作系统通过SIGCHLD信号通知父进程,由于SIGCHLD默认为忽略信号,父进程忽略子进程退出,导致僵尸进程。父进程可以自定义SIGCHLD信号的处理函数,这样父进程只需专心处理自己的工作,不必关心子进程了,子进程终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可。
当有多个进程退出时可能信号丢失,导致自己新的信号处理函数纸杯回调一次,只处理了一个子进程退出,因此在自定义处理函数中应修改条件:while(waitpid(-1,NULL,WNOHANG)>0);处理退出的子进程直到不能处理