sigaction函数解析

sigaction函数解析sigaction函数的功能是检查或修改与指定信号相关联的处理动作(可同时两种操作)。他是POSIX的信号接口,而signal()是标准C的信号接口(如果程序必须在非POSIX系统上运行,那么就应该使用这个接口)给信号signum设置新的信号处理函数act,同时保留该信号原有的信号处理函数oldactint sigaction(int signo,

大家好,又见面了,我是你们的朋友全栈君。

sigaction函数的功能是检查或修改与指定信号相关联的处理动作(可同时两种操作)。

他是POSIX的信号接口,而signal()是标准C的信号接口(如果程序必须在非POSIX系统上运行,那么就应该使用这个接口)

给信号signum设置新的信号处理函数act, 同时保留该信号原有的信号处理函数oldact

int sigaction(int signo,const struct sigaction *restrict act,

              struct sigaction *restrict oact);

结构sigaction定义如下:

struct sigaction{

  void (*sa_handler)(int);
   sigset_t sa_mask;
  int sa_flag;
  void (*sa_sigaction)(int,siginfo_t *,void *);
};

sa_handler字段包含一个信号捕捉函数的地址

sa_mask字段说明了一个信号集,在调用该信号捕捉函数之前,这一信号集要加进进程的信号屏蔽字中。仅当从信号捕捉函数返回时再将进程的信号屏蔽字复位为原先值。

sa_flag是一个选项,主要理解两个

SA_INTERRUPT 由此信号中断的系统调用不会自动重启
SA_RESTART 由此信号中断的系统调用会自动重启

SA_SIGINFO 提供附加信息,一个指向siginfo结构的指针以及一个指向进程上下文标识符的指针

最后一个参数是一个替代的信号处理程序,当设置SA_SIGINFO时才会用他。

例子:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void show_handler(int sig)
{

    printf(“I got signal %d\n”, sig);
    int i;
    for(i = 0; i < 5; i++) {

        printf(“i = %d\n”, i);
        sleep(1);
    }
}

int main(void)
{

    int i = 0;
    struct sigaction act, oldact;
    act.sa_handler = show_handler;
    sigaddset(&act.sa_mask, SIGQUIT); //见注(1)
    act.sa_flags = SA_RESETHAND | SA_NODEFER; //见注(2)
    //act.sa_flags = 0; //见注(3)

    sigaction(SIGINT, &act, &oldact);
    while(1) {

        sleep(1);
        printf(“sleeping %d\n”, i);
        i++;
    }
}


注:
(1)    如果在信号SIGINT(Ctrl + c)的信号处理函数show_handler执行过程中,本进程收到信号SIGQUIT(Crt+\),将阻塞该信号,直到show_handler执行结束才会处理信号SIGQUIT。


(2)    SA_NODEFER       一般情况下, 当信号处理函数运行时,内核将阻塞<该给定信号 — SIGINT>。但是如果设置了SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号。 SA_NODEFER是这个标记的正式的POSIX名字(还有一个名字SA_NOMASK,为了软件的可移植性,一般不用这个名字)    
       SA_RESETHAND    当调用信号处理函数时,将信号的处理函数重置为缺省值。 SA_RESETHAND是这个标记的正式的POSIX名字(还有一个名字SA_ONESHOT,为了软件的可移植性,一般不用这个名字)   


(3)    如果不需要重置该给定信号的处理函数为缺省值;并且不需要阻塞该给定信号(无须设置sa_flags标志),那么必须将sa_flags清零,否则运行将会产生段错误。但是sa_flags清零后可能会造成信号丢失!

http://webcache.googleusercontent.com/search?q=cache:B2HsD1Zf2f8J:hi.baidu.com/operationsystem/blog/item/bb215411f4dc61f4c2ce79e6.html/cmtid/c150423c8b8feae13d6d97b0+sigaction&cd=1&hl=zh-CN&ct=clnk




正文 
我觉得这是挺好理解的,就好比在系统这个大进程里运行许多派生的进程,为了协调这些派生出的子进程,就必然要使用一些手段来通知监视。而信号就是这样一种系统级别的全局变量的通知。想想在写程序中,多个函数协调一个全局函数的情形。。。 
the signal is an event generated by the UNIX and Linux systems in response to some condition,upon receipt of which a process may in turn take some action. 


函数 
我想我需要如下系列的函数,修改本身的信号处理函数,对其他进程发送信号, 
#include <signal.h> 
void (*signal(int sig, void (*func)(int)))(int); 

which 
is the previous value of the function set up to handle this signal, or one of these two special values: 

Java代码  
收藏代码

  1. SIG_IGN             Ignore the signal.  
  2. SIG_DFL             Restore default behavior.  





比如想捕捉SIGINT信号,但是我们又只想捕捉一次,就可以用到DFL信号来恢复之前的行为,可能会是这样 


但要注意的是,It is no safe to call all function, such as printf, from within a signal handler.A useful technique is to use a signal handler to set flag and then check that flag from the main 


pg and print a message if required.Toward the end of the chapter, you will find a list of calls that can safely be made inside signal handlers. 

Java代码  
收藏代码

  1. void ouch(int sig)  
  2. {  
  3.     printf(“OUCH ! – I got signal %d\n”, sig);  
  4.     signal(SIGINT, SIG_DFL);      
  5. }  
  6.   
  7. int main(int argc, char **argv)  
  8. {  
  9.     signal(SIGINT, ouch);     
  10.       
  11.     while(1)  
  12.     {  
  13.         printf(“Hello World!\n”);  
  14.         sleep(1);     
  15.     }  
  16.       
  17.     return 0;  
  18. }  





来点强壮的
 


X/Open规范的更新更健壮的接口 


#include <signal.h> 


int sigaction(int sig, const struct sigaction *act, struct sigaction *oact); 


oact如果不为空,将把前次act的状态保存下来。 


这个函数主要的是加入了信号集(sa_mask)这个功能。比如前面提到的,如果信号先发出而后调用pause(),则遗失掉这个信号。采用信号集他可以先收集或者说阻塞不传递给主进程,由主进程再来自主调用和处理。 



Java代码  
收藏代码

  1. void (*) (int) sa_handler     /* function, SIG_DFL or SIG_IGN  
  2. sigset_t sa_mask          /* signals to block in sa_handler  
  3. int sa_flags             /* signal action modifiers,SA_RESETHAND,具有reset功能  





对这个信号集有如下几种操作: 


初始为空集,初始为所有已有的信号,增加新信号,删除指定信号 

Java代码  
收藏代码

  1. #include <signal.h>  
  2. int sigaddset(sigset_t *set, int signo);  
  3. int sigemptyset(sigset_t *set);  
  4. int sigfillset(sigset_t *set);  
  5. int sigdelset(sigset_t *set, int signo);  



然后是一个批处理的函数 

Java代码  
收藏代码

  1. int sigprocmask(int how, const sigset_t *set, sigset_t *oset);  
  2. SIG_BLOCK   The signals in set are added to the signal mask.  
  3. SIG_SETMASK     The signal mask is set from set.  
  4. SIG_UNBLOCK     The signals in set are removed from the signal mask.  







判断是否是当前信号集里的信号 


int sigismember(sigset_t *set, int signo); 






最后就是最重要的,对信号集里的信号进行操作,假设已经把信号收集到,主程序可以阻塞/非阻塞式的调用。(不过非阻塞的调用好象不能自动把收到的信号清除掉,阻塞式的就可以自动清除)。 




If a signal is blocked by a process, it won’t be delivered, but will remain pending. A program can determine 


which of its blocked signals are pending by calling the function sigpending. 


#include <signal.h> 


int sigpending(sigset_t *set); 






A process can suspend execution until the delivery of one of a set of signals by calling sigsuspend. This 


is a more general form of the pause function we met earlier. 


#include <signal.h> 


int sigsuspend(const sigset_t *sigmask); 




sigaction Flags 


The sa_flags field of the sigaction structure used in sigaction may contain the following values to 


modify signal behavior: 



Java代码  
收藏代码

  1. SA_NOCLDSTOP        Don’t generate SIGCHLD when child processes stop.  
  2. SA_RESETHAND        Reset signal action to SIG_DFL on receipt.  
  3. SA_RESTART      Restart interruptible functions rather than error with EINTR.  
  4. SA_NODEFER      Don’t add the signal to the signal mask when caught.  







通过以上的操作介绍,似乎已经很完美了。但是最大的问题来了,先了解几个概念: 


不完全重入函数:即可能被其他信号中断触发EINTR 




假设我们现在正在执行一个信号处理函数,突然发生一个中断,那么该信号函数将被打断。在一次情况下似乎没什么问题。但是假如连续性的被信号打断,而导致函数不断重启。就比如执行到一半退出,又执行,又退,又执行。。那么可能就有问题发生。有两个方法可以解决 


1、根本方法,用完全重入函数。如下图所示英文版 P474 


2、如果非不得以,那就不让信号来阻断信号函数的运行。可以用SA_RESTART标志把信号先放到屏蔽集的缓冲区里 



Java代码  
收藏代码

  1. SA_RESTART(似乎默认也是这个行为)  
  2. void ouch(int sig)  
  3. {  
  4.  //..  
  5.  select()//10秒  
  6.  //..  
  7. }  
  8.   
  9. int main(int argc, char **argv)  
  10. {  
  11.     struct sigaction act, act_g;  
  12.     act.sa_handler = ouch;  
  13.     act.sa_flags = SA_RESTART;   //设置重启  
  14.     sigemptyset(&act.sa_mask);  
  15.     sigaddset(&act.sa_mask, SIGTERM);  
  16.   
  17.     if( –1==sigaction(SIGTERM, &act, 0))  //捕捉SIGTERM  
  18.     {  
  19.         perror(“sigaction\n”);    
  20.     }  
  21.   
  22.     select//10秒  
  23.   
  24. }  



操作如下,首先运行该程序,然后在另一个tty里发送一个kill命令(在第一个select未结束前),那么将进入信号处理函数ouch,接着在ouch的select运行之际,再次发送kill,因为select也不完全函数,将会被再次打断,而又一次进入信号。但是因为设置了SA_RESTART,ouch将不会被打断,而是执行完后,接着执行响应第二次的信号函数。 




SA_NODEFER
 


和上面一样的操作,发送第二次KILL信号时,第一个信号函数马上中断(由于是select),又再次进入信号函数。这里加点东西打印可以更清楚些。 




流程图
 


以上这两种实现,有个东西起了至关重要的作用。那就是进程的信号屏蔽字。原理流程大概是这样的,当向一个进程发送信号时,可根据是否被屏蔽掉而发送至进程信号屏蔽字集合中或者进程本身。对于后者,sigaction可直接捕捉到,而前者,可以看成是一个暂存的集合,可用sigpending来取得。通常的情况是,已经用了sigaction函数直接获取信号,如果再次触发信号会马上跳出当前正在执行的信号函数而再次执行信号函数。而SA_RESTART这个标志应该是将信号保存到被屏蔽的信号集合里等待下次取出后执行,保证了当前函数的运行。SA_NODEFER就不做这一操作,直接发送到进程,让sigaction继续马上捕捉。      


整个走向可看下图,当然这里没有参考系统源码,必然存在着一定的疏忽 




sigaction函数解析
 


目前已知的对屏蔽集的操作有pending/suspend函数,SA_RESTART,SA_NODEFER标志操作。写到这里,把屏蔽集合集看成一个临时的信号存放缓冲区更形象点。 

http://blog.chinaunix.net/uid-1877180-id-3011232.html

http://lin-style.iteye.com/blog/296917

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/141397.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • pycharm使用matplotlib绘图学习笔记「建议收藏」

    pycharm使用matplotlib绘图学习笔记「建议收藏」#encoding=utf-8importnumpyasnpdefmain():importmatplotlib.pyplotasplt##lesson1:画图#x=np.linspace(-1,1,50)#x=np.linspace(-np.pi,np.pi,256,endpoint=True)#c,s=np.cos(x),np.sin(x)#plt.figure(1)#plt.plot(x,.

  • 【深度学习】梯度下降算法和随机梯度下降算法「建议收藏」

    【深度学习】梯度下降算法和随机梯度下降算法「建议收藏」导语梯度是神经网络中最为核心的概念,在介绍梯度之前我们要先知道数学中的导数以及偏微分的理论概念。导数这里套用维基百科上的介绍,导数描述了函数在某一点附件的变化率,导数的本质是通过极限对函数进行局部的线性逼近,当函数\(f\)的自变量在一点\(x_0\)上产生一个增量\(△x\)时,则函数值的增量\(△y\)与自变量的增量\(△x\)的比值在\(△x\)趋于0时的极限存在,即为\(f\)在\(…

  • list转json字符串

    list转json字符串使用Gson把list转成json字符串com.google.gson.Gson@GetMapping(“/valueTest”)publicStringvalueTest(){List<Map<String,Object>>list=newArrayList<>();Map<String,Object>map1=newHashMap<>();map1

    2022年10月18日
  • 更换好用conda源「建议收藏」

    更换好用conda源「建议收藏」window是更换conda源windows在用户目录中的.condarc文件中替换以下源(如果没有这文件则需要自己创建)channels:-https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/-https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/-https://mirrors.tuna.tsinghua.edu.cn/anaconda/

  • pycharm默认主题_pycharm主题插件

    pycharm默认主题_pycharm主题插件一、Pycharm基本设置(小白篇)1、打开Pycharm设置,【File】-【Settings】2、设置解释器,【File】-【Settings】-【Project:项目名字】-【ProjectInterpreter】-【设置图标】-【Add】-【浏览到目标解释器】,选择相应解释器即可。3、设置pycharm主题,【File】-【Settings】-【Appearance&Behavior】-【Appearance】;Theme:修改主题、Usecustomf

  • mysql 5.5 驱动jar包_MySQL驱动jar包下载「建议收藏」

    mysql 5.5 驱动jar包_MySQL驱动jar包下载「建议收藏」MySQLJDBC驱动是Java连接MySQL数据库时要用到的驱动包,MySQL驱动就是赋值外界与数据的连接接口,对于专业的Java开发人员一定会使用到的MySQL驱动Jar包的,有需要的赶快来试试吧!【功能特点】易于开发的特点,包括通过自动注册服务提供商机制,标准化的连接有效性检查和分类的SQLExceptions的基础上可恢复/重试能力和一流的底层错误。DriverManager隔离解开包…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号