Linux信号实践(3) --信号内核表示

信号在内核中的表示

   执行信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending)。进程可以选择阻塞(Block)某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。

   注意,阻塞和忽略是不同,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。信号在内核中的表示可以看作是这样的:

 

图-信号的发送过程

 

解释说明:

  1)PCB进程控制块(task_struct)中函数有信号屏蔽状态字(block)信号未决状态字(pending)还有是否忽略标志

  2) 信号屏蔽状态字(block), 1代表阻塞、0代表不阻塞;

      信号未决状态字(pending)的1代表未决,0代表信号可以抵达了;

  3)向进程发送SIGINT,内核首先判断信号屏蔽状态字是否阻塞,若阻塞,信号未决状态字(pending)相应位制成1;若阻塞解除,信号未决状态字(pending)相应位制成0;表示信号可以抵达了。

  4)block状态字、pending状态字均64位(bit);

  5)block状态字用户可以读写,pending状态字用户只能读;这是信号设计机制

 

  思考1:状态字都64bit,编程时,如何表示状态字那?

  思考2:block状态字信息如何获取或者操作那?哪些api?

  思考3:pending状态字信息如何获取或者操作那?哪些api?

    答案见下;

 

信号集操作函数(状态字表示) 

#include <signal.h>
int sigemptyset(sigset_t *set);	//把信号集清零;(64bit/8=8字节)
int sigfillset(sigset_t *set); 	//把信号集64bit全部置为1
int sigaddset(sigset_t *set, int signo); 	//根据signo,把信号集中的对应位置成1
int sigdelset(sigset_t *set, int signo); 	//根据signo,把信号集中的对应位置成0
int sigismember(const sigset_t *set, int signo);	//判断signo是否在信号集中

sigprocmask:读取/更改信号屏蔽状态字(Block)

int sigprocmask(int how, const sigset_t *set, sigset_t *oset);

返回值:若成功则为0,若出错则为-1

  读取:如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。

  更改:如果set是非空指针,则更改进程的信号屏蔽字,参数how指示如何更改。如果oset和set都是非空指针,则先将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。假设当前的信号屏蔽字为mask,下表说明了how参数的可选值。

How:

 

 

sigpending获取信号未决状态字(pending)信息 

#include <signal.h>
int sigpending(sigset_t *set);

DESCRIPTION

 sigpending()  returns the set of signals that are pending for delivery to the calling thread 

(i.e., the signals which have been raised while blocked).  

The mask of pending signals is returned in set.

/**	示例1:添加信号SIGINT到信号屏蔽字
此时再按下Ctrl+C键, 进程也接收不到SIGINT信号了
**/
inline void err_exit(std::string message);
void sigHandler(int signo);
void printSigSet(const sigset_t &sigset);int main()
{
//虽然此处安装了信号处理函数, 但是该进程还是接收不到SIGINT信号if (signal(SIGINT, sigHandler) == SIG_ERR)err_exit("signal error");//添加信号屏蔽字: 屏蔽SIGINT信号sigset_t addset;sigemptyset(&addset);sigaddset(&addset, SIGINT);if (sigprocmask(SIG_BLOCK, &addset, NULL) == -1)err_exit("sigprocmask error");// 不断打印当前的信号屏蔽字sigset_t sigset;while (true){sigpending(&sigset);printSigSet(sigset);sleep(1);}
}inline void err_exit(std::string message)
{perror(message.c_str());exit(EXIT_FAILURE);
}
void sigHandler(int signo)
{cout << "catch a signal, number = " << signo << endl;
}
void printSigSet(const sigset_t &sigset)
{for (unsigned i = 1; i < NSIG; ++i){if (sigismember(&sigset, i))putchar('1');elseputchar('0');}putchar('\n');
}
/** 示例2:在示例1的基础上继续屏蔽SIGINT信号, 但是如果该进程接收到了SIGQUIT信号, 则将对SIGINT信号的屏蔽解除, 需要
1.在main函数中再安装一个SIGQUIT捕捉函数
2.对sigHandler函数进行改造
**/
int main()
{if (signal(SIGINT, sigHandler) == SIG_ERR)err_exit("signal SIGINT error");if (signal(SIGQUIT, sigHandler) == SIG_ERR)err_exit("signal SIGQUIT error");//添加信号屏蔽字: 屏蔽SIGINT信号sigset_t addset;sigemptyset(&addset);sigaddset(&addset, SIGINT);if (sigprocmask(SIG_BLOCK, &addset, NULL) == -1)err_exit("sigprocmask error");// 不断打印当前的信号屏蔽字sigset_t sigset;while (true){sigpending(&sigset);printSigSet(sigset);sleep(1);}
}void sigHandler(int signo)
{switch (signo){case SIGINT:cout << "catch a signal SIGINT" << endl;break;case SIGQUIT://如果是SIGQUIT信号, 则将SIGINT信号的屏蔽进行解除sigset_t unblockSet;sigemptyset(&unblockSet);sigaddset(&unblockSet, SIGINT);if (sigprocmask(SIG_UNBLOCK, &unblockSet, NULL) == -1)err_exit("sigprocmask unblock error");elsecout << "sigprocmask success" << endl;break;default:cerr << "unknown signal" << endl;break;}
}
/**连续的按Ctrl+c键盘,虽然发送了多个SIGINT信号,但是因为信号是不稳定的(不支持排队),所以只保留了一个,如下图
*/

//示例: 换成实时信号SIGRTMAX
int main()
{if (signal(SIGRTMAX, sigHandler) == SIG_ERR)err_exit("signal SIGRTMAX error");if (signal(SIGQUIT, sigHandler) == SIG_ERR)err_exit("signal SIGQUIT error");//添加信号屏蔽字: 屏蔽SIGRTMAX信号sigset_t addset;sigemptyset(&addset);sigaddset(&addset, SIGRTMAX);if (sigprocmask(SIG_BLOCK, &addset, NULL) == -1)err_exit("sigprocmask error");// 不断打印当前的信号屏蔽字sigset_t sigset;while (true){sigpending(&sigset);printSigSet(sigset);sleep(1);}
}
void sigHandler(int signo)
{if (signo == SIGRTMAX)cout << "catch a signal SIGRTMAX" << endl;else if (signo == SIGQUIT){sigset_t unblockSet;sigemptyset(&unblockSet);sigaddset(&unblockSet, SIGRTMAX);if (sigprocmask(SIG_UNBLOCK, &unblockSet, NULL) == -1)err_exit("sigprocmask unblock error");elsecout << "sigprocmask success" << endl;}elsecerr << "unknown signal" << endl;
}

/** 向该进程连续发送四个SIGRTMAX, 则进程都能够收到**/

 

 

综合案例

   1) 创建子进程与父进程;

   2) 注册SIGINT非实时信号与SIGRTMIN实时信号,并将这两种信号添加到进程屏蔽信号组中;

   3) 注册用户自定义信号;

   4) 子进程发送5次非实时信号,发5次实时信号;

   5) 然后子进程发送SIGUSR1解除进程对SIGINT,SIGTRMIN信号的阻塞

   6) 观察实时信号与非实时信号的区别

//程序示例
void onSigAction(int signalNumber, siginfo_t *sigInfoStruct, void *)
{//获取接收到的数据int receiveNumber = sigInfoStruct->si_int;//如果收到的是SIGUSR1信号,则解除对SIGINT,SIGRTMIN的屏蔽if (signalNumber == SIGUSR1){sigset_t unblockSet;sigemptyset(&unblockSet);sigaddset(&unblockSet,SIGINT);sigaddset(&unblockSet,SIGRTMIN);;sigprocmask(SIG_UNBLOCK,&unblockSet,NULL);//Value值会是乱码!//cout << "Receive SIGUSR1, Value = " << receiveNumber << endl;cout << "Unblock, Receive SIGUSR1" << endl;}else if (signalNumber == SIGINT){cout << "Receive SIGINT, Value = " << receiveNumber << endl;}else if (signalNumber == SIGRTMIN){cout << "Receive SIGRTMIN, Value = " << receiveNumber << endl;}
}int main()
{struct sigaction act;//如果需要使得信号处理程序接收额外数据,//则必须将sa_flags位置为SA_SIGINFOact.sa_flags = SA_SIGINFO;sigemptyset(&act.sa_mask);act.sa_sigaction = onSigAction;//注册信号处理函数if (sigaction(SIGINT,&act,NULL) < 0)err_exit("sigaction SIGINT");if (sigaction(SIGRTMIN,&act,NULL) < 0)err_exit("sigaction SIGRTMIN");if (sigaction(SIGUSR1,&act,NULL) < 0)err_exit("sigaction SIGUSR1");//将信号SIGINT,SIGRTMIN信号阻塞sigset_t blockSet;sigemptyset(&blockSet);sigaddset(&blockSet,SIGINT);sigaddset(&blockSet,SIGRTMIN);if (sigprocmask(SIG_BLOCK,&blockSet,NULL) < 0)err_exit("sigprocmask error");pid_t pid = fork();if (pid == -1)err_exit("fork error");else if (pid == 0){union sigval Value;Value.sival_int = 200;//给父进程发送五次带额外数据的非可靠信号(其实最终只接受到了一次)for (int i = 0; i < 5; ++i){++ Value.sival_int;if (sigqueue(getppid(),SIGINT,Value) != 0)err_exit("sigqueue error");}//给父进程发送五次带额外数据的可靠信号(最终接收到了五次!!!)Value.sival_int = 0;for (int i = 0; i < 5; ++i){++ Value.sival_int;if (sigqueue(getppid(),SIGRTMIN,Value) != 0)err_exit("sigqueue error");}//给父进程发送SIGUSR1信号,解除对SIGINT,SIGRTMIN信号的阻塞kill(getppid(),SIGUSR1);}while (true){pause();}
}

运行结果:

 

 其实只是收到了一份非可靠信号SIGINT!

转载于:https://www.cnblogs.com/itrena/p/5926969.html

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

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

相关文章

第2章 数据认知与预处理

《大数据挖掘及应用》学习笔记。 第2章 数据认知与预处理 2.1 数据分析的定义和流程 数据分析(data analysis)是指用适当的统计分析方法对收集来的大量数据进行分析和解释&#xff0c;提取出有用的信息形成结论&#xff0c;从而对数据加以详细研究和概括总结的过程。 2.1.1 如…

9 C++ Boost 多线程,线程同步

线程的创建 boost_thread,boost_system 多线程的创建 线程的参数传递 线程的创建方式 线程的join 加入join,回收线程线程中断 线程中断2, 线程组 boost 线程的死锁 boost 线程递归锁 线程互斥锁,线程同步 unique_lock 锁,离开作用域自动释放 unique_lock 锁 示例 2,可以显式的释…

命令注入_命令注入绕过方式总结

前言命令注入是web中常见的漏洞之一&#xff0c;由于web应用程序未对用户提交的数据做严格的过滤&#xff0c;导致用户输入可以直接被linux或windows系统当成命令执行&#xff0c;一般都会造成严重的危害。常用符号分号(;)多条语句顺序执行时的分割符号。1cmd1;cmd2管道符(|)cm…

linux网络编程之用socket实现简单客户端和服务端的通信(基于UDP)

1、sendto和recvfrom函数介绍 sendto(经socket传送数据) 相关函数 send , sendmsg,recv , recvfrom , socket表头文件 #include < sys/types.h > #include < sys/socket.h >定义函数 int sendto ( int s , const void * msg, int len, unsigned int flags, const…

redis缓存设计要点随谈

在高并发系统中&#xff0c;缓存是必不可少的一部分。没有缓存对系统的加速和阻挡大量的请求直接落到系统的数据库&#xff0c;系统是很难撑住高并发的冲击。所以缓存设计是系统很关键的一环。1、缓存更新缓存的数据一般都是有有效期的&#xff0c;过了一段时间之后就会失效&am…

集合的结构示意图

转载于:https://blog.51cto.com/8467007/1364724

Java设计模式(8)组合模式(Composite模式)

Composite定义&#xff1a;将对象以树形结构组织起来&#xff0c;以达成“部分&#xff0d;整体” 的层次结构&#xff0c;使得客户端对单个对象和组合对象的使用具有一致性。Composite比较容易理解&#xff0c;想到Composite就应该想到树形结构图。组合体内这些对象都有共同接…

第1章 数据分析概述

《Python数据分析基础教程》学习笔记。 第1章 数据分析概述 1.1 数据的性质 1.1.1 数据的概念 所谓数据就是描述事物的符号&#xff0c;是对客观事物的性质、状态和相互关系等进行记载的物理符号或者是这些物理符号的组合。 在计算机系统中&#xff0c;各种文字、字母、数字符…

Android之通过adb shell getprop、netstat命令看dns、ip

1、查看dns 1)、输入adb shell 2 )、输入getprop ,查看配置 3)、getprop | grep dns 过滤dns 4) 、getprop | grep dns 输出dns 5) 、修改dns 需要root ,然后输入 adb shell 然后输

python testng_单元测试工具 TestNG 使用

写一篇小文&#xff0c;介绍一下 Java 下单元测试工具 TestNG 的使用&#xff0c;代码在 IDEA 环境在编写。单元测试&#xff0c;顾名思义&#xff0c;对系统中原子性的功能进行测试&#xff0c;一般情况下是单元测试是针对某个功能函数的测试。编写单元测试是系统开发中重要的…

AM335x kernel4.4.12 LCD 时钟翻转设置记录

TI AM335x kernel 4.4.12 LCD display 时钟翻转记录 因为公司硬件上已经确定LCD 转LVDS 转换芯片上确认以上升沿时钟为基准&#xff0c;所以只能在软件上调整相关东西。 入口在&#xff1a; drivers/gpu/drm/tilcdc/tilcdc_drv.c入口函数&#xff1a; module_init(tilcdc_drm_i…

Cache占用过多内存导致Linux系统内存不足问题排查

问题描述Linux服务器内存使用量超过阈值&#xff0c;触发报警。问题排查首先&#xff0c;通过free命令观察系统的内存使用情况&#xff0c;显示如下&#xff1a;total used free shared buffers cached Mem: 24675796 24587144 88652 …

第2章 Python与数据分析

《Python数据分析基础教程》学习笔记。 第2章 Python与数据分析 2.1 Python数据分析常用的类库 类库是用来实现各种功能的类的集合。 -1. NumPy NumPy(Numerical Python)是Python科学计算的基础包&#xff0c;提供以下功能&#xff1a; 快速高效的多维数组对象ndarrray是其…

LSPCI具体解释分析

一、PCI简单介绍 PCI是一种外设总线规范。我们先来看一下什么是总线&#xff1a;总线是一种传输信号的路径或信道。典型情况是&#xff0c;总线是连接于一个或多个导体的电气连线&#xff0c;总 线上连接的全部设备可在同一时间收到全部的传输内容。总线由电气接口和编程接…

linux之ip route命令

1.基础知识 1.1 路由 &#xff08;Routing&#xff09; 1.1.1 路由策略 &#xff08;使用 ip rule 命令操作路由策略数据库&#xff09; 基于策略的路由比传统路由在功能上更强大&#xff0c;使用更灵活&#xff0c;它使网络管理员不仅能够根据目的地址而且能够根据报文大小、应…

违反Apache 2.0许可证再分发被指控,火山引擎回应

文 | 白开水不加糖出品 | OSC开源社区&#xff08;ID&#xff1a;oschina2013&#xff09;针对有关违反 Apache 2.0 许可证&#xff0c;重新发行 SkyWalking 的指控&#xff0c;火山引擎方面作出回应称&#xff1a;火山引擎相关负责人表示&#xff0c;火山引擎接到社区反馈后&a…

vue 日期格式化返回指定个数月份_vue过滤器实现日期格式化的案例分析

说明今天将要介绍的是vue中的过滤器&#xff0c;并且将实现一个日期格式化的小案例。大家都知道&#xff0c;我们获取当前日期可以通过Date对象获取。下面我将获取当前时间并打印出来。console.log(new Date());我们获取的是一个标准时间&#xff0c;控制台的输出如下所示。在实…

linux网络编程之IP协议首部格式与其配套使用的四个协议(ARP,RARP,ICMP,IGMP)和TCP、UDP协议头结构总结

首先声明,这篇博客是几篇博客转载然后总结在一起的,只当是学习笔记,不在意是什么原创和转载了,学到东西就好。 1、IP协议首部格式(IP协议处余网络层) IP数据报首部图片格式: 最高位在左边,记为0 bit;最低位在右边,记为31 bit 头部代码结构如下 //定义IP首部typede…

无线安全***--启程

无线安全将来会成为一个值得重视的领域&#xff0c;现在无线的普及大大的方便我们的生活&#xff0c;同时在带来的便利的同时也会给我带来新的威胁&#xff01;下面我来通过cdlinux以及BT5来演示现在比较常见的无线***之战。攻破解我们都知道现在的个人无线局域网基本都会使用w…

Java读取word文件,字体,颜色

在Android读取Word文件时&#xff0c;在网上查看时可以用tm-extractors&#xff0c;但好像没有提到怎么读取Word文档中字体的颜色&#xff0c;字体&#xff0c;上下标等相关的属性。但由于需要&#xff0c;要把doc文档中的内容&#xff08;字体&#xff0c;下划线&#xff0c;颜…