【Linux】进程间通信之信号机制2

文章目录

  • 信号阻塞代码验证
    • 验证信号的阻塞
    • 验证信号的阻塞不影响信号注册
    • 验证可靠信号不会丢信号,不可靠信号会丢信号
    • 验证9号和19号信号不能被阻塞
  • 用信号解决僵尸进程
  • volatile关键字

信号阻塞代码验证

在上篇详解信号机制的博文中,我们提到了设置阻塞位图的函数sigprocmask函数:

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

现在我们用代码来演示一下:

首先了解一下位图的设置函数:

在这里插入图片描述

验证信号的阻塞

代码如下:

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <signal.h>
  4 void handler(int signum)
  5 {
  6   printf("signum : %d\n",signum);
  7 }
  8 int main(){
  9   signal(2, handler);
 10   sigset_t set;
 11   sigaddset(&set, 2);                                       
 12   sigprocmask(SIG_BLOCK, &set, NULL);
 13   while(1){
 14     printf("Hello!\n");
 15     sleep(1);
 16   }
 17   return 0;
 18 }

执行结果:
在这里插入图片描述

验证信号的阻塞不影响信号注册

验证可靠信号不会丢信号,不可靠信号会丢信号

验证思路:

我们选取两个信号2(非可靠信号)、40(可靠信号),首先把这两个信号阻塞,其次再接触这两个信号的阻塞,观察2号信号和40号信号处理几次,同时也证明可靠信号不能丢失信号,非可靠信号会丢失信号

代码如下:

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <signal.h>
  4 void sigcallback(int sig){                                                                                                                               
  5     printf("sigcallback recv sig: %d\n", sig);
  6 }                                             
  7  
  8 int main(){
  9   signal(2, sigcallback);
 10   signal(40, sigcallback);
 11                           
 12   sigset_t set;
 13   sigemptyset(&set);
 14   sigaddset(&set, 2);
 15   sigaddset(&set, 40);
 16                       
 17   sigprocmask(SIG_BLOCK, &set, NULL);
 18   getchar();                         
 19   sigset_t oldset;
 20   sigemptyset(&oldset);
 21   sigprocmask(SIG_SETMASK, &oldset, NULL);
 22                                           
 23   while(1){       
 24     printf("i am main, sleep(1)\n");
 25     sleep(1);                             
 26   }          
 27   return 0;
 28 }        

首先代码是阻塞的,我们发送多次信号,但是都是反应,接着我们随便敲了一个字符,此时启动getchar后面的代码,也就是解除阻塞,此时可以看到程序对之前阻塞了的信号的处理情况

执行结果:

在这里插入图片描述

验证9号和19号信号不能被阻塞

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <signal.h>
  4 void handler(int signum)
  5 {
  6   printf("signum : %d\n",signum);    
  7 }
  8 
  9 int main(){
 10   signal(9, handler);
 11   signal(19, handler);
 12 
 13   sigset_t set;
 14   sigfillset(&set); // 位图全部置为1
 15 
 16   sigprocmask(SIG_SETMASK,&set,NULL);
 17   while(1)
 18   {
 19     printf("Hello!\n");
 20     sleep(1);
 21   }
 22 
 23   return 0;                                             
 24 }

执行结果:

9号信号和19号信号不执行我自定义的处理方式,依旧按照原有方式处理信号

用信号解决僵尸进程

我们之前在如何解决僵尸问题的时候,提到了用wait或者waitpid函数,但是这两个函数都有不方便的地方,wait函数在调用时,父进程一直处于阻塞等待等待子进程退出状态,waitpid函数需要配合循环,这样的结果就是我们的父进程得不到充分利用,只等着回收子进程退出状态信息了,这里我们可以用信号,对子进程进行回收。

代码如下:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>

void sigcallback(int sig){
    printf("sigcallback recv sig: %d\n", sig);
    wait(NULL);
}

int main(){
    /*
     * 目的: 解放父进程, 让父进程创建子进程之后, 还能执行父进程的代码逻辑。 并且,还能防止子进程变成僵尸进程
     *
     * 做法:
     *    将SIGCHLD信号的处理方式进行自定义
     *    在自定义的函数当中调用wait/waitpid函数
     *
     *    子进程退出之后, 会向父进程发送SIGCHLD信号
     *    父进程回调自定义处理函数, 从调用wait/waitpid函数, 回收子进程的退出状态信息
     * */

    /* 1. 自定义处理SIGCHLD信号 */
    signal(SIGCHLD, sigcallback);

    /*
     * 2. 创建子进程了
     * */

    pid_t ret = fork();
    perror("fork");
    if(ret < 0){
        perror("fork");
        return 0;
    }else if(ret == 0){
        //child
        sleep(5);
        printf("i am child\n");
    }else{ //ret > 0
        //father
        while(1){
            printf("i am father, exec father process code\n");
            sleep(1);
        }
    }
    return 0;
}

执行结果:

在这里插入图片描述

volatile关键字

volatile关键字作用:保证内存可见性,告诉编译器,该变量的值可能会在程序的控制之外被修改,因此编译器不应该对该变量的读写进行优化或缓存。每次CPU要计算的数据都是从内存中获取,拒绝编译时优化的方案(从寄存器当中获取),gcc/g++的编译选项“-O0, O1, -O2,-O3“,优化级别越来越高。(理解优化级别越高,程序可能执行的越快)优化级别越高就从寄存器中取值的可能性越大。

代码演示:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

int g_val = 1;

void sigcallback(int sig){
    printf("sigcallback recv sig: %d\n", sig);
    g_val = 0;
}

int main(){
    signal(2, sigcallback);
    while(g_val){
    }

    printf("hhh, jump down\n");
    return 0;
}

运行结果:

在这里插入图片描述

上述代码运行按下ctrl+c向进程发送2号信号,进程直接结束了,说明g_val的值被修改了,这是因为我们在编译时没有对程序进行优化,这时候是从内存中拿的

我们试着优化一下,执行结果:

在这里插入图片描述

当我们试着将g_val用volatile关键字声明:

volatile int g_val = 1;

再执行:

在这里插入图片描述
此时优化就不管用了,保证了从内存中读取数据

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue-9.集成(.editorconfig、.eslintrc.js、.prettierrc)

介绍 同时使用 .editorconfig、.prettierrc 和 .eslintrc.js 是很常见的做法&#xff0c;因为它们可以在不同层面上帮助确保代码的格式一致性和质量。这种组合可以在开发过程中提供全面的代码维护和质量保证。然而&#xff0c;这也可能增加一些复杂性&#xff0c;需要谨慎配置…

一文详解4种聚类算法及可视化(Python)

在这篇文章中&#xff0c;基于20家公司的股票价格时间序列数据。根据股票价格之间的相关性&#xff0c;看一下对这些公司进行聚类的四种不同方式。 苹果&#xff08;AAPL&#xff09;&#xff0c;亚马逊&#xff08;AMZN&#xff09;&#xff0c;Facebook&#xff08;META&…

【Java】Java如何生成随机数?

文章目录 前言一、Random类介绍二、Random类生成随机数1.生成随机数2.nextInt()方法 三、使用场景四、官方提示总结 前言 我们在学习 Java 基础时就知道可以生成随机数&#xff0c;可以为我们枯燥的学习增加那么一丢丢的乐趣。本文就来介绍 Java 随机数。 一、Random类介绍 …

docker的资源控制及docker数据管理

文章目录 docker的资源控制及docker数据管理一.docker的资源控制1.CPU 资源控制1.1 资源控制工具1.2 cgroups有四大功能1.3 设置CPU使用率上限1.4 进行CPU压力测试1.5 设置50%的比例分配CPU使用时间上限1.6 设置CPU资源占用比&#xff08;设置多个容器时才有效&#xff09;1.6.…

合宙Air724UG LuatOS-Air LVGL API--简介

为何是 LVGL LVGL 是一个开源的图形库&#xff0c;它提供了创建嵌入式 GUI 所需的一切&#xff0c;具有易于使用的图形元素、漂亮的视觉效果和低内存占用的特点。 LVGL特点&#xff1a; 强大的 控件 &#xff1a;按钮、图表、列表、滑动条、图像等 高级图形引擎&#xff1a;动…

【Visual Studio】生成.i文件

环境 VS版本&#xff1a;VS2013 问题 如何生成.i预编译文件&#xff1f; 步骤 1、打开VS项目属性&#xff0c;打开C/C\预处理器页面&#xff0c;【预处理到文件】选择是&#xff0c;开启。 2、生成文件如下。 3、正常编译需要关闭此选项。

ORB-SLAM2学习笔记9之图像帧Frame

文章目录 0 引言1 Frame类1.1 构造和重载函数1.1.1 双目相机1.1.2 RGBD相机1.1.3 单目相机 1.2 成员函数1.2.1 特征点去畸变1.2.2 特征点网格分配1.2.3 双目匹配1.2.4 RGBD相机深度计算 1.3 成员变量 2 Frame类的用途 0 引言 ORB-SLAM2学习笔记7详细了解了System主类和多线程和…

安卓图形显示系统

Android图形显示系统 Android图形显示系统是Android比较重要的一个子系统&#xff0c;和很多其他子系统的关联紧密。 Android图形系统比较复杂&#xff0c;这里我们从整体上理一遍&#xff0c;细节留待后期再去深入。Android图形系统主要包括以下几个方面&#xff1a; - 渲染…

Shell编程及自动化运维实现

Linux Shell编程及自动化运维实现 变量 Linux Shell编程及自动化运维实现 判断 Linux Shell编程及自动化运维实现 循环 Linux Shell编程及自动化运维实现 数组和函数 Linux Shell编程及自动化运维实现 三剑客 Linux Shell编程及自动化运维实现 综合实战 什么是…

API 接口选择那个?RESTful、GraphQL、gRPC、WebSocket、Webhook

大家好&#xff0c;我是比特桃。目前我们的生活紧紧地被大量互联网服务所包围&#xff0c;互联网上每天都有数百亿次API调用。API 是两个设备相互通讯的一种方式&#xff0c;人们在手机上每次指尖的悦动&#xff0c;背后都是 API 接口的调用。 本文将列举常见的一些 API 接口&…

code论坛系统测试

目录 一 项目介绍**项目名称****项目介绍****项目功能****项目展示** 二 测试用例设计和功能测试1.测试用例设计**①登录页面****②注册页面****③首页****④发布帖子页面****⑤修改个人信息页面** 2.功能测试环境3.实际执行功能测试的部分操作**①登录页面****②注册页面****③…

ps怎么布尔运算多个图层合并?

我们经常使用Photoshop制作大型海报类&#xff0c;也可以用ps进行一些简单icon小图标的制作&#xff0c;这些icon图标多数应用在工具按钮上&#xff0c;比较小巧美观。但是对于ps对图形的操作经常会用到布尔运算的使用&#xff0c;今天小编就给大家详细讲解下ps布尔运算多个图层…

C语言和JavaScript中的默认排序行为对比

前言 今天在js里使用sort时遇见了一个不理解的现象 即使用sort默认排序后 9 从排序前的第一位被排到了最后一位.一开始我对js sort的理解和c一样&#xff0c;然后通过查阅后发现并不是这样. 正文 排序是一项常见而重要的操作。不同的编程语言提供了不同的排序函数&#xf…

常见的网络设备有哪些?分别有什么作用?

个人主页&#xff1a;insist--个人主页​​​​​​ 本文专栏&#xff1a;网络基础——带你走进网络世界 本专栏会持续更新网络基础知识&#xff0c;希望大家多多支持&#xff0c;让我们一起探索这个神奇而广阔的网络世界。 目录 一、网络设备的概述 二、常见的网络设备 1、…

华星时空展锐芯片5g随身WiFi改串教程

前段时间入手了一个华正易尚&#xff0c;发现插手机卡可以用&#xff0c;插微闯移植卡直接没网&#xff0c;于是研究出展锐改串的教程分享给大家 ⭐注意:理论上所有的展锐芯片棒子都可以用&#xff0c;至于电池机请自行测试 话不多说&#xff0c;教程开始: 1.下载展锐AT改串驱…

Lnton羚通算法算力云平台如何在OpenCV-Python中使用cvui库创建复选框

CVUI 之 复选框 Python import numpy as np import cv2 import cvuidef checkbox_test():WINDOW_NAME Checkbox-Testchecked [False]# 创建画布frame np.zeros((300, 400, 3), np.uint8)# 初始化窗口cvui.init(WINDOW_NAME)while True:# 画布填色frame[:] (100, 200, 100…

Python学习日志(二)

数据类型转换 num_strstr(11)# 整数转换为字符串 print(type(num_str), num_str) 输出结果为&#xff1a; <class str> 11 类型转换成功&#xff0c;并且原本内容没有发生变化。 float_strstr(13.14)#小数转字符串 print(type(float_str),float_str) 同理&#xff0c;…

变动的Python爬虫实现

在电商时代&#xff0c;了解商品价格的变动对于购物者和卖家来说都非常重要。本文将分享一种基于Python的实时监控电商平台商品价格变动的爬虫实现方法。通过本文的解决方案和代码示例&#xff0c;您将能够轻松监控商品价格&#xff0c;并及时做出决策。 一、了解需求和目标 在…

Python将网络文件下载到本地

Python将网络文件下载到本地 前言相关介绍Python将网络文件下载到本地 前言 由于本人水平有限&#xff0c;难免出现错漏&#xff0c;敬请批评改正。更多精彩内容&#xff0c;可点击进入Python日常小操作专栏、YOLO系列专栏、自然语言处理专栏或我的个人主页查看基于DETR的人脸伪…

线段树详解——影子宽度

OK&#xff0c;今天来讲一讲线段树~~ 线段树是什么线段树的实现线段树的时间复杂度线段树的应用线段树的节点结构其他操作和优化例题——影子宽度输入输出格式输入格式输出格式 输入输出样例输入样例输出样例 例题讲解 线段树是什么 线段树&#xff08; S e g m e n t Segmen…