• 信号 signal sigaction补充


    目前linux中的signal()是通过sigation()函数实现的。

    由signal()安装的实时信号支持排队,同样不会丢失。

    先看signal 和 sigaction 的区别:

    关键是

     struct sigaction act;  

    里面有三个部分,除了 signal函数会关注的 sa_handler 之外,

    还有 sa_mask,这里面可以提供阻塞功能(类似于sigprocmask)

    1. sigemptyset(&act.sa_mask);  
    2. sigaddset(&act.sa_mask, SIGQUIT);  

    类似于:

    sigset_t blockset;     
         sigemptyset(&blockset);     
         sigaddset(&blockset,SIGINT);     
         sigaddset(&blockset,SIGTSTP);     

    意思是在sigaction处理过程中,阻塞这些信号。

    千万注意,开始我也理解错了,是阻塞不是屏蔽也不是忽略,在函数之后还是要处理的。

    这样能够防止处理函数被打断。

    被信号打断的函数会返回错误,errno是EINTR,如果不想手动处理,可以看下面这个sa_flags.

    还有一个 sa_flags,如果 |= SA_RESTART 就能够自动重启被这个信号打断的函数。

    sigaction调用的时候 sigaction(SIGINT, &act, 0);  

    第三个参数是 struct sigaction *oact,表示获得返回的原来的sigaction

    信号内部实现:

    下面这一篇对于信号的内部实现机制,讲的不错:

    http://www.cnblogs.com/blueyunchao0618/p/5953862.html

    当然,我之前的文章也讲的不错。可以看看。

    信号的本质是软件层次上对中断的一种模拟(软中断)。它是一种异步通信的处理机制,事实上,进程并不知道信号何时到来。

    2.信号来源

    (1)程序错误,如非法访问内存

    (2)外部信号,如按下了CTRL+C

    (3)通过kill或sigqueue向另外一个进程发送信号

    1. 信号的生命周期

    信号产生->信号注册->信号在进程中注销->信号处理函数执行完毕

    (2)信号注册

    指的是在目标进程中注册,该目标进程中有未决信号的信息:

    struct sigpending pending:
    struct sigpending{
    struct sigqueue *head, **tail;
    sigset_t signal;
    };

    struct sigqueue{
    struct sigqueue *next;
    siginfo_t info;
    }

    注:也有地方写的是 *tail,未深究。

    其中 sigqueue结构组成的链称之为未决信号链,sigset_t称之为未决信号集。

    信号注册的过程就是将信号值加入到未决信号集siginfo_t中,将信号所携带的信息加入到未决信号链的某一个sigqueue中去。

    对于可靠的信号,可能存在多个未决信号的sigqueue结构,对于每次信号到来都会注册。而不可靠信号只注册一次,只有一个sigqueue结构。

    (3)信号在目标进程中注销

     在进程的执行过程中,每次从系统调用或中断返回用户空间的时候,都会检查是否有信号没有被处理。如果这些信号没有被阻塞,那么就调用相应的信号处理函数来处理这些信号。

    则调用信号处理函数之前,进程会把信号在未决信号链中的sigqueue结构卸掉。是否从未决信号集中把信号删除掉,对于实时信号与非实时信号是不相同的。

    非实时信号:由于非实时信号在未决信号链中只有一个sigqueue结构,因此将它删除的同时将信号从未决信号集中删除。

    实时信号:由于实时信号在未决信号链中可能有多个sigqueue结构,如果只有一个,也将信号从未决信号集中删除掉。如果有多个那么不从未决信号集中删除信号,注销完毕。

    (3)处理过程:

    程序运行在用户态时->进程由于系统调用或中断进入内核->转向用户态执行信号处理函数->信号处理函数完毕后进入内核->返回用户态继续执行程序

    :要多几次内核态切换。原因下面有写

    首先程序执行在用户态,在进程陷入内核并从内核返回的前夕,会去检查有没有信号没有被处理,如果有且没有被阻塞就会调用相应的信号处理程序去处理。首先,内核在用户栈上创建一个层,该层中将返回地址设置成信号处理函数的地址,这样,从内核返回用户态时,就会执行这个信号处理函数。当信号处理函数执行完,会再次进入内核,主要是检测有没有信号没有处理,以及恢复原先程序中断执行点恢复内核栈等工作,这样,当从内核返回后便返回到原先程序执行的地方了。

    注:上面也说明了,内核态下是可以访问用户栈的。

  • 相关阅读:
    [luogu p4447] [AHOI2018初中组]分组
    咕咕咕通知
    [luogu p3817] 小A的糖果
    [luogu p1228] 地毯填补问题
    [luogu p1259] 黑白棋子的移动
    [luogu p3612] [USACO17JAN]Secret Cow Code S
    [luogu p1990] 覆盖墙壁
    [luogu p1928] 外星密码
    [luogu p2036] Perket
    [luogu p2392] kkksc03考前临时抱佛脚
  • 原文地址:https://www.cnblogs.com/charlesblc/p/6506064.html
Copyright © 2020-2023  润新知