信号捕捉(signal、sigaction)

信号的基本属性:软中断,由内核发送,内核处理。某个进程通过内核向另一个进程发送信号时(引起信号产生的五个因素),另一个进程将会陷入内核进行中断处理,未决信号集中相应信号置1,当递达后,置0。如果阻塞信号集相应信号为1,则该信号处于未决状态。处于未决状态中的信号,多次发送时,只是执行一次,因为在未决信号集中只是记录了该信号的状态,没有记录发送的次数。信号抵达后,内核进行处理。处理方式有三:默认处理方式(5种);忽略(丢弃)和捕捉。下面说明捕捉机制。

signal和sigaction函数只是完成对一个信号进行注册的功能,而对信号的捕捉的处理都是由内核完成的。当对一个信号进行注册后,内核对其捕捉同时调用其注册时对应的用户处理函数。

1signal函数

typedef void (*sighandler_t)(int);  //定义一个函数类型 sighandler_t

sighandler_t signal(int signum, sighandler_t handler);

作用:注册一个信号捕捉函数

返回值:成功返回sighandler_t类型的函数(或函数首地址);失败则返回一个宏:SIG_ERR。注意判断该函数的返回值: sighandler ret = signal(·······);if(ret==SIG_ERR)

第一个参数为信号;第二个参数为sighandler_t类型函数(即返回值为void,形参为int)。

注意:该函数由ANSI定义,由于历史原因在不同版本的Unix和不同版本的Linux中可能有不同的行为。因此应该尽量避免使用它,取而代之使用sigaction函数。

#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>typedef void (*sighandler_t) (int);  //定义sighandler_t类型void catchsigint(int signo)
{printf("-----------------catch\n");
}int main(void)
{sighandler_t handler;handler = signal(SIGINT, catchsigint);  //注册2号信号if (handler == SIG_ERR) {perror("signal error");exit(1);}                            //判断返回值while (1);return 0;
}

[root@localhost 01_signal_test]# ./signal2

^C-----------------catch                 //Ctrl+C 

^C-----------------catch                 //Ctrl+C 

^\Quit (core dumped)                 //Ctrl+\

只要一发送2号信号,就会执行相应函数。

2sigaction函数

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); 

作用:对某个信号进行注册(同signal),即对某个信号之前对应的处理方式(函数)进行修改。

返回值:成功0;失败-1,设置errno。

参数:

act传入参数,新的处理方式。

oldact传出参数,旧的处理方式。

struct 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_restorer:该元素是过时的,不应该使用,POSIX.1标准将不指定该元素。(弃用)

sa_sigaction:当sa_flags被指定为SA_SIGINFO标志时,使用该信号处理程序。(很少使用) 

重点掌握:

sa_handler:指定信号捕捉后的处理函数名(即注册函数)。也可赋值为SIG_IGN表忽略 SIG_DFL表执行默认动作;

sa_mask: 调用信号处理函数时,所要屏蔽的信号集合(信号屏蔽字)。注意:仅在处理函数被调用期间屏蔽生效,是临时性设置;sa_mask也是一个字(64位),只是在执行相应的用户处理函数期间生效。即在执行用户处理函数期间, sa_mask屏蔽的信号也不能递达,处于未决状态。如果sa_mask未屏蔽,则响应信号,中断嵌套。相当于此期间,sa_mask代替了mask。

sa_flags:通常设置为0,表示用默认属性。默认属性即为:sa_mask中将自己屏蔽,即该信号的注册函数执行期间,再次向进程发送该信号,该信号不能递达,处于未决状态。

最后一个参数如果不关心之前的处理方式,可以为NULL

3)信号捕捉机制

进程正常运行时,默认PCB中有一个信号屏蔽字,假定为mask,它决定了进程自动屏蔽哪些信号。当注册了某个信号捕捉函数,捕捉到该信号以后,要调用该函数。而该函数有可能执行很长时间,在这期间所屏蔽的信号不由mask来指定。而是用sa_mask来指定。调用完信号处理函数,再恢复为mask。

sa_flags为0时,XXX信号捕捉函数执行期间,XXX信号自动被屏蔽。

阻塞的常规信号(1-31)不支持排队,产生多次只记录一次。(后32个实时信号支持排队)

内核实现信号捕捉的过程如下:

首先,处于用户态(user mode)的某个进程在执行到某个指令时突然接收某个信号(软中断,终端按键产生;硬件异常产生;命令产生;系统调用产生或者软件条件产生),会暂停执行下一条指令而陷入内核进入内核态。

内核在处理这一异常后,在准备会用户态之前先处理可以递达该进程的信号。

如果该信号的处理方式为捕捉,则内核对该信号进行捕捉,同时调用相应的用户处理函数,回到用户态执行相应的用户处理函数(注意不是回到主控制流程)。

在用户处理函数执行完返回时,再次执行系统调用sigretum再次进入内核。因为函数执行完需要返回到该函数的调用点,而该函数是内核调用的,因此需要再次返回到内核。

最后,从内核再次返回到用户模式,从上次中断处继续执行下一条指令。

 

//练习1:为某个信号设置捕捉函数;验证在信号处理函数执行期间,该信号多次递送,那么只在处理函数之行结束后,处理一次;验证sa_mask在捕捉函数执行期间的屏蔽作用。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>void docatch(int signo)  //用户处理函数
{printf("the %dth signal is catched\n", signo);sleep(10); printf("-------finish------\n");
}
int main(void)
{int ret;struct sigaction act;act.sa_handler = docatch;sigemptyset(&act.sa_mask);sigaddset(&act.sa_mask, SIGQUIT);  //sa_mask屏蔽字中,3号信号置1act.sa_flags = 0;  //默认属性   信号捕捉函数执行期间,自动屏蔽本信号ret = sigaction(SIGINT, &act, NULL);   //注册2号信号if (ret == -1) {perror("sigaction error");exit(1);}while (1);return 0;
}

[root@localhost 01_signal_test]# ./test_sigac

^Cthe 2th signal is catched   // 2号信号 Ctrl +C

-------finish------

^Cthe 2th signal is catched  // 2号信号 Ctrl +C

^C^C^C^C^C^C^C^C^C^C^C^C^C-------finish------   // 执行期间,发多个2号信号

the 2th signal is catched

-------finish------            //但是只是执行了一次

^Cthe 2th signal is catched

^\^\^\^\^\^\^\^\^\^\^\^\-------finish------   // 执行期间,发多个3号信号

Quit (core dumped)   //2号信号处理完后,处理2号,则退出进程,结束。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/385328.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

1090 Highest Price in Supply Chain (25)(25 分)

A supply chain is a network of retailers&#xff08;零售商&#xff09;, distributors&#xff08;经销商&#xff09;, and suppliers&#xff08;供应商&#xff09;-- everyone involved in moving a product from supplier to customer. Starting from one root suppli…

时序竞态(竞态条件)

产生原因&#xff1a;仍然以前文实现的sleep函数为例&#xff0c;如果进程在执行完alarm函数后&#xff0c;突然失去CPU&#xff0c;被阻塞等待&#xff08;这是有可能的&#xff0c;进程在执行过程中&#xff0c;若非原子操作&#xff0c;都有可能随时失去CPU&#xff09;&…

1106 Lowest Price in Supply Chain (25)

A supply chain is a network of retailers&#xff08;零售商&#xff09;, distributors&#xff08;经销商&#xff09;, and suppliers&#xff08;供应商&#xff09;-- everyone involved in moving a product from supplier to customer. Starting from one root suppli…

【Leetcode | 顺序刷题 】二分查找目录

二分查找序号题号129. 两数相除 50. Pow(x, n) 69. x 的平方根

sigsuspend函数(mysleep函数的改进)

可以通过设置屏蔽SIGALRM的方法来控制程序执行逻辑&#xff0c;但无论如何设置&#xff0c;程序都有可能在“解除信号屏蔽”与“挂起等待信号”这个两个操作间隙失去cpu资源。除非将这两步骤合并成一个“原子操作”。sigsuspend函数具备这个功能。在对时序要求严格的场合下都应…

【Leetcode | 顺序刷题】数学目录

序号题号1 7. 整数反转 28. 字符串转换整数 (atoi)39. 回文数443. 字符串相乘

全局变量的异步I/O问题

全局变量的异步I/O问题同样属于时序竞态问题&#xff0c;其本质就是多个进程或者同一个进程中的多个时序&#xff08;如主控程序和信号捕捉时的用户处理函数&#xff09;对同一个变量进行修改时&#xff0c;它们的执行顺序不一样就会导致该变量最终的值不一样&#xff0c;从而产…

【Leetcode | 03】String

字符串目录序号题号33. 无重复字符的最长子串 151. 翻转字符串里的单词

可/不可重入函数

一个函数在被调用执行期间&#xff08;尚未调用结束&#xff09;&#xff0c;由于某种时序&#xff08;递归或者处理信号捕捉时等情况&#xff09;又被重复调用&#xff0c;称之为“重入”。根据函数实现的方法可分为“可重入函数”和“不可重入函数”两种。看如下程序。 可以看…

【Leetcode | 顺序刷题】杂项目录

序号题号类别1136. 只出现一次的数字位运算2137. 只出现一次的数字 II位运算3 260. 只出现一次的数字 III 位运算4191. 位1的个数位运算5231. 2的幂位运算6342. 4的幂位运算7 338. 比特位计数 位运算8405. 数字转换为十六进制数位运算9371. 两整数之和位运算10401. 二进制手表位…

SIGCHLD信号

&#xff08;1&#xff09;SIGCHLD信号产生的条件 1.子进程终止时会向父进程发送SIGCHLD信号&#xff0c;告知父进程回收自己&#xff0c;但该信号的默认处理动作为忽略&#xff0c;因此父进程仍然不会去回收子进程&#xff0c;需要捕捉处理实现子进程的回收&#xff1b; 2.子…

信号传参

&#xff08;1&#xff09;发送信号传参 前面已经知道从一个进程向另一个进程发送信号可以使用kill函数&#xff0c;但是kill函数在向进程发送信号的时候不能携带除了信号以外的其他信息&#xff0c;这时可以使用与kill相对应的sigqueue函数&#xff0c;该函数也是向一个进程发…

【Leetcode | 52】257. 二叉树的所有路径

给定一个二叉树&#xff0c;返回所有从根节点到叶子节点的路径。 说明: 叶子节点是指没有子节点的节点。 示例: 输入: 1 / \ 2 3 \ 5 输出: ["1->2->5", "1->3"] 解释: 所有根节点到叶子节点的路径为: 1->2->5, 1->3 解法一&a…

623. 在二叉树中增加一行

给定一个二叉树&#xff0c;根节点为第1层&#xff0c;深度为 1。在其第 d 层追加一行值为 v 的节点。 添加规则&#xff1a;给定一个深度值 d &#xff08;正整数&#xff09;&#xff0c;针对深度为 d-1 层的每一非空节点 N&#xff0c;为 N 创建两个值为 v 的左子树和右子树…

终端的概念

操作系统接口&#xff1a;用户接口和程序接口。用户接口分为联机用户接口和脱机用户接口。脱机用户接口出现在早期的批处理系统中&#xff08;将作业提前交给操作系统&#xff0c;作业完成的过程中用户无法交互&#xff09;&#xff1b;联机用户接口即为终端&#xff08;所有输…

终端的启动流程

在Linux操作系统启动时&#xff0c;首先加载的进程就是init进程&#xff08;ID为1&#xff09;&#xff0c;其余进程都是init进程产生的&#xff08;fork&#xff0c;然后exec金蝉脱壳&#xff09;&#xff0c;因此系统中所有进程都可以看成是init进程的子孙进程。可以通过ps a…

进程组(作业)

&#xff08;1&#xff09;概念和特性 进程组&#xff0c;也称之为作业。BSD于1980年前后向Unix中增加的一个新特性。代表一个或多个进程的集合。每个进程都属于一个进程组。在waitpid函数和kill函数的参数中都曾使用到。操作系统设计的进程组的概念&#xff0c;是为了简化对多…

437. 路径总和 III

给定一个二叉树&#xff0c;它的每个结点都存放着一个整数值。 找出路径和等于给定数值的路径总数。 路径不需要从根节点开始&#xff0c;也不需要在叶子节点结束&#xff0c;但是路径方向必须是向下的&#xff08;只能从父节点到子节点&#xff09;。 二叉树不超过1000个节…

会话(session)

一组进程形成一个进程组&#xff0c;一组进程组形成一个会话&#xff0c;即一个会话中可以包括多个进程组。 &#xff08;1&#xff09;创建会话 创建一个会话需要注意以下6点注意事项&#xff1a;1.调用进程不能是进程组组长&#xff08;不能是父进程&#xff09;&#xff0…

508. 出现次数最多的子树元素和

给出二叉树的根&#xff0c;找出出现次数最多的子树元素和。一个结点的子树元素和定义为以该结点为根的二叉树上所有结点的元素之和&#xff08;包括结点本身&#xff09;。然后求出出现次数最多的子树元素和。如果有多个元素出现的次数相同&#xff0c;返回所有出现次数最多的…