模板网站和定,网站首页被k还有救吗,谷歌seo济南,诚信宁津建设网站信号Ⅲ #x1f517; 接上篇七、可重入函数八、volatile 关键字九、SIGCHLD 信号 #x1f517; 接上篇
#x1f449;#x1f517;进程信号篇Ⅰ#xff1a;信号的产生#xff08;signal、kill、raise、abort、alarm#xff09;、信号的保存#xff08;core dump#x… 信号Ⅲ 接上篇七、可重入函数八、volatile 关键字九、SIGCHLD 信号 接上篇
进程信号篇Ⅰ信号的产生signal、kill、raise、abort、alarm、信号的保存core dump
进程信号篇Ⅱ信号的阻塞及保存sigset_t, sigprocmask, sigpending、信号的处理、信号的捕捉sigaction 七、可重入函数
不同的执行流中同一个函数被重复进入。
有的函数在功能上重新进入后会产生我们不想看到的结果这样的函数叫 不可重入函数。
对于没有重入问题的函数我们叫做 可重入函数Reentrant
例如insert 函数被不同的控制流程调用有可能在第一次调用还没返回时就再次进入该函数这称为重入。
insert 函数访问一个全局链表有可能因为重入而造成错乱。两个不同的控制流程 调用同一个函数 访问它的 **同一个局部变量或参数**就是可重入的。如果一个函数符合以下条件之一则是不可重入的: 调用了 malloc 或 free因为 malloc 也是用全局链表来管理堆的。 调用了标准 I/O 库函数。标准 I/O 库的很多实现都以不可重入的方式使用全局数据结构。 八、volatile 关键字 volatile 这个关键字可以声明让编译器每次都去内存中读取数据可以保证内存的可见性。 #include stdio.h
#include signal.h/*volatile*/ int quit 0; // 保证内存可见性void handler(int signo)
{printf(change quit from 0 to 1\n);quit 1;printf(quit: %d\n, quit);
}int main()
{signal(2, handler);while(!quit); // 这里不携带代码块故意让编译器认为在 main 中quit 只做检测作用printf(main quit 正常\n);return 0;
}在一些编译版本下如此叫 while 不挟带代码块可以让编译器对 只用作检测的 quit 做优化。原本每次都要从内存中 load 进 cpu 的寄存器中再进行判断计算优化后编译器认为 quit 只是检测用的便直接把 quit 的值 load 进寄存器后每次直接从寄存器中读取数据。这种优化就导致了内存不可见。
对上述代码 quit 进行 volatile 声明就表示要求编译器每次都要从内存里去重新读取数据。不让直接使用寄存器中的数据保证内存数据可见。
九、SIGCHLD 信号
进程一章讲过用 wait 和 waitpid 函数清理僵尸进程父进程可以阻塞等待子进程结束也可以非阻塞地查询是否有子进程结束等待清理也就是轮询的方式。
采用第一种方式父进程阻塞了就不能处理自己的工作了采用第二种方式父进程在处理自己的工作的同时还要记得时不时地轮询一下程序实现复杂。
其实子进程在终止时会给父进程发 SIGCHLD 信号该信号的默认处理动作是 忽略父进程可以自定义 SIGCHLD 信号的处理函数,这样父进程只需专心处理自己的工作不必关心子进程了子进程终止时会通知父进程父进程在信号处理函数中调用 waitpid 清理子进程即可。
代码举例父进程 fork 出子进程子进程调用 exit(1) 终止父进程自定义 SIGCHLD 信号的处理函数在其中调用 wait 获得子进程的退出状态并打印。
#include stdio.h
#include stdlib.h
#include signal.h
#include unistd.h
#include sys/wait.hpid_t id;void waitprocess(int signo)
{printf(我%d 捕捉到一个信号%d\n,getpid(),signo);sleep(5); // 这期间子进程将处于 僵尸状态// 实现只将部分退出的回收没有退出需求的不处理while(1){ // 如果 WNOHANG 位置填 0会导致遇到没有退出的子进程时就 hang 住了没法往下继续运行// WNOHANG 意在有的话给退出没有的话就返回pid_t res waitpid(-1, NULL, WNOHANG); // -1 代表回收任意一个子进程if(res 0){printf(wait success,res: %d, id: %d\n, res, id);}else break; // 如果没有子进程了就 break}printf(handler done...\n);
}int main()
{signal(SIGCHLD, waitprocess);int i 1;for(; i 10; i){id fork();if(id 0){int count 5;while(count){printf(我是子进程我的 pid%dppid%d\n, getpid(), getppid());sleep(1);count--;}exit(1);}}while(1){sleep(1);}return 0;
}如果父进程没啥事要干可以在下面 waitpid
如果父进程很忙而且不退出可以选择信号的方式事实上由于 UNIX 的历史原因要想不产生僵尸进程还有另外一种办法父进程调用 sigaction 将 SIGCHLD 的处理动作置为 SIG_IGN这样 fork 出来的子进程在终止时会自动清理掉不会产生僵尸进程也不会通知父进程。
系统默认的忽略动作和用户用 sigaction 函数自定义的忽略通常是没有区别的但 这是一个特例。此方法对于 Linux 可用但不保证在其它 UNIX 系统上都可用。
总之上面的代码可以改写成这样
int main()
{//signal(SIGCHLD, waitprocess);sigaction(SIGCHLD, SIG_IGN);int i 1;for(; i 10; i){id fork();if(id 0){int count 5;while(count){printf(我是子进程我的 pid%dppid%d\n, getpid(), getppid());sleep(1);count--;}exit(1);}}while(1){sleep(1);}return 0;
}如果本文对你有些帮助请给个赞或收藏你的支持是对作者大大莫大的鼓励(✿◡‿◡) 欢迎评论留言~~