二 Linux进程间通信( 七 )

运行结果如下:

二 Linux进程间通信

文章插图
代码示例:旧的信号处理函数
sa_flags标志为0代表使用的是旧的信号处理函数
#include <stdio.h>#include <unistd.h>#include <signal.h>void handler(int signo){printf("catch a signal: %d\n", signo);}int main(){int ret = -1;struct sigaction act;//标志为0,代表使用的是旧的信号处理函数指针act.sa_flags = 0;//给阻塞集初始化sigfillset(&act.sa_mask);act.sa_handler = handler;// 信号注册ret =sigaction(SIGINT, &act, NULL);if(ret == -1){perror("sigaction");return 1;}printf("按下任意键退出.....\n");getchar();return 0;}运行结果如下:
二 Linux进程间通信

文章插图
代码示例:新的信号处理函数
#include <stdio.h>#include <unistd.h>#include <signal.h>void handler(int signo,siginfo_t *info,void *context){printf("catch a signal: %d\n", signo);}int main(){int ret = -1;struct sigaction act;//使用新的信号处理函数指针act.sa_flags = 0;//给阻塞集初始化sigfillset(&act.sa_mask);act.sa_handler = handler;// 信号注册ret =sigaction(SIGINT, &act, NULL);if(ret == -1){perror("sigaction");return 1;}printf("按下任意键退出.....\n");getchar();return 0; }
二 Linux进程间通信

文章插图
不可重入函数和可重入函数先看下面一段代码:
#include <stdio.h>#include <signal.h>int a = 10;void SelfAdd(int n){ a = a + n; a = a + n;}void handler(int signo){ SelfAdd(signo);}int main(){ signal(2, handler); SlefAdd(2); printf("%d\n", a); return 0;}上面我写了一个比较简单的代码,我们慢慢分析,当我们在主函数中执行调用SelfAdd时,进入该函数,执行完函数中int a = a + n这句代码后,a变成了12,此时收到2号信号,发生中断
二 Linux进程间通信

文章插图
最后打印a结果是16,其实正常调用该函数的话,打印的应该是18 。像上面这样的因为重入导致结果错乱的函数就叫做不可重入函数 。其中a是一个全局变量 。如果一个函数值访问自己的局部变量或参数,那么这样的函数就叫做可重入函数 。
说的通俗点,不可重入的意思是,如果你定义了一个全局变量,在函数1里面这个变量应该是10,但是有一个函数2改变了这个变量的值,此时本来函数1用的是10,你把他改变了,这就是不安全的,这就是不可重入函数 。
思考一个问题:为什么两个不同的控制流程调用同一个函数,访问同一个局部变量或参数不会造成错乱?
在多线程中,每个线程虽然是资源共享,但是他们的栈却是独有的,所以说局部变量不会造成错乱 。
如果一个函数符合以下条件之一则是不可重入的:
  • 调用了malloc或free,因为malloc也是用全局链表来管理堆的 。
  • 调用了标准I/O库函数 。标准I/O库的很多实现都以不可重入的方式使用全局数据结构 。
  • 函数体内使用了静态的数据结构 。
保证函数可重入性的方法:
  • 在写函数的时候尽量使用局部变量 。(例如寄存器和栈中的变量)
  • 对于要使用的全局变量要加以保护(采取中断、信号量互斥的方法),这样构成的函数就一定是可重入函数 。
使用信号避免僵尸进程SIGCHLD信号
产生条件:1)子进程终止时
?2)子进程接收到SIGSTOP信号停止的时候
?3)子进程处于停止态,接收到SIGCONT后唤醒时
如何避免僵尸进程:
1)最简单的方法,父进程通过wait和waitpid等待子进程函数结束,但是,这会导致父进程挂起 。

经验总结扩展阅读