信号(进程间通信3)

信号(软中断)中断机制的模拟 (还有一种)

 信号是进程间通信机制中唯一的异步通信机制,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。信号机制除了基本通知功能外,还可以传递附加信息。

信号的中断类似于系统调用的中断,所以所有信号的中断标志都是一样的,然后去查信号向量表对应的就是tast_stuct 中sig_struct 每个 请求中断都可以被屏蔽不做出信号,信号也有这种属性。
 struct signal_struct
{
 atomic count;
struct k_sigacton action[_NSIG]; //每个元素相当于一个信号向量。 具体的信号
spinlock_t siglock

};


对于每个信号向量而言都有几种方式。SIG_DFL 默认 SIG_IGN 忽略 SIG_ERR 错误
除此之外还有定义自定义信号处理函数。

信号的处理流程


**腾讯的一个面试题 系统如何将一个信号通知到进程?
1) A进程调用信号发送函数,发送信号给B,这是软中断,所以A进程会进入内核态运行操作系统的信号调度代码
2) 操作系统发现B进程正在运行,于是写入管理B进程的某个数据结构
3) 操作系统返回给A,A继续执行
4) B进程分配的处理器时间用完了,被时钟硬件中断
5) 操作系统的时钟硬件中断处理函数准备挂起B进程,也就是把寄存器和函数堆栈保存起来,发现B进程收到了singal
6) 操作系统在保存好B进程的stack和register后,新开stack(为了不干扰B进程真正的代码stack),激活B进程,B进程的信号处理函数。

每个信号的向量的结构体如上图所示,
里面定义了函数指针信号处理函数,定义了sa_mask 这个东西(不一样在后面多线程编程中会作为一个大点进行梳理)(信号掩码)
首先说的这个东西就要说可靠信号和不可靠信号。

sa_mask 来历在早期的信号处理过程中对于信号的嵌套处理采用的是每当执行一个信号处理函数时候就会自动把信号向量表 中的函数指针设置为SIG_DFL ,下次信号来了就不会进入相同的服务子函数中去。如果还需要就需要在设置,但是这种是有缺点的。比如同时来了多个SIGINT如果第一个信号处理函数进去没执行,第二个就来了那末他看到的SIGINT处理方式改为SIG_DFL  就会kill 进程。所以人们把这种早期的信号处理机制成为不可靠信号。sigmask他的作用是如果屏蔽自身信号或者屏蔽其他信号防止嵌套调用。但是屏蔽并不是丢弃。他只是暂时的遮蔽一下啊等处理函数完了就去处理。并且每次处理完成 并不会把它设置成默认。

现在在看一下sa_flag 用这个东西
sa_flags 用来设置信号处理的其他相关操作,下列的数值可用。
SA_RESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL
SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用
SA_NODEFER :一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。但是如果设置了 SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号。

进程task struct signal中断请求寄存器 如果有信号来就将这一位置为1,中断程序处理就将这一位置为0;但是如果在中断处理之中,有又同时来了多个信号,更不就不知道有几个信号过来所以最后之后执行完成一次(只限于前32个信号对于后面加入了未决队列将所有没有被处理的信号放到未就队列上(但是由于历史原因前32个为了兼容所以就不会放在未决队列上通过老信号调用老接口信心号用新的方式所以我们不能把前32个称为不可靠信号好多人都这样叫但是不可靠和这还是有区别的))

具体对于我们而言信号的调用常用的有两种 signal 和sigaction 。

具体使用身上而言signal从用户区获取更少的的东西,好多都给了默认的。而sigacton都是要自己赋值的。
具体使用代码、
   
  1 #include <signal.h>
  2 #include <stdio.h>
  3 #include <unistd.h>
  4 #include <errno.h>
  5 #include <stdlib.h>
  6 #define BUFSIZE 1024  
  7 
  8 void handler(int sig)
  9 {
 10 }
 11 
 12 int main()
 13 {
 14     struct sigaction act;
 15     act.sa_flags = 0 ;
 16     sigemptyset(&act.sa_mask);
 17     act.sa_handler = handler;
 18     if(sigaction(SIGINT,&act,0) == -1)
 19         exit(-1);
 20    char buf[BUFSIZE] = {0};                                                                                                                                                              
 21   // signal(SIGINT,handler);
 22    read(0,buf,BUFSIZE-1);
 23    if(errno==EINTR)
 24    {
 25         printf("SIGINT INTERRPUT\n");
 26    }
 27     write(1,buf,BUFSIZE);
 28 }
~      
对于signal我就不贴了具体上而言和一前的区别是signal并没有如同我书上说的将flag设置为SA_ONSHOT 和SA_NOMASK;并且 反而他默认 的加上了SA _RESTART.可能我的linux中对于signal已经进行了优化。

实时信号和非实时信号前32个都是非实时信号后面的都是实时信号。SIGKILL 和SIGSTOP不允许改变相应。
接下来说上面的问题,上面大多数都在说中断请求标志位,但是对于中断屏蔽标志位没有说,所以我来整理整理。
中断屏蔽标志位都有一个blocked位图,他会一直有作用(这个才是多线程下的pthreadmask和)sigpocmask这个是整个进程的。

Sigwait()函数


这个函数的作用
因为 sigwait()使用了串行的方式处理信号的到来,为避免信号的处理存在滞后,或是非实时信号被丢失的情况,处理每个信号的代码应尽量简洁、快速,避免调用会产生阻塞的库函数。

还有一些问题信号处理函数尽量简单所以在多线程处理信号问题有两种方法。一种linux高性能服务器中讲的统一事件源,信号处理函数中放入管道的写段。如果信号触发管道中写东西就行。     或者专门写一个线程去处理该信号其他线程利用pthreadmsk屏蔽。










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值