三十四、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多 ...
随机推荐
- LVS负载均衡集群(DR)
-----构建DR模式的LVS群集----- --client---------------------LVS------------------------WEB1----------------- ...
- [HAOI2012]道路(最短路DAG上计数)
C国有n座城市,城市之间通过m条[b]单向[/b]道路连接.一条路径被称为最短路,当且仅当不存在从它的起点到终点的另外一条路径总长度比它小.两条最短路不同,当且仅当它们包含的道路序列不同.我们需要对每 ...
- BZOJ--1045-- 糖果传递(中位数,排序)
题目链接 :BZOJ--1045-- 糖果传递 我们知道如果不头尾相连的话 直接求一个前缀和 答案为ans+=s[i] 不相连的话就是1 和n之间断开 头尾相连的话就是 在第k个人之间断开 设A[i] ...
- vue层级关系的数据管理
项目背景:为一些有层级关系的数据管理做一套后台管理系统,例如一个小区,里面是有许多楼,楼里有许多层,每一层有许多不同的房······,现在就是要实现对这些数据进行增删改查操作. 1.Tree(树形组件 ...
- JMeter关联(正则表达式提取器)
正则表达式总结 关联:与系统交互过程中,系统返回的内容,需要在接下来的交互中用到,如防止csrf攻击而生成的token. 从前一个请求中取,用Regular Expression Extractor ...
- 第四节,Neural Networks and Deep Learning 一书小节(上)
最近花了半个多月把Mchiael Nielsen所写的Neural Networks and Deep Learning这本书看了一遍,受益匪浅. 该书英文原版地址地址:http://neuralne ...
- No cached version of cn.lightsky.infiniteindicator:library:1.2.2 available for offline mode.
去掉勾勾
- YII 框架在windows系统下的安装
第一步,下载yiii框架 http://www.yiichina.com 第二步安装: 1.首先需要下载应用模板,分为基础模板和高级应用模板,这里我以高级应用模板为例子 : 去这里现在高级应用模板 h ...
- kettle连接mysql数据库并进行数据分析
1.数据库链接驱动 如果没有安装对应的数据库链接驱动,在数据库链接的过程中,可能会报某个数据库连接找不到的异常,因此需要下载对应驱动后(安装步骤可以参见“怎么在官网上下载java连接mysql的驱动j ...
- python机器学习-sklearn挖掘乳腺癌细胞(五)
python机器学习-sklearn挖掘乳腺癌细胞( 博主亲自录制) 网易云观看地址 https://study.163.com/course/introduction.htm?courseId=10 ...