详解sigaction「建议收藏」

详解sigaction「建议收藏」一、内核如何实现信号的捕捉如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下:1.用户程序注册了SIGQUIT信号的处理函数sighandler。2.当前正在执行main函数,这时发生中断或异常切换到内核态。3.在中断处理完毕后要返回用户态的main函数之前检查到有信号SIGQU

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

一、内核如何实现信号的捕捉

如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下:
1. 用户程序注册了SIGQUIT信号的处理函数sighandler。
2. 当前正在执行main函数,这时发生中断或异常切换到内核态。
3. 在中断处理完毕后要返回用户态的main函数之前检查到有信号SIGQUIT递达。
4. 内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函数,sighandler和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是两个独立的控制流程。
5. sighandler函数返回后自动执行特殊的系统调用sigreturn再次进入内核态。
6. 如果没有新的信号要递达,这次再返回用户态就是恢复main函数的上下文继续执行了。

详解sigaction「建议收藏」

上图出自ULK。

二、sigaction函数

#include <signal.h>

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

sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回0,出错则返回-1。signo是指定信号的编号。若act指针非空,则根据act修改该信号的处理动作。若oact指针非空,则通过oact传出该信号原来的处理动作。act和oact指向sigaction结构体:  struct sigaction {

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

将sa_handler赋值为常数SIG_IGN传给sigaction表示忽略信号,赋值为常数SIG_DFL表示执行系统默认动作,赋值为一个函数指针表示用自定义函数捕捉信号,或者说向内核注册了一个信号处理函数,该函数返回值为void,可以带一个int参数,通过参数可以得知当前信号的编号,这样就可以用同一个函数处理多种信号。显然,这也是一个回调函数,不是被main函数调用,而是被系统所调用。
当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么它会被阻塞到当前处理结束为止。如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。

需要注意的是sa_restorer 参数已经废弃不用,sa_handler主要用于不可靠信号(实时信号当然也可以,只是不能带信息),sa_sigaction用于实时信号可以带信息(siginfo_t),两者不能同时出现。sa_flags有几个选项,比较重要的有两个:SA_NODEFER 和 SA_SIGINFO,当SA_NODEFER设置时在信号处理函数执行期间不会屏蔽当前信号;当SA_SIGINFO设置时与sa_sigaction 搭配出现,sa_sigaction函数的第一个参数与sa_handler一样表示当前信号的编号,第二个参数是一个siginfo_t 结构体,第三个参数一般不用。当使用sa_handler时sa_flags设置为0即可。

siginfo_t {

              int      si_signo;    /* Signal number */
              int      si_errno;    /* An errno value */
              int      si_code;     /* Signal code */
              int      si_trapno;   /* Trap number that caused
                                       hardware-generated signal
                                       (unused on most architectures) */
              pid_t    si_pid;      /* Sending process ID */
              uid_t    si_uid;      /* Real user ID of sending process */
              int      si_status;   /* Exit value or signal */
              clock_t  si_utime;    /* User time consumed */
              clock_t  si_stime;    /* System time consumed */
              sigval_t si_value;    /* Signal value */
              int      si_int;      /* POSIX.1b signal */
              void    *si_ptr;      /* POSIX.1b signal */
              int      si_overrun;  /* Timer overrun count; POSIX.1b timers */
              int      si_timerid;  /* Timer ID; POSIX.1b timers */
              void    *si_addr;     /* Memory location which caused fault */
              long     si_band;     /* Band event (was int in
                                       glibc 2.3.2 and earlier) */
              int      si_fd;       /* File descriptor */
              short    si_addr_lsb; /* Least significant bit of address
                                       (since kernel 2.6.32) */
          }

需要注意的是并不是所有成员都在所有信号中存在定义,有些成员是共用体,读取的时候需要读取对某个信号来说恰当的有定义的部分。

下面用sigaction函数举个小例子:

双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/*************************************************************************
    
> File Name: process_.c
    
> Author: Simba
    
> Mail: dameng34@163.com
    
> Created Time: Sat 23 Feb 2013 02:34:02 PM CST
 
************************************************************************/
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<signal.h>
     
#define ERR_EXIT(m)
    
do
{
        
perror
(m);
        
exit
(EXIT_FAILURE);
    
}
while
(0)
     
void
handler(
int
sig);
     
     
int
main(
int
argc,
char
*argv[])
{
    
struct
sigaction act;
    
act.sa_handler = handler;
    
sigemptyset(&act.sa_mask);
    
act.sa_flags = 0;
     
    
if
(sigaction(SIGINT, &act, NULL) < 0)
        
ERR_EXIT(
"sigaction error"
);
     
    
for
(; ;)
        
pause();
     
    
return
0;
     
}
     
void
handler(
int
sig)
{
    
printf
(
"rev sig=%dn"
, sig);
}

simba@ubuntu:~/Documents/code/linux_programming/APUE/signal$ ./sigaction 
^Crev sig=2
^Crev sig=2
^Crev sig=2

………………………

即按下ctrl+c 会一直产生信号而被处理打印recv语句。

其实我们在前面文章说过的signal 函数是调用sigaction 实现的,而sigaction函数底层是调用 do_sigaction() 函数实现的。可以自己实现一个my_signal 函数,如下:

双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/*************************************************************************
    
> File Name: process_.c
    
> Author: Simba
    
> Mail: dameng34@163.com
    
> Created Time: Sat 23 Feb 2013 02:34:02 PM CST
 
************************************************************************/
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<signal.h>
     
#define ERR_EXIT(m)
    
do
{
        
perror
(m);
        
exit
(EXIT_FAILURE);
    
}
while
(0)
     
void
handler(
int
sig);
/* 系统调用signal()实际上调用了sigaction() */
__sighandler_t my_signal(
int
sig, __sighandler_t handler);
     
int
main(
int
argc,
char
*argv[])
{
    
my_signal(SIGINT, handler);
     
    
for
(; ;)
        
pause();
     
    
return
0;
     
}
     
__sighandler_t my_signal(
int
sig, __sighandler_t handler)
{
    
struct
sigaction act;
    
struct
sigaction oldact;
    
act.sa_handler = handler;
    
sigemptyset(&act.sa_mask);
    
act.sa_flags = 0;
     
    
if
(sigaction(sig, &act, &oldact) < 0)
        
return
SIG_ERR;
     
    
return
oldact.sa_handler;
// 返回先前的处理函数指针
}
     
void
handler(
int
sig)
{
    
printf
(
"rev sig=%dn"
, sig);
}

输出测试的一样的,需要注意的是 signal函数成功返回先前的handler,失败返回SIG_ERR。而sigaction 是通过oact 参数返回先前的handler,成功返回0,失败返回-1。

下面再举个小例子说明sa_mask 的作用:

双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/*************************************************************************
    
> File Name: process_.c
    
> Author: Simba
    
> Mail: dameng34@163.com
    
> Created Time: Sat 23 Feb 2013 02:34:02 PM CST
 
************************************************************************/
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<signal.h>
     
#define ERR_EXIT(m)
    
do
{
        
perror
(m);
        
exit
(EXIT_FAILURE);
    
}
while
(0)
     
void
handler(
int
sig);
     
     
int
main(
int
argc,
char
*argv[])
{
    
struct
sigaction act;
    
act.sa_handler = handler;
    
sigemptyset(&act.sa_mask);
    
sigaddset(&act.sa_mask, SIGQUIT);
// 在信号处理函数执行期间屏蔽SIGQUIT信号,完毕后会抵达
    
/* 注意sigprocmask中屏蔽的信号是一直不能抵达的,除非解除了阻塞*/
    
act.sa_flags = 0;
     
    
if
(sigaction(SIGINT, &act, NULL) < 0)
        
ERR_EXIT(
"sigaction error"
);
     
    
for
(; ;)
        
pause();
     
    
return
0;
     
}
     
void
handler(
int
sig)
{
    
printf
(
"rev sig=%dn"
, sig);
    
sleep(5);
}

先按下ctrl+c ,然后马上ctrl+,程序是不会马上终止的,即等到handler处理完毕SIGQUIT信号才会抵达。

simba@ubuntu:~/Documents/code/linux_programming/APUE/signal$ ./sa_mask 
^Crev sig=2
^
5s过后接着才输出Quit (core dumped),即在信号处理函数执行期间sa_mask集合中的信号被阻塞直到运行完毕。

sa_flags 和 sa_sigaction 参数的示例看这里

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

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/141485.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • linux opera flash插件,Linux下64位的Firefox、Opera浏览器安装Flash插件

    linux opera flash插件,Linux下64位的Firefox、Opera浏览器安装Flash插件Linux下,64位的Firefox、Opera等浏览器默认搜索到的Flash插件是32位的,安装之后也不能正常工作。需要手工安装一下。1.下载插件使用浏览器下载:到Adobe的站点上下载64位的Flash插件:http://labs.adobe.com/downloads/flashplayer10_square.html插件下载地址:http://download.macromedia.co…

    2022年5月10日
    57
  • L3-023 计算图(链式求导+bfs拓扑|dfs)「建议收藏」

    L3-023 计算图(链式求导+bfs拓扑|dfs)「建议收藏」原题链接“计算图”(computational graph)是现代深度学习系统的基础执行引擎,提供了一种表示任意数学表达式的方法,例如用有向无环图表示的神经网络。 图中的节点表示基本操作或输入变量,边表示节点之间的中间值的依赖性。 例如,下图就是一个函数 ( 的计算图。现在给定一个计算图,请你根据所有输入变量计算函数值及其偏导数(即梯度)。 例如,给定输入,,上述计算图获得函数值 (;并且根据微分链式法则,上图得到的梯度 ∇。知道你已经把微积分忘了,所以这里只要求你处理几个简单的算子:加法、减法、乘

    2022年8月8日
    6
  • mac datagrip2021 激活码[在线序列号]

    mac datagrip2021 激活码[在线序列号],https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月17日
    115
  • 【TensorFlow】查看TensorFlow版本[通俗易懂]

    【TensorFlow】查看TensorFlow版本[通俗易懂]代码pythonimporttensorflowastftf.__version__ 

    2022年6月25日
    23
  • Android布局之表格布局[通俗易懂]

    Android布局之表格布局[通俗易懂]表格布局(Tablelayout) 简介:       Tablelayout类以行和列的形式对控件进行管理,每一行为一个TableRow对象,或一个View控件。     当为TableRow对象时,可在TableRow下添加子控件,默认情况下,每个子控件占据一列。     当为View时,该View将独占一行。表格布局是以行和列的形式来对控件…

    2022年5月14日
    36
  • Java课程设计[通俗易懂]

    Java课程设计[通俗易懂]#1.团队名称、团队成员##团队名称:秃头团队|成员|任务分配|||||林小强(组长)|dao包util包可视化||陈泽役|model包可视化|#2.前期

    2022年6月30日
    22

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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