三十四、Linux 进程与信号——信号特点、信号集和信号屏蔽函数
34.1 信号特点
- 信号的发生是随机的,但信号在何种条件下发生是可预测的
- 进程杠开始启动时,所有信号的处理方式要么默认,要么忽略;忽略是 SIGUSR1 和 SIGUSR2 两个信号,其他都采取默认方式(大多数是终止进程)。
- 进程在调用 exec 函数后,原有信号的捕捉函数失效
- 子进程的诞生总是继承父进程的信号处理方式
- 在系统层面上信号的发生是可靠的
- 在Linux 中的可靠性只保证一次,进程在处理信号期间,若发生同类型的信号不会丢失(内核会保留),会被延迟处理
- 但同类型信号的多次发生只会保留一次,即被处理一次。
- 若不同类型的信号发生也会被内核保留直接会被处理,处理完后再处理原有信号
- 用户层面可靠性依赖于信号而执行的用户代码放置在信号处理程序内部执行是可靠的,否则不一定可靠
- 在信号发生时,慢系统调用以被中断并在信号处理后系统调用会被重启
- 在信号发生时,用户函数可以被中断,但不能被重启,沿着中断点继续执行
- 在用户函数中要保证数据一致性,即可重入性,不要去访问全局变量和静态变量,堆中的变量若在函数内部分配没有关系,否则会出现不可重入性
34.2 信号集和信号屏蔽函数
34.2.1 信号集
- 信号集为一个或多个信号的集合,主要用在信号屏蔽函数中
- #include <signal.h>
- int sigemptyset(sigset_t *set);
- 函数功能:将信号集清空,对应将所有信号屏蔽字置 0
- 函数参数:
- set 信号集合
- #include <signal.h>
- int sigfillset(sigset_t *set);
- 函数功能:将所有信号加入到信号集中,对应将所有信号屏蔽字置 1
- #include <signal.h>
- int sigaddset(sigset_t *set, int signo);
- 函数功能:将某个信号加入到信号集中,对应将信号屏蔽字某位置 1
- #include <signal.h>
- int sigdelset(sigset_t *set, int signo);
- 函数功能:将某个信号从信号集中删除,对应将信号屏蔽字某位置 0
上述函数返回,若成功返回0,出错返回 -1
- #include <signal.h>
- int sigismember(sigset_t *set, int signo);
- 函数功能:测试信号集中是否包含某个信号,对应判断信号屏蔽字某位是否置 1
- 返回值:真,返回1,假,返回0;出错返回 -1
34.2.2 信号屏蔽函数
- #include <signal.h>
- int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrct oset);
- 函数功能:利用 set 去覆盖内核中信号屏蔽字, oset 存放原有的信号屏蔽字
- 函数参数:
- @ how
- SIG_BLOCK:利用 set 中信号设置信号屏蔽字
- SIG_UNBLOCK:利用 set 中信号不设置信号屏蔽字
- SIG_SETMASK:利用 set 中信号去替换内核信号屏蔽字
- @ how
- 返回:成功,返回0;失败,返回 -1
- 说明:
- 进程可以暂时屏蔽信号,使得进程在执行过程中发生的相应信号暂时被阻塞,等待进程解除信号屏蔽后,再由内核或驱动将信号传递给进程
- 信号屏蔽可屏蔽程序执行过程中的中断
- #include <signal.h>
- int sigpending(sigset_t *set);
- 函数功能:获取信号未决字的内容
- 返回值:成功返回0;出错返回 -1
34.2.3 信号屏蔽设置
- 信号在处理过程中是被屏蔽的(置 1),处理完毕解除屏蔽(被置 0),可在函数可重入性中使用信号屏蔽技术
- 内核中的 task_struct 中包含两个 32 位字(记录相关的信号信息),分别为信号屏蔽字 mask 和信号未决字 pending。
- mask:
- 共有 31 位(代表 1~31 号信号,0 号没有意义),每一位代表一个信号,初始为 0,若这位上发生信号,会被立即处理;若为 1(设置 1 则信号被屏蔽,设置 0 则信号不被屏蔽),则在该位上发生信号不会被处理,会延迟处理
- pengding
- 初始为 0,若 mask 中某一位为 1,但又发生了同样的信号,则在 pending 同样的位置会被置为 1,以便让进程知道该信号又发生过而进行延迟处理
- mask:
- 若干个信号一起设置为 0 或 1 称为信号集
- 子进程继承父进程中的信号屏蔽字,而不继承信号未决字
34.2.4 获取信号屏蔽字
- #include <stdio.h>
- #include <stdlib.h>
- #include <signal.h>
- #include <sys/types.h>
- #include <unistd.h>
- void out_set(sigset_t set)
- {
- int i;
- for(i = ; i < ; i++){
- if(sigismember(&set, i)) {
- printf("%d\n", i);
- }
- }
- }
- void sig_handler(int signo)
- {
- printf("begin process the %d\n", signo);
- /** 获得正在处理信号时内核中的信号屏蔽字的内容 */
- sigset_t oset;///< 放置内核屏蔽字的内容
- sigemptyset(&oset);
- if((sigprocmask(SIG_BLOCK, NULL, &oset)) < ) {
- perror("sigprocmask error");
- }
- out_set(oset);
- printf("finish process the %d\n", signo);
- }
- int main(void)
- {
- if(signal(SIGUSR1, sig_handler) == SIG_ERR) {
- perror("signal sigusr1 error");
- }
- if(signal(SIGUSR2, sig_handler) == SIG_ERR) {
- perror("signal sigusr2 error");
- }
- sigset_t oset;///< 放置内核屏蔽字的内容
- printf("before signal occured mask:\n");
- /** 清空信号集 oset */
- sigemptyset(&oset);
- /** 在信号发生前,获得信号屏蔽字的内容 */
- if((sigprocmask(SIG_BLOCK, NULL, &oset)) < ) {
- perror("sigprocmask error");
- }
- out_set(oset);
- printf("process %d wait signal...\n", getpid());
- pause();///< 进程暂停等待信号
- printf("after signal occured mask:\n");
- sigemptyset(&oset);
- /** 在信号发生后,获得信号屏蔽字的内容 */
- if((sigprocmask(SIG_BLOCK, NULL, &oset)) < ) {
- perror("sigprocmask error");
- }
- out_set(oset);
- return ;
- }
测试:
在信号发生前,信号屏蔽字都为 0,然后发送 SIGUSR1 信号,信号集被设置为 10。在结束后,信号又被重新设置回 0.
34.2.5 解决函数不可重入性
- #include <signal.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- int g_v[];
- int *h_v; ///< 堆中变量
- void set(int val)
- {
- int a_v[];
- int i = ;
- for(; i < ; i++) {
- a_v[i] = val;
- g_v[i] = val;
- h_v[i] = val;
- sleep();
- }
- printf("g_v:");
- for(i = ; i < ; i++){
- if(i != ) {
- printf(", %d", g_v[i]);
- }
- else {
- printf(", %d", g_v[i]);
- }
- }
- printf("\n");
- printf("h_v:");
- for(i = ; i < ; i++){
- if(i != ) {
- printf(", %d", h_v[i]);
- }
- else {
- printf(", %d", h_v[i]);
- }
- }
- printf("\n");
- printf("a_v:");
- for(i = ; i < ; i++){
- if(i != ) {
- printf(", %d", a_v[i]);
- }
- else {
- printf(", %d", a_v[i]);
- }
- }
- }
- void sig_handler(int signo)
- {
- if(signo == SIGTSTP){
- printf("SIGTSTP occured\n");
- set();
- printf("\nend SIGTSTP\n");
- }
- }
- int main(void)
- {
- if(signal(SIGTSTP, sig_handler) == SIG_ERR){
- perror("signal sigtstp error");
- }
- h_v = (int *)calloc(, sizeof(int));
- printf("begin running main\n");
- //屏蔽信号(1~31)
- sigset_t sigset;
- sigemptyset(&sigset);
- sigfillset(&sigset); ///<要屏蔽所有的信号
- if(sigprocmask(SIG_SETMASK, &sigset, NULL) < 0){
- perror("sigprocmask error");
- }
- set();
- //解除信号屏蔽
- if(sigprocmask(SIG_UNBLOCK, &sigset, NULL) < ){
- perror("sigprocmask error");
- }
- printf("\nend running main\n");
- return ;
- }
未加屏蔽之前的测试结果,开始运行后,按 CTRL+Z 进行中断:
加入上述信号屏蔽函数后:
可以看出,数值都可以正常输出。
34.2.6 查看信号未决字的内容
在不断发同类型的信号的时候,查看信号未决字
- #include <stdio.h>
- #include <stdlib.h>
- #include <signal.h>
- #include <unistd.h>
- void out_set(sigset_t set)
- {
- int i = ;
- for(; i < ; i++){
- if(sigismember(&set, i)){
- printf("%d,", i);
- }
- }
- printf("\n");
- }
- void sig_handler(int signo)
- {
- printf("begin the signal handler\n");
- int i = ;
- sigset_t sigset;
- for(; i < ; i++) {
- sigemptyset(&sigset);
- if(sigpending(&sigset) < ) {
- perror("sigpending error");
- }
- else {
- printf("pending signal:");
- out_set(sigset);
- sigemptyset(&sigset);
- }
- printf("i is %d\n", i);
- sleep();
- }
- printf("end the signal handler\n");
- }
- int main(void)
- {
- if(signal(SIGTSTP, sig_handler) == SIG_ERR){
- perror("signal sigtstp error");
- }
- printf("process %d wait signal...\n", getpid());
- pause();///< 进程暂停等待信号
- printf("process finished\n");
- }
只发送一次中断信号的结果:
信号未决字在发送一个信号的时候,再发送同类信号的时候,信号未决字才置1,
连续发送两次信号:
可以看出信号被处理了两次,未决字发生了变化。
同样可以发送超过两次同类信号,会发现也只会处理两次。
三十四、Linux 进程与信号——信号特点、信号集和信号屏蔽函数的更多相关文章
- 三十、Linux 进程与信号——信号的概念及 signal 函数
30.1 信号的基本概念 信号(signal)机制是Linux 系统中最为古老的进程之间的通信机制,解决进程在正常运行过程中被中断的问题,导致进程的处理流程会发生变化 信号是软件中断 信号是异步事件 ...
- 第三十四天- 线程队列、线程池(map/submit/shutdown/回调函数)
1.线程列队 queue队列 :使用import queue,用法与进程Queue一样 class queue.Queue(maxsize=0) # 先进先出: q = queue.Queue(3) ...
- Gradle 1.12用户指南翻译——第三十四章. JaCoCo 插件
本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
- COJ966 WZJ的数据结构(负三十四)
WZJ的数据结构(负三十四) 难度级别:C: 运行时间限制:20000ms: 运行空间限制:262144KB: 代码长度限制:2000000B 试题描述 给一棵n个节点的树,请对于形如"u ...
- NeHe OpenGL教程 第三十四课:地形
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- JAVA之旅(三十四)——自定义服务端,URLConnection,正则表达式特点,匹配,切割,替换,获取,网页爬虫
JAVA之旅(三十四)--自定义服务端,URLConnection,正则表达式特点,匹配,切割,替换,获取,网页爬虫 我们接着来说网络编程,TCP 一.自定义服务端 我们直接写一个服务端,让本机去连接 ...
- Java进阶(三十四)Integer与int的种种比较你知道多少?
Java进阶(三十四)Integer与int的种种比较你知道多少? 前言 如果面试官问Integer与int的区别:估计大多数人只会说到两点:Ingeter是int的包装类,注意是一个类:int的初值 ...
- SQL注入之Sqli-labs系列第三十四关(基于宽字符逃逸POST注入)和三十五关
开始挑战第三十四关和第三十五关(Bypass add addslashes) 0x1查看源码 本关是post型的注入漏洞,同样的也是将post过来的内容进行了 ' \ 的处理. if(isset($_ ...
- spring boot 常见三十四问
Spring Boot 是微服务中最好的 Java 框架. 我们建议你能够成为一名 Spring Boot 的专家. 问题一 Spring Boot.Spring MVC 和 Spring 有什么区别 ...
- “全栈2019”Java多线程第三十四章:超时自动唤醒被等待的线程
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
随机推荐
- poj3680 Intervals (费用流)
建图((x,y,c,l)表示x到y,费用c,流量l) (S,1,0,K) (i,i+1,0,K) 这个边上的流量,表示i还可以被覆盖的次数 (N,T,0,K) (i,j,w,1)对于权值为w的区间[i ...
- 【linux】/dev/null作用和/dev/random
一. /dev/null /dev/null属于字符特殊文件,它属于空设备,是一个特殊的设备文件,它会丢弃一切写入其中的数据,写入它的内容都会永远丢失,而且没有任何可以读取的内容. 我们用file命 ...
- django restframework 环境配置
Requirements: coreapi (1.32.0+) - Schema generation support.Markdown (2.1.0+) - Markdown support for ...
- JQuery选择器,事件,DOM操作,动画
JQuery是一个JavaScript代码库,或者是JavaScript框架: 1.选择器:(和CSS选择器一致) 基本选择器:ID选择器$('#div1');Class选择器('.div1');标签 ...
- 【洛谷P1198】最大数
题目大意:在线维护一个序列,支持插入一个数,查询区间最值. 题解:直接建立线段树,插入就单点修改,查询就正常查..orz开始还真没想到.. 代码如下 #include <bits/stdc++. ...
- 【UVA】11400 照明系统设计 排序+dp
题目中有一个重要的信息是:每一种灯泡只能换成比它电压更大的灯泡,因此电压的大小限制了状态的转移.因此,在这里按照电压从小到大把每种灯泡排序,使得在考虑后面的灯泡时,前面的灯泡自然可以换成后面的灯泡.状 ...
- (转)深入理解Java注解类型(@Annotation)
背景:在面试时候问过关于注解的问题,工作中也用到过该java的特性,但是也没有深入的了解. 秒懂,Java 注解 (Annotation)你可以这样学 ps:注解最通俗易懂的解释 注解是一系列元数据, ...
- gallery
效果如下 目录如下 代码如下: //index.html <!DOCTYPE html> <html> <head> <meta charset=" ...
- C++ template一些体悟(3)
其实没啥体悟,因为还没有感受到这些例子的作用,记一下先 #include <iostream> using namespace std; class alloc { }; template ...
- 桌面面板和内部窗体JDeskPane、JInternalFrame
桌面面板和内部窗体JDeskPane.JInternalFrame,内部窗体必须在桌面面板里. import javax.swing.*; import java.awt.*; public clas ...