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)
blank

相关推荐

  • 各种聚类算法(原理+代码+对比分析)最全总结「建议收藏」

    各种聚类算法(原理+代码+对比分析)最全总结「建议收藏」序言还是要持续总结,持续积累。一、聚类的目标使同一类对象的相似度尽可能地大;不同类对象之间的相似度尽可能地小。二、聚类算法分类1.基于划分给定一个有N个元组或者纪录的数据集,分裂法将构造K个分组,每一个分组就代表一个聚类,K<N。特点:计算量大。很适合发现中小规模的数据库中小规模的数据库中的球状簇。算法:K-MEANS算法、K-MEDOIDS算法、CLARANS算法2….

  • 硬盘恢复分区_怎么把efi分区删掉

    硬盘恢复分区_怎么把efi分区删掉Windows系统在安装的时候,会自动为我们的磁盘划分一个恢复分区和一个EFI分区。如果后面不打算再用这些分区的时候,却发现无法删除。本文将提供解决方法。因为误操作会导致数据丢失,所以我将两种不同的解决方法分开成两篇文章以避免干扰:EFI分区/恢复分区不可删除?你需要使用命令行了(配合鼠标操作)EFI分区/恢复分区不可删除?你需要使用命令行了(全命令行操作)本文内容无法删…

  • golang面试题(带答案)[通俗易懂]

    golang面试题(带答案)[通俗易懂]1.下面代码输出什么,为什么 //make([]T,length,capacity) s1:=[]int{1,2,3} fmt.Println(s1,”哈哈”)//[123] s2:=s1 fmt.Println(s1,”哈哈”)//[123] fori:=0;i<3;i++{ s2[i]=s2[i]+1 } fmt.Println(s1)//[234] fmt.Println(s2)//[234][12

  • oracle 创建索引的sql语句_oracle数据库创建索引语句

    oracle 创建索引的sql语句_oracle数据库创建索引语句CREATEINDEXPOLICYIMPART_INDEXONROOTE.W_POLICYIMPART(POLICYIDASC,IMPARTCODEASC,CUSTOMERTYPEASC)POLICYIMPART_INDEX索引名称ROOTE.W_POLICYIMPART表名

  • PHP文件锁

    PHP文件锁一、文件锁是什么?    顾名思义,对文件上锁。    可以通过“进门”的实际情况来理解:    有多个人要通过一个大门到食堂里吃饭,但食堂只有一个座位。     食堂管理员A有点偷懒,不想等那么久,于是就告诉大家,中午都可以来食堂吃饭,但是要跑快点才行,只有一个座位,第一个到的人就可以在食堂吃饭,然后就会锁门,其他人看到门锁上了就哪来的回哪去吧,这…

  • faster rcnn 详解

    faster rcnn 详解

发表回复

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

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