当前位置: 首页 > news >正文

Linux学习记录——이십삼 进程信号(2)

文章目录

  • 1、可重入函数
  • 2、volatile关键字
  • 3、如何理解编译器的优化
  • 4、SIGCHLD信号


1、可重入函数

两个执行流都执行一个函数时,这个函数就被重入了。比如同一个函数insert,在main中执行时,这个进程时间片到了,嵌入了内核,然后再执行另一个函数,这个函数里也调用了insert,当它执行完代码后,又回到main,继续刚才的位置,这就相当于insert被重复进入了,会导致一些问题。这也就说明insert是不可重入函数,如果重入后没有问题,那就是可重入函数。可重入是函数的一个特性。

如果一个函数内部使用了全局性的数据结构,那么这个函数通常是不可重入的;比如malloc,标准IO库的大多数函数都是不可重入含糊。如果一个函数里只有一些局部变量,那就可以重入

2、volatile关键字

#include <stdio.h>
#include <signal.h>int quit = 0;void handler(int signo)
{printf("change quit from 0 to 1\n");quit = 1;
}int main()
{signal(2, handler);while(!quit);//这里故意没有携带while的代码块,故意让编译器认为在main中,quit只会被检测printf("man quit 正常\n");return 0;
}

像这样的代码,可以发现如果如果不执行handler,while那里就会死循环,而执行handler后,while就结束,就打印正常的语句。

但是实际上gcc编译的时候是有一些编译级别的,它就做优化。

在这里插入图片描述

mysignal:mysignal.cgcc -o mysignal mysignal.cc -O2
.PHONY:clean
clean:rm -f mysignal

每个编译器做的优化不一样,分别试试O1 O2 O3 O0等这些选项,运行起来后当我们发送2号信号,也就是Ctrl + C,如果能退出,打印man quit正常,那就说明你的编译器优化级别是当前设置的这个级别。

这怎么优化得?这样一份简单的代码为什么要做优化呀?如果按Ctrl + C没有退出的时候,一直按,一直会打印change quit from 0 to 1,但quit貌似没有变成1,要不然就退出了。所以现在有很多问题,慢慢来。

CPU的运算有两种,算术运算和逻辑运算,这两个顾名思义。所有的运算都要在CPU里,那么到了while循环这里,存在物理内存的quit全局变量要load到CPU的寄存器,交给CPU去做逻辑判断,把结果告知while,CPU里还有PC指针指向这个程序的代码和数据,如果while条件成立,那就继续执行while的代码,如果不成立,PC指针就指向下一行代码,也就是printf那里。

改了信号处理动作后,收到信号,quit就变成1了,然后再次load到内存,再判断,就会退出了。

对于这个代码,编译器对它的优化在于,main函数块里只是判断quit,每一次while判断,判断完后又回到代码,然后再次将quit放到寄存器中,再去判断,所以编译器就在编译代码时把quit放进寄存器后就只在寄存器判断,不再回到内存中再次load了。这也就是编译器对它的优化,以后只需要看寄存器中的数据就好了。所以quit即使在内存中被改变了,寄存器也看不到。

所以编译器优化不一定好,CPU只看到寄存器的数据而看不到内存的,这就是内存位置不可见。那么为了让CPU每一次都从内存中读取数据,不用寄存器的数据,就得用volatile关键字。

volatile int quit = 0;

这个关键字的作用就是保证内存可见性。

3、如何理解编译器的优化

编译器的本质是在代码上动手脚。

CPU其实很笨,用户给什么就执行什么代码。相反操作系统才是很聪明的。

刚才的代码中,如果没优化,代码在转为汇编代码前,先把内存的某个数据先放到寄存器中,再放到另一个寄存器中,然后逻辑反一下,再检测后面放的那个寄存器中的数据即可,条件满足就跳转到循环,继续检测,如果不满足就运行后面的代码;如果优化了,那么循环就从逻辑反开始,而不是load内存数据开始,所以重复循环,它只能看到最一开始的那个数据,即使后面数据改了,也看不到了,因为会发现在循环中不需要改数据,改数据是在内存中改的,所以就直接这样优化了。

4、SIGCHLD信号

在之前,我们知道父进程会阻塞式等待或者非阻塞式轮询来得知子进程退出,这两个方法都需要父进程主动检测,这是因为子进程退出了,父进程不知道。但是这可不是因为子进程只管自己,什么也不说地退出了。

子进程会在结束进程后给父进程发送SIGCHLD信号,只不过父进程默认忽略这个信号。

waitpid的第一个参数也可以这样写。

在这里插入图片描述

测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <uhistd.h>
#include <signal.h>
pid_t id;void handler(int signo)
{sleep(5);printf("捕捉到一个信号: %d, who: %d\n", signo. getpid());pid_t res = waitpid(-1, NULL, 0);if(res > 0){printf("wait success, res: %d, id: %d\n", res, id);}
}int main()
{signal(SIGCHLD, handler);id = fork();if(id == 0){int cnt = 5;while(cnt){printf("我是子进程,我的pid: %d, ppid: %d\n", getpid(), getppid());sleep(1);--cnt;}exit(1);}while(1){sleep(1);}return 0;
}

另开一个窗口,用这行命令while :; do ps axj | head -1 && ps axj | grep mysignal; echo “################”; sleep 1; done,每隔一行检查一下,ehco后双引号的内容可自定义。

但是这个代码有问题,如果说子进程是循环创建了10个,那么这10个子进程依次退出,退出的时候第一个把父进程的pending位对应位置变为1,但是父进程忽略,然后第二个也重复置为1,这就相当于第一个的信号丢失了,这个代码运行起来后,就会发现只有几个被回收了。所以handler里面的回收也要有循环。

    while(1){pid_t res = waitpid(-1, NULL, 0);if(res > 0){printf("wait success, res: %d, id: %d\n", res, id);}else break;}int i = 1;for(; i <= 10; ++i){id = fork();if(id == 0){int cnt = 5;while(cnt){printf("我是子进程,我的pid: %d, ppid: %d\n", getpid(), getppid());sleep(1);--cnt;}exit(1);}}

-1的意思就是等待任意一个子进程,直到没有可回收的就结束了。但如果不是所有的进程都会退呢?比如每个进程的运行时间不一样,有5个退了,5个没退,回收5个后,第6个还会继续回收吗?还会继续回收,这样就变回了阻塞式等待。把waitpid的0换成WNOHANG,也就是非阻塞式。

要想不产生僵尸进程还有另外一种方法,父进程调用sigaction将SIGCHLD的处理动作置为SIG_IGN,这样fork出来的子进程在终止时会自动清理掉。

整体代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <uhistd.h>
#include <signal.h>
pid_t id;void handler(int signo)
{printf("捕捉到一个信号: %d, who: %d\n", signo. getpid());sleep(5);while(1){pid_t res = waitpid(-1, NULL, WNOHANG);if(res > 0){printf("wait success, res: %d, id: %d\n", res, id);}else break;}printf("handler done\n");
}int main()
{//signal(SIGCHLD, handler);signal(SIGCHLD, SIG_IGN);int i = 1;for(; i <= 10; ++i){id = fork();if(id == 0){int cnt = 5;while(cnt){printf("我是子进程,我的pid: %d, ppid: %d\n", getpid(), getppid());sleep(1);--cnt;}exit(1);}}while(1){sleep(1);}return 0;
}

本身父进程就会忽略这个信号,那为什么还要这样写出来?如果这样写的话,会修改父进程pcb的状态位,子进程继承过去,然后按照这个办法就执行了,因为这相当于系统调用,会修改pcb。但是这个只在Linux中有效,不保证其他Unix系统上可用。

结束。

相关文章:

Linux学习记录——이십삼 进程信号(2)

文章目录 1、可重入函数2、volatile关键字3、如何理解编译器的优化4、SIGCHLD信号 1、可重入函数 两个执行流都执行一个函数时&#xff0c;这个函数就被重入了。比如同一个函数insert&#xff0c;在main中执行时&#xff0c;这个进程时间片到了&#xff0c;嵌入了内核&#xf…...

Revit中如何创建曲面嵌板及一键成板

一、Revit中如何创建曲面嵌板 在我们的绘图过程中可能会遇见一些曲面形状&#xff0c;而我们的常规嵌板没办法满足我们绘制的要求&#xff0c;我们今天学习如何在revit中绘制曲面嵌板。 1.新建“自适应公制常规模型”族&#xff0c;创建4个点图元并为其使用自适应。 2.在相同的…...

STM32F4_DHT11数字温湿度传感器

目录 前言 1. DHT11简介 2. DHT11数据结构 3. DHT11的传输时序 3.1 DHT11开始发送数据流程 3.2 主机复位信号和DHT11响应信号 3.3 数字 “0” 信号表示方法 3.4 数字 “1” 信号表示方法 4. 硬件分析 5. 实验程序详解 5.1 main.c 5.2 DHT11.c 5.3 DHT11.h 前言 DH…...

WiFi(Wireless Fidelity)基础(十一)

目录 一、基本介绍&#xff08;Introduction&#xff09; 二、进化发展&#xff08;Evolution&#xff09; 三、PHY帧&#xff08;&#xff08;PHY Frame &#xff09; 四、MAC帧&#xff08;MAC Frame &#xff09; 五、协议&#xff08;Protocol&#xff09; 六、安全&#x…...

操作系统—— 精髓与设计原理--期末复习

一、计算机系统概述 1、基本构成 计算机有四个主要的结构化部件&#xff1a; ①处理器&#xff08;Processor&#xff09;&#xff1a;控制计算机的操作&#xff0c;执行数据处理功能。当只有一个处理器时&#xff0c;它通常指中央处理器&#xff08;CPU&#xff09; ②内存…...

每天一道算法练习题--Day21 第一章 --算法专题 --- ----------位运算

我这里总结了几道位运算的题目分享给大家&#xff0c;分别是 136 和 137&#xff0c; 260 和 645&#xff0c; 总共加起来四道题。 四道题全部都是位运算的套路&#xff0c;如果你想练习位运算的话&#xff0c;不要错过哦&#xff5e;&#xff5e; 前菜 开始之前我们先了解下…...

D1. LuoTianyi and the Floating Islands (Easy Version)(树形dp)

Problem - D1 - Codeforces 这是问题的简化版本。唯一的区别在于在该版本中k≤min(n,3)。只有在两个版本的问题都解决后&#xff0c;才能进行黑客攻击。 琴音和漂浮的岛屿。 洛天依现在生活在一个有n个漂浮岛屿的世界里。这些漂浮岛屿由n−1个无向航线连接&#xff0c;任意两个…...

rk3588移植ubuntu server

ubuntu server 18.04 arm版本. 1、使用qemu运行 安装qemu-system-aarch64 sudo apt install -y qemu-system-arm 2、下载ubuntu server Index of /releases/18.04.3 3、创建虚拟磁盘 qemu-img create ubuntuimg.img 40G 4、创建虚拟机 弹出界面&#xff0c;直接回车选…...

如何更好地刷力扣

之前刷力扣是一口气看很多题目&#xff0c;打算时不时看一会题解&#xff0c;逐渐熟悉套路&#xff0c;争取背过&#xff0c;最后就可以写出来了。我个人是背知识比较喜欢这种方法&#xff0c;但后来发现根本不适用 算法题本身就比较复杂&#xff0c;不经过实际写代码中的思考…...

上采样和下采样

首先&#xff0c;谈谈不平衡数据集。不平衡数据集指的是训练数据中不同类别的样本数量差别较大的情况。在这种情况下&#xff0c;模型容易出现偏差&#xff0c;导致模型对数量较少的类别预测效果不佳。 为了解决这个问题&#xff0c;可以使用上采样和下采样等方法来调整数据集…...

小猪,信息论与我们的生活

前言 动态规划是大家都熟悉与陌生的知识&#xff0c;非常灵活多变&#xff0c;我自己也不敢说自己掌握了&#xff0c;今天给大家介绍一道题&#xff0c;不仅局限于动态规划做题&#xff0c;还会上升到信息论&#xff0c;乃至于启发自己认知世界的角度 因为比较难&#xff0c;本…...

【鸿蒙应用ArkTS开发系列】- http网络库使用讲解和封装

目录 前言http网络库组件介绍http网络库封装创建Har Module创建RequestOption 配置类创建HttpCore核心类创建HttpManager核心类对外组件导出添加网络权限 http网络库依赖和使用依赖http网络库&#xff08;httpLibrary&#xff09;使用http网络库&#xff08;httpLibrary&#x…...

【Java零基础入门篇】第 ⑥ 期 - 异常处理

博主&#xff1a;命运之光 专栏&#xff1a;Java零基础入门 学习目标 掌握异常的概念&#xff0c;Java中的常见异常类&#xff1b; 掌握Java中如何捕获和处理异常&#xff1b; 掌握自定义异常类及其使用&#xff1b; 目录 异常概述 异常体系 常见的异常 Java的异常处理机制…...

计算职工工资

目录 问题描述 程序设计 问题描述 【问题描述】 给定N个职员的信息,包括姓名、基本工资、浮动工资和支出,要求编写程序顺序输出每位职员的姓名和实发工资(实发工资=基本工资+浮动工资-支出)。 【输入形式】 输入在一行中给出正整数N。随后N行,每行给出一位职员的信息,…...

2019年上半年软件设计师下午试题

试题四(共 15 分) 阅读下列说明和 C 代码&#xff0c;回答问题 1 至 3&#xff0c;将解答写在答题纸的对应栏内 【说明】 n 皇后问题描述为&#xff1a;在一个 n*n 的棋盘上摆放 n 个皇后&#xff0c;要求任意两个皇后不能冲突, 即任意两个皇后不在同一行、同一列或者同一斜…...

IS200TPROH1BCB用于工业应用和电力分配等。高压型隔离开关用于变电站

​ IS200TPROH1BCB用于工业应用和电力分配等。高压型隔离开关用于变电站 什么是隔离器&#xff0c;它与断路器有何不同 什么是隔离器&#xff0c;为什么要使用隔离器 隔离器是一种开关装置&#xff0c;它可以手动或自动操作&#xff0c;隔离一部分电能。隔离器可用于在无负载情…...

【MySql】数据库 select 进阶

数据库 数据库表的设计ER 关系图三大范式 聚合函数与分组查询聚合函数 (count、sum、avg、max、min)分组查询 group by fields....having....(条件) 多表联查内连接外连接&#xff08;左连接&#xff0c;右连接&#xff09;自连接子查询合并查询 UNION 数据库表的设计 ER 关系…...

CVPR 2023 | VoxelNeXt实现全稀疏3D检测跟踪,还能结合Seg Anything

在本文中&#xff0c;研究者提出了一个完全稀疏且以体素为基础的3D物体检测和跟踪框架VoxelNeXt。它采用简单的技术&#xff0c;运行快速&#xff0c;没有太多额外的成本&#xff0c;并且可以在没有NMS后处理的情况下以优雅的方式工作。VoxelNeXt在大规模数据集nuScenes、Waymo…...

本地使用3台centos7虚拟机搭建K8S集群教程

第一步 准备3台centos7虚拟机 3台虚拟机与主机的网络模式都是桥接的模式&#xff0c;也就是他们都是一台独立的“主机” &#xff08;1&#xff09;kebe-master的配置 虚拟机配置&#xff1a; 网络配置&#xff1a; &#xff08;2&#xff09;kebe-node1的配置 虚拟机配…...

NVIDIA CUDA驱动安装

1 引言 因为笔记本电脑上运行Milvus图像检索代码&#xff0c;需要安装CUDA驱动。电脑显卡型号是NVIDIA GeForce GTX 1050 Ti Mobile, 操作系统是Ubuntu 20.04&#xff0c;内核版本为Linux 5.15.0-72-generic。 2 CUDA驱动测试 参考网上的资料&#xff1a;https://blog.csdn.…...

python 从excel中获取需要执行的用例

classmethod def get_excel_data(cls, excel_name, sheet_name, case_numNone):"""读取excel文件的方法:param excel_name: 文件名称:param sheet_name: sheet页的名称:param case_name: 执行的case名称:return:"""def get_row_data(table, row)…...

Web3中文|乱花渐欲meme人眼,BRC-20总市值逼近10亿美元

现在的Web3加密市场&#xff0c;用“乱花渐欲meme人眼”来形容再合适不过了。 何为meme&#xff1f; “meme”这个词大概很多人都不知道如何正确发音&#xff0c;并且一看到它就会和狗狗币Dogecoin等联系在一起。那它究竟从何而来呢&#xff1f; Meme&#xff1a;[mi:m]&#x…...

盖雅案例入选「首届人力资源服务国际贸易交流合作大会20项创新经验」

近日&#xff0c;首届人力资源服务国际贸易交流合作大会顺利召开。为激励企业在人力资源服务贸易领域不断创新&#xff0c;加快培育对外贸易新业态、新模式&#xff0c;形成人力资源服务领域国际竞争新优势&#xff0c;大会评选出了「首届人力资源服务国际贸易交流合作大会20项…...

[论文笔记]SimMIM:a Simple Framework for Masked Image Modeling

文章地址&#xff1a;https://arxiv.org/abs/2111.09886 代码地址&#xff1a;https://github.com/microsoft/SimMIM 文章目录 摘要文章思路创新点文章框架Masking strategyPrediction headPrediction targetEvaluation protocols 性能实验实验设置Mask 策略预测头目标分辨率预…...

mysql从零开始(4)----索引/视图/范式

接上文 mysql从零开始&#xff08;3&#xff09; 索引 索引是在数据库表的字段上添加的&#xff0c;是为了提高查询效率存在的一种机制。一张表的一个字段可以添加一个索引&#xff0c;也可以多个字段联合起来添加索引。索引相当于一本书的目录&#xff0c;是为了缩小扫描范围…...

Flutter框架:从入门到实战,构建跨平台移动应用的全流程解析

第一章&#xff1a;Flutter框架介绍 Flutter框架是由Google推出的一款跨平台移动应用开发框架。相比其他跨平台框架&#xff0c;Flutter具有更高的性能和更好的用户体验。本章将介绍Flutter框架的概念、特点以及与其他跨平台框架的比较&#xff0c;以及Flutter开发环境的搭建和…...

Spring AOP+注解方式实现系统日志记录

一、前言 在上篇文章中&#xff0c;我们使用了AOP思想实现日志记录的功能&#xff0c;代码中采用了指定连接点方式&#xff08;Pointcut(“execution(* com.nowcoder.community.controller..(…))”)&#xff09;&#xff0c;指定后不需要在进行任何操作就可以记录日志了&…...

OpenGL 4.0的Tessellation Shader(细分曲面着色器)

细分曲面着色器&#xff08;Tessellation Shader&#xff09;处于顶点着色器阶段的下一个阶段&#xff0c;我们可以看以下链接的OpenGL渲染流水线的图&#xff1a;Rendering Pipeline Overview。它是由ATI在2001年率先设计出来的。 目录 细分曲面着色器细分曲面Patch细分曲面控…...

项目经理如何及时掌控项目进度?

延迟是指超出计划的时间&#xff0c;而无法掌控则意味着管理者对实际情况一无所知。 为了解决这些问题&#xff0c;我们需要建立好的制度和沟通机制。例如使用项目管理软件来跟踪进度、定期开会并避免沟通障碍等。 管理者可以建立相关制度&#xff1a; 1、建立进度记录制度。…...

HTML <applet> 标签

HTML5 中不支持 <applet> 标签在 HTML 4 中用于定义嵌入式小程序(插件)。 实例 一个嵌入的 Java applet: <applet code="Bubbles.class" width="350" height="350"> Java applet that draws animated bubbles. </applet&g…...