二 Linux进程间通信( 五 )


信号的捕获一个进程收到一个信号的时候,可以用以下的方法进行处理:
(1)执行系统默认动作:对大多数信号来说,系统默认动作就是来终止进行 。
(2)忽略此信号(丢弃):接收到此信号后没有任何动作
(3)执行自定义信号处理函数(捕获):用户定义的信号处理函数处理该信号
注意:SIGKILL和SIGSTOP不能更改信号的处理方式 , 因为它们向用户提供了一种使进程终止的可靠方法 。
信号捕捉的过程先思考一个问题:信号是什么时候被进程处理的?
首先,不是立即被处理的 。而是在合适的时候 , 这个合适的时候,具体指的是进程从用户态切换回内核态时进行处理
这句话如何理解,什么是用户态?什么是内核态?

  • 用户态: 处于?户态的 CPU 只能受限的访问内存,用户的代码,并且不允许访问外围设备,权限比较低
  • 内核态: 处于内核态的 CPU 可以访问任意的数据,包括外围设备,?如?卡、硬盘等,权限比较高
注意: 操作系统中有一个cr寄存器来记录当前进程处于何种状态
进程空间分为用户空间和内核空间 。此前我们介绍的页表都是用户级页表 , 其实还有内核级页表 。进程的用户空间是通过用户级页表映射到物理内存上,内核空间是通过内核级页表映射到物理内存上,如下面简图所示:
二 Linux进程间通信

文章插图
二 Linux进程间通信

文章插图
  • 当进程运行在内核空间时就处于内核态,而进程运行在用户空间时则处于用户态 。
  • 最高 1G 的内核空间是被所有进程共享的!
进程有不同的用户空间,但是只有一个内核空间,不同进程的用户空间的代码和数据是不一样的,但是内核空间的代码和数据是一样的 。上面这些主要是想说:进程处于用户态访问的是用户空间的代码和数据 , 进程处于内核态,访问的是内核空间的代码和数据 。
信号捕捉的整个过程:
二 Linux进程间通信

文章插图
从上面的图可以看出,进程是在返回用户态之前对信号进行检测,检测pending位图,根据信号处理动作,来对信号进行处理 。这个处理动作是在内核态返回用户态后进行执行的,所以这里也就回答了开始提出的那一个问题了 。
sigaction函数#include <signal.h>int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);功能:检查或修改指定信号的设置(或同时执行两种操作)参数:signum:要操作的函数act:要设置的对信号的新处理方式(传入方式)oldact:原来对信号的处理方式(传出参数)如果act指针非空,则要改变指定信号的处理(设置),如果oldact指针非空,则系统将此前指定信号的处理方式入oldact返回值:成功:0失败:-1struct sigaction结构体:
struct sigaction { void(*sa_handler)(int);//旧的信号处理函数指针void(*sa_sigaction)(int, siginfo_t *, void *);//新的信号处理函数指针sigset_tsa_mask;//信号阻塞集intsa_flags;//信号处理的方式void(*sa_restorer)(void);//已经弃用};(1)sa_handler、sa_sigaction:信号处理函数指针 , 和signal里面的函数指针用法是一样的,根据情况给两个指针赋值 。
?a)SIG_IGN:忽略该信号
?b)SIG_DFL:执行系统默认的动作
?c)处理函数名:自定义信号处理函数
(2)sa_mask:信号阻塞集,在执行信号处理函数的时候 , 用来临时的屏蔽信号
(3)sa_flags:用于指定信号处理的行为,通常设置为0,表示使用默认的属性 。它可以是一下值的“按位”或“组合”:
  • SA_NOCLDSTOP:使父进程在它的子进程暂停或继续运行时不会收到SIGCHLD信号 。
  • SA_NOCLDWAIT:使父进程在它的子进程退出的时候不会收到SIGCHLD信号,这时子进程如果退出也不会成为僵尸进程 。
  • SA_NODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出整个信号 。
  • SA_RESETHAND:信号处理之后重新设置为默认的处理方式 。
  • SA_SIGINFO:使用sa_sigaction成员而不是sa_handler作为信号处理函数 。
代码示例:
#include <stdio.h>#include <unistd.h>#include <signal.h>void handler(int signo){printf("catch a signal: %d\n", signo);}int main(){struct sigaction act, oact;act.sa_flags = 0;// 选项 设置为0sigfillset(&act.sa_mask);act.sa_handler = handler;// 对2号信号修改处理动作sigaction(2, &act, &oact);while (1){raise(2);sleep(1);}return 0;}

推荐阅读