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 中信号去替换内核信号屏蔽字
  • 返回:成功,返回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,以便让进程知道该信号又发生过而进行延迟处理
  • 若干个信号一起设置为 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 进程与信号——信号特点、信号集和信号屏蔽函数的更多相关文章

  1. 三十、Linux 进程与信号——信号的概念及 signal 函数

    30.1 信号的基本概念 信号(signal)机制是Linux 系统中最为古老的进程之间的通信机制,解决进程在正常运行过程中被中断的问题,导致进程的处理流程会发生变化 信号是软件中断 信号是异步事件 ...

  2. 第三十四天- 线程队列、线程池(map/submit/shutdown/回调函数)

    1.线程列队 queue队列 :使用import queue,用法与进程Queue一样 class queue.Queue(maxsize=0) # 先进先出: q = queue.Queue(3) ...

  3. Gradle 1.12用户指南翻译——第三十四章. JaCoCo 插件

    本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  4. COJ966 WZJ的数据结构(负三十四)

    WZJ的数据结构(负三十四) 难度级别:C: 运行时间限制:20000ms: 运行空间限制:262144KB: 代码长度限制:2000000B 试题描述 给一棵n个节点的树,请对于形如"u  ...

  5. NeHe OpenGL教程 第三十四课:地形

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  6. JAVA之旅(三十四)——自定义服务端,URLConnection,正则表达式特点,匹配,切割,替换,获取,网页爬虫

    JAVA之旅(三十四)--自定义服务端,URLConnection,正则表达式特点,匹配,切割,替换,获取,网页爬虫 我们接着来说网络编程,TCP 一.自定义服务端 我们直接写一个服务端,让本机去连接 ...

  7. Java进阶(三十四)Integer与int的种种比较你知道多少?

    Java进阶(三十四)Integer与int的种种比较你知道多少? 前言 如果面试官问Integer与int的区别:估计大多数人只会说到两点:Ingeter是int的包装类,注意是一个类:int的初值 ...

  8. SQL注入之Sqli-labs系列第三十四关(基于宽字符逃逸POST注入)和三十五关

    开始挑战第三十四关和第三十五关(Bypass add addslashes) 0x1查看源码 本关是post型的注入漏洞,同样的也是将post过来的内容进行了 ' \ 的处理. if(isset($_ ...

  9. spring boot 常见三十四问

    Spring Boot 是微服务中最好的 Java 框架. 我们建议你能够成为一名 Spring Boot 的专家. 问题一 Spring Boot.Spring MVC 和 Spring 有什么区别 ...

  10. “全栈2019”Java多线程第三十四章:超时自动唤醒被等待的线程

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

随机推荐

  1. Dividing the Path POJ - 2373(单调队列优化dp)

    给出一个n长度的区间,然后有一些小区间只能被喷水一次,其他区间可以喷水多次,然后问你要把这个区间覆盖起来最小需要多少喷头,喷头的半径是[a, b]. 对于每个只能覆盖一次的区间,我们可以把他中间的部分 ...

  2. angular与vue的应用对比

    因为各种笔试面试,最近都没时间做一些值得分享的东西,正好复习一下vue技术栈,与angular做一下对比. angular1就跟vue比略low了. 1.数据绑定 ng1 ng-bind,{{ sco ...

  3. 跟angular2学一键开启项目--关于上个react-redux项目的一键调试

    一键调试类似于webpack的hot-loader,但是这个hot-loader并不怎么好用,想省事的同学可以配置一下就完了. 今天介绍browser-sync,用它来一键开启项目.它可以监听任意文件 ...

  4. SQL查询语句的进阶使用

    MySQL的进阶使用 sql语句一些功能的使用 导入现有大量数据文件步骤 1) 把*.sql文件拷贝到Linux某一位置(例如Desktop) 2) Linux命令行进入该位置 cd ~/Deskto ...

  5. CAS与ABA问题产生和解决

    乐观锁和悲观锁 Synchronized属于悲观锁,悲观地认为程序中的并发情况严重,所以严防死守.CAS属于乐观锁,乐观地认为程序中的并发情况不那么严重,所以让线程不断去尝试更新. 性能对比: Syn ...

  6. 五大理由分析Springboot 2.0为什么选择HikariCP

    五大理由分析Springboot 2.0为什么选择HikariCP 2018-05-04 工匠小猪猪 占小狼的博客 本文非原创,是工匠小猪猪的技术世界搜集了一些HikariCP相关的资料整理给大家的介 ...

  7. 自制模态窗体闪烁效果: MessageBeep & FlashWindowEx

    SetFocus(hwnd_frame_preview); //设置焦点 /** 模拟模态窗口动作 **/ MessageBeep(0xFFFFFFFF); //0xFFFFFFFF SystemDe ...

  8. python3+django2 开发易语言网络验证(下)

    第六步:网络验证服务器端项目上线部署 功夫不负有心人,终于部署成功啦! 前期准备: 项目名:netauth 系统:百度云服务器下的Ubuntu16.4 软件:xshell(无论如何想办法用这个跟服务器 ...

  9. 自定义QMenu

    参考: http://blog.csdn.net/qq1623803207/article/details/77449884 http://blog.sina.com.cn/s/blog_a6fb6c ...

  10. centos7.4+openvpn-2.4.4+easy-rsa-3.0

    [生产环境物理机安装]openvpn2.4.4服务搭建,并可以正常运行 服务器内网:172.31.33.64 openvpn:10.8.0.0 本机ifconfig yum install epel- ...