我们知道进程在收到信号的时候可能不是立即去处理信号的,而是要等到“合适的时候”。这个合适的时候指的是“从内核态切换回用户态的时候”。
内核态通常用来执行OS代码,是一种权限非常高的状态。用户态是一种用来执行普通用户代码的状态,是一种受监管的约束状态。


进程无论如何切换都能看到操作系统,但是不一定都能访问

捕捉方式即上图所示,下面我们来学习一下sigaction函数
#include
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact)
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回0,出错则返回- 1。signo是指定信号的编号。若act指针非空,则根据act修改该信号的处理动作。若oact指针非空,则通过oact传出该信号原来的处理动作.
我们如果想使用该函数自定义处理信号的方式,只需要自定义一个handler赋给sa_handler,将sa_mask使用sigemptyset函数清空再将sa_flags赋值0即可,结构体内其他内容我们不需要关心,下面我们写一段代码测试一下
#include
#include
#include
#include
struct sigaction act, oact;
void handler(int signo)
{
printf("get a signal: %d\n", signo);
//再将信号的操作恢复成最初的样子
sigaction(SIGINT, &oact, NULL);
}
int main()
{
//先将结构体清空
memset(&act, 0, sizeof(act));
memset(&oact, 0, sizeof(oact));
act.sa_handler = handler;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGINT, &act, &oact);//以SIGINT信号测试一下
while(1)
{
printf("I am a process\n");
sleep(1);
}
return 0;
}

一个函数被不同的控制流程调用,有可能在第一次调用还没返回时就再次进入该函数,这称为重入。如果因为重入造成错乱,这样的函数称为不可重入函数,反之就是可重入函数。
符合以下任一条件的函数都是不可重入函数:
这是c语言的一个关键字。我们在讲这个关键字之前先看这样一段代码

这段的代码逻辑很简单,当进程没有收到2号信号的时候,while是一个死循环,直到收到2号信号,flag变为1,此时while循环不成立,打印process quit,然后进程结束。
这样的代码看似没有任何问题,可是当编译器的优化级别较高时就会出现一直死循环的情况。我们先将优化级别调高试试
Makefile
mytest:mytest.c
gcc -O3 -o $@ $^//-O3是最高级别优化
.PHONY:clean
clean:
rm -f mytest

为什么会出现这种情况呢?

这个时候如果我们使用volatile关键字,即使优化级别高,也不会出现这种情况了

volatile的作用就是保持内存的可见性,告知编译器,被该关键字修饰的变量,不允许被优化,对该变量的任何操作,都必须在真实的内存中进行操作。
我们前面讲过wait和waitpid函数清理僵尸进程,父进程可以阻塞等待子进程结束,也可以非阻 塞地查询是否有子进程结束等待清理(也就是轮询的方式)。采用第一种方式,父进程阻塞了就不 能处理自己的工作了;采用第二种方式,父进程在处理自己的工作的同时还要记得时不时地轮询一 下,程序实现复杂。
其实,子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自 定义SIGCHLD信号的处理函数,这样父进程只需专心处理自己的工作,不必关心子进程了,子进程 终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可。
下面我们就自定义SIGCHLD信号的处理方式来验证一下
1 #include <stdio.h>
2 #include <signal.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6 #include <sys/wait.h>
7 #include <sys/types.h>
8 void handler(int signo)
9 {
10 printf("get a signal: %d\n", signo);
11 int ret = 0;
12 while((ret = waitpid(-1, NULL, WNOHANG)) > 0)//为什么这里要循环,因为可能有多个信号需要回收
13 {
14 printf("wait child %d success\n", ret);
15 }
16 }
17 int main()
18 {
19 signal(17, handler);
20 if(fork() == 0)
21 {
22 //child
23 printf("child will quit, pid: %d\n", getpid());
24 sleep(3);
25 exit(2);
26 }
27 while(1)
28 {}
29
30 return 0;
31 }
