信号
由内核向进程发出,或者由其他进程发出等
异步的操作系统机制
除零会触发exception,也会使得内核发出SIGFPE这个信号
多个同时发出的信号可能只会触发一次处理程序,处理程序需要考虑这种情况
defer
sigprocmask()允许进程忽视信号,即暂时屏蔽信号的处理,并不会忽略或丢弃信号
通过阻止异步事件的处理,创建了一个临界区,保证了临界区和信号处理程序不会形成竞争
等待
// simplesh-all-better.c
// wait之前已保证屏蔽了SIGCHILD
static void waitForForegroundProcess(pid_t pid) {
fgpid = pid;
sigset_t empty;
sigemptyset(&empty);
while (fgpid == pid) {
sigsuspend(&empty);
}
unblockSIGCHLD();
}SIGCHLD信号在子进程结束时发出,我们希望程序在检查fgpid(foreground pid,shell需要等待前台进程完成)时不发生数据竞争
sigsuspend()会使进程休眠,并设置新的屏蔽mask,在捕捉到信号后处理信号,并恢复原来的屏蔽mask,然后return
类似于条件变量提供了原子地解锁并等待
在这里休眠后才开始接收SIGCHILD信号,return后又屏蔽SIGCHILD信号
信号处理语义
信号不是function call
信号处理不会立刻执行,可以有delay。多次信号只会有一次处理
异步
信号如何实现
栈处理,sigreturn
linux规定信号处理程序运行期间信号会被block
static const size_t kNumChildren = 5;
static size_t numDone = 0;
int main(int argc, char *argv[]) {
printf("Let my five children play while I take a nap.\n");
signal(SIGCHLD, reapChild);
for (size_t kid = 1; kid <= 5; kid++) {
if (fork() == 0) {
sleep(3 * kid); // sleep emulates "play" time
printf("Child #%zu tired... returns to dad.\n", kid);
return 0;
}
}
while (numDone < kNumChildren) {
printf("At least one child still playing, so dad nods off.\n");
sleep(5);
printf("Dad wakes up! ");
}
printf("All children accounted for. Good job, dad!\n");
return 0;
}
static void reapChild(int unused) {
while (true) {
pid_t pid = waitpid(-1, NULL, WNOHANG);
if (pid <= 0) break; // note the < is now a <=
numDone++;
}
}异步信号安全
可以在异步的信号处理程序中安全调用的
printf不是重入安全的,但是是线程安全的
CPU机制
interrupt
外部,异步
exception
处理器内部,同步
relaxed
保证了单个变量的一致性
保证了只会观察到一个统一的修改顺序
释放获取