可重入函数
【分析】
mian函数正在调用insert函数向链表中插入节点。insert函数分为两步,刚刚执行完第一步时此时硬件发生中断,使进程切换到内核。中断处理完毕切换到用户态之前发现有信号未决,于是进入了信号的处理函数,信号的处理函数中同样有insert操作,于是向链表中插入了一个新节点。进行完毕后返回到main函数,按照上下文接着执行main函数中insert函数的第二步,令头节点指向node1。从图中可以看出,这时候出现了一个问题,明明我们想要两个节点插入,可实际仅仅插入了一个,node2丢失了,也就是我们平常说的内存泄漏。
像上例这样,insert函数被不同的控制流调用,在第一次调用还没有返回时就再次进入该函数,这称为重入。如果insert访问的是一个全局链表,就可能因为重入而产生错误,这称为不可重入函数。相反,如果一个函数只访问自己的参数和局部变量,则称为可重入函数。
C语言的volatile关键字
【测试代码】
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
int flag=0;
void handler(int signo)
{
printf("捕捉到信号:%d ;",signo);
printf("flag:0->1\n",flag);
flag=1;
}
int main()
{
signal(2,handler);
while(!flag);
printf("process noaml exti\n");
return 0;
}
标准情况下,键入 CTRL-C ,2号信号被捕捉,执行自定义动作,修改 flag=1 , while 条件不满足,退出循环,进程退出
优化情况下,键入 CTRL-C ,2号信号被捕捉,执行自定义动作,修改 flag=1 ,但是 while 条件依旧满足,进程继续运行!但是很明显flag肯定已经被修改了,但是为何循环依旧执行?很明显, while 循环检查的flag,并不是内存中最新的flag,这就存在了数据二异性的问题。 while 检测的flag其实已经因为优化,被放在了CPU寄存器当中。
如何解决呢?很明显需要 volatil (中文:易变的)
volatile的作用:
避免变量被写到寄存器中,就算写到内存器中,读取时也要先读取内存中的值,然后刷新到寄存器里。
SIGCHLD信号
子进程退出,父进程是是如何知道的呢?
答:①以前:通过调用waitpid函数,阻塞或者非阻塞进行等待--都需要父进程的检测,子进程退出了,父进程不知道!
②现在:其实,子进程退出的时候,会向父进程发送SIGCHLD信号,而父进程默认的处理动作是什么都不做!
【证明子进程会向父进程发送SIGCHLD信号代码】
void handler(int signo)
{
printf("%d:捕捉到信号:%d\n", getpid(), signo);
}
int main()
{
signal(SIGCHLD, handler);
pid_t id = fork();
if (id == 0)
{
int cnt = 5;
while (cnt--)
{
printf("我是子进程,我的pid:%d,ppid:%d\n", getpid(), getppid());
sleep(1);
}
printf("子进程正常退出!\n");
exit(0);
}
sleep(7);
printf("父进程正常退出!\n");
return 0;
}
要求:父进程很忙,编写代码让父进程不要随时检测子进程退出,让子进程退出的时候在告诉父进程要回收了!
【代码实现】
void handler(int signo)
{
printf("%d:捕捉到信号:%d\n", getpid(), signo);
while (1)
{
pid_t id = waitpid(-1, NULL, 0);
if (id > 0)
{
printf("wait child id:%d\n", id);
}
else
break;
}
}
int main()
{
signal(SIGCHLD, handler);
for (int i = 0; i < 5; i++)
{
pid_t id = fork();
if (id == 0)
{
int cnt = 5;
while (cnt--)
{
printf("我是子进程,我的pid:%d,ppid:%d\n", getpid(), getppid());
sleep(1);
}
printf("子进程正常退出!\n");
exit(0);
}
}
while (1)
;
printf("父进程正常退出!\n");
return 0;
}
父进程不想回收子进程,加入这条语句即可:signal(SIGCHLD, SIG_IGN);
信号总结