本文已参与「新人创作礼」活动,一起开启掘金创作之路。
信号的捕捉
signal接口
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signum:信号编号handler:函数指针 使用handler函数替换signum信号的处理方式。
信号自定义处理方式的捕捉流程:
- 在用户态因为系统调用,或中断,或异常,进入内核运行。
- 当完成内核功能,准备返回用户态主控流程前,先检测是否有信号待处理。
- 如果现在有一个信号未处理,并且是自定义处理方式的信号,此时调用
do_signal(),返回用户态,找到自定制处理函数(此时为sigcb函数),去处理这个信号。 (默认或者忽略的处理方式在内核态直接完成) - 事件处理完毕,通过特殊的系统调用
sigreturn()返回内核态,此时信号处理完毕,准备从内核态返回用户态,判断没有未处理信号了,通过sys_sigreturn()从主控制流程中上次被打断的位置继续向下运行。
怎么从用户态切换到内核态?
系统调用、中断、异常。
sigaction接口
使用act动作替换signum原有的处理动作,并且将原有处理动作拷贝到oldact中。
int sigaction(
int signum,
const struct sigaction *act,
struct sigaction *oldact
);
signum:编号。act:动作,用户自己定义一个动作。oldact:旧动作。
结构体:
struct sigaction{
void (*sa_handler)(int);
void (*sa_sigaction)(int,siginfo_t *,void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
}
sigemptyset接口
int sigemptyset(sigset_t *set);
清空一个信号集合。
信号的阻塞
阻塞:暂时阻止信号被递达,信号依然可以注册,但是暂时不处理,解除阻塞之后才会处理。
- 信号的递达:(动作)信号的处理。
- 信号的未决:(状态)信号的产生到处理之前,所处的状态。
pending未决信号集合,每一个信号到来之后,都会在pending位图中修改本信号的位,添加本信号结点。blocked阻塞信号集合。handler动作函数数组。
信号的阻塞过程实际就是在PCB的blocked信号阻塞集合中标记哪些信号到来之后暂时不处理,将blocked位图集合中对应的位置1,进行阻塞这个信号。
【注意】: 9号信号
SIGKILL与19号信号SIGSTOP无法被忽略,无法被阻塞,用户也无法对它们进行自定义处理方式。
sigprocmask接口
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
- 阻塞
set集合中的信号,将原来阻塞的信号保存到oldset中,不关心则置NULL。 - 返回值:成功返回
0,失败返回-1。 how的取值:
-
SIG_BLOCK:阻塞 set 中的信号。 计算方式:mask = mask | set -
SIG_UNBLOCK:对 set 中的信号解除阻塞。 计算方式:mask = mask & (~set) -
SIG_SETMASK:将 set 中的信号设置为阻塞信号。 计算方式:mask = set
sigfileset接口
int sigfileset(sigset_t *set);
将所有信号添加到set集合中。
sigaddset接口
int sigaddset(sigset_t *set,int signum);
将指定信号signum添加到set集合中。
总结:
sigpromask:阻塞 / 解除阻塞信号
sigemptyset: 清空信号集合
sigfillset: 向集合中添加所有信号
sigaddset: 向集合中添加指定信号
sigismember:判断信号信号是否在集合中sigdelset:从集合中移除执行信号sigpending:获取当前进程的未决信号
竞态条件
因为运行时序而造成数据竞争,导致数据二义性。 函数中所完成的操作并非原子性操作,并且操作的数据是一个全局数据。
- 不可重入函数: 如果一个函数操作了全局性数据,并且这个操作不是原子性操作,且这个操作不受保护,则这个函数是一个不可重入函数。
- 不可重入函数:不可在多个时序的运行中重复调用,重复调用有可能会造成数据二义性,数据混乱。
- 可重入函数:在多个时序运行中重复调用,不会造成异常影响,多数为数据二义性问题。
【注】不可重入函数:malloc / free
SIGCHLD信号
子进程退出,系统通过SIGCHLD信号通知父进程,但是此信号的默认处理方式是忽略处理,导致子进程成为僵尸进程。
因此用户选择自定义SIGCHLD信号处理方式:在回调函数中执行waitpid等待子进程退出。为了将同一时间退出的子进程全部处理,防止事件丢失。