进程信号(信号产生、注册、注销、处理),信号阻塞和volatile关键字

2023-11-12

进程信号

信号:信号是一个软件中断;
作用:操作系统通过信号告诉进程发生了某个事件,打断进程当前操作,去处理这个事情
操作系统中的信号:通过 kill -l 命令可以查看系统中的信号种类(62种)

1~31好信号:从Uinux借而来,每个信号都有具体对应的系统事件;(非可靠信号,有可能丢失信号);
34~64号信号:后期补充的,因为没有具体对应的事件,因此命名比较草率;(可靠信号,不会丢失信号);

信号的生命周期:1、产生;2、在进程中注册;3、在进程中注销;4、信号处理;

信号产生

硬件:ctrl+c / ctrl+z /ctrl + |
软件:kill -signum pid命令 kill默认发送15号进程 /kill(int pid, int signum) / raise(int signum) / abort( ) /alarm(int seconds)
kill杀死一个进程的原理:向进程发送一个信号,信号有对应的事件,进程放下手头工作去处理这个事件,然而事件的处理结果就是让进程退出。fg的功能是将一个暂停的后台程序调到前台运行

  • kill(进程id,信号值)
  • kill(getpid(),SIGHUP); // 给指定进程发送指定的信号
  • raise(SIGTERM); //给进程自己发送指定的信号
  • abort( ); //给自己发送SIGABRT信号通常用于异常通知
  • alarm(3); //3秒之后给进程自己发送SIGALRM信号(相当于定时器)

信号在进程中注册

如何让进程知道自己收集了某个信号;pcb -> struct sigpending -> struct sigset_t
sigset_t这个结构体中只有一个数据成员;这个数组用于实现一个位图 - - - 称之为未决信号集合(收到了但是没有被处理的信号集合);
给一个进程发送一个信号,就会将这个位图中对应位置为1,表示进程当前收到了这个信号;
信号的注册其实不仅会修改位图,还会为信号组织一个sigqueue节点添加到pcb的sigqueue链表中;

  • 1~31号非可靠信号注册:若信号注册的时候位图为0,则互创建一个sigqueue节点并修改位图为1,但是若位图为1,则什么也不做;
  • 34~64号可靠信号的注册:不管位图当前是否为0,都会创建一个节点,添加到链表中,并修改位图;

信号在进程的注销

为了保证一个信号只会被处理一次,因此先注销再处理;在pcb中删除当前信号信息;将pending位图置为0;删除信号节点;
非可靠信号注销:因为非可靠信号只会有一个节点,因此删除节点后,位图直接置为0;
可靠信号注销:因为可靠信号有可能注册多次,有多个节点,因此删除节点后,需要判断是否还有相同节点,若没有才会将位图置为0;

为什么要注销?(先注销后处理)
答:因为有这个信号需要处理,并且取出了这个信号的信息,因此注销之后就立即去处理;这样保证一个信号只会被处理一次。

信号的处理

信号表示一个事件的到来,处理事件就是完成功能,(在C语言中完成一个功能的最小模块为- - -函数)。
其实每一个信号都对应有自己的事件处理函数,信号到来,去处理这个事件就是去执行这个处理函数;执行完毕事件就处理完了。

信号的处理方式:

  1. 默认处理方式:操作系统中原定义好的每个信号的处理方式;
  2. 忽略处理方式:处理方式就是忽略,什么也不做;
  3. 自定义处理方式:自己定义一个回调函数,使用这个函数替换内核中默认的处理函数;信号到来就会调用我们定义的函数了。
    在这里插入图片描述
    typedef void(*sighandler_t)(int signo); - - - - 定义了一个名称为sighandler_t的函数指针类型。
    sighandler_t signal(int signum,sighandler_t handler);
    handler :SIG_DFL- - - 默认处理方式 / SIG_IGN - - - 忽略处理方式 / 用户自己定义的一个没有返回值,有个int型参数的函数地址。

自定义信号的捕捉流程

1、主控流程因为中断/异常/系统调用切换到内核态运行;
2、在返回用户态之前处理信号;
3、返回用户态运行自定义回调函数;
4、返回内核态;
5、返回用户态主控流程;

在这里插入图片描述
默认处理方式调用的函数与忽略处理方式调用的函数都是系统中已经实现的 - - -内核中直接处理。
自定义信号处理方式- - - -用户自己写一个事件处理函数;

信号阻塞

并不是不接收信号。信号依然可以注册,只是标识哪些信号暂时不处理。
在pcb中有一个位图,位图叫block位图 - - - 阻塞信号集合,这个集合中的信号如果来了(添加到pending位图中)则暂时不处理。
在这里插入图片描述

如何阻塞一个信号?

所有信号中,有两个信号比较特殊:SIGKILL -9 / SIGSTOP -19,这两个信号不可被阻塞,不可被忽略,不可被自定义。

int sigprocmask(int how, sigset_t *set, sigset_t *old);

how

SIG_BLOCK - - -将set集合中的信号添加到内核中的block阻塞信号集合中,使用old保存原来的阻塞信息以便于还原- - -(阻塞set集合中的信号);
SIG_UNBLOCK - - - 将set集合中的信号从内核中的block阻塞信号集合中移除- - - (对set集合中的信号解除阻塞);
SIG_SETMASK - - - 将内核中的block阻塞信号集合内容设置为set集合中的信息 - - -(阻塞set集合中的信号);
SIG_BLOCK - - -set | block;
SIG_UNBLOCK - - - set & block;
SIG_SETMASK - - - block = set;

0.将一些信号的处理函数自定义;1.将所有的信号都给阻塞;2.在解除阻塞之前,给进程发送信号;3.解除阻塞,查看信号的处理情况。

int sigemptyset(sigset_t *set); //清空set信号集合 - - - 使用一个变量时的初始化过程
int sigaddset(sigset_t *set, int signum); //向set集合中添加指定的信号
int sigdelset(sigset_t *set,int signum); //从set集合中移除指定的信号
int sigismember(const sigset_t *set,int signum); //判断指定信号是否在set集合中
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>

void sigcb(int signo)
{
    printf("recv a signal:%d\n", signo);
}
int main()
{
    signal(SIGINT, sigcb);
    signal(SIGRTMIN+4, sigcb);

    sigset_t set;
    sigemptyset(&set);//清空集合,防止未知数据造成影响
    sigfillset(&set);//向集合中添加所有信号

    sigprocmask(SIG_BLOCK, &set, NULL);//阻塞set集合中的所有信号

    printf("press enter coninue\n");
    getchar();//等待一个回车,如果不按回车就一直卡在这里

    sigprocmask(SIG_UNBLOCK, &set, NULL);//解除set集合中的信号阻塞

    while(1) {
        sleep(1);
    }
    return 0;
}

僵尸进程:子进程退出后会向父进程发送SIGCHLD信号通知父进程,子进程的状态改变;但是因为SIGCHLD信号默认 处理方式是忽略;因此之前的程序中若不进行进程等待则不通知子进程退出。
如果进行进程等待,而且不想让父进程阻塞,就可以自定义SIGCHLD信号的处理方式;
在自定义回调函数中调用waipid,处理僵尸进程,父进程就不用一直等待;
但是SIGCHLD信号是一个非可靠信号,如果有多个子进程同时退出,有可能造成信号丢失。

while(waitpid(-1,NULL,WNOHANG)>0);//非阻塞循环在一个回调中将所有的僵尸进程全部处理
waitpid(int pid,int *status, int options)
options:WNOHANG- - -将waipid设置为非阻塞,没有子进程退出则立即报错返回;(0 - - - 默认阻塞等待子进程退出);
返回值:>0(退出的子进程pid);==0(有子进程但是没有退出); <0 (出错了,比如当前没有子进程);

关键字volatile

用于修饰一个变量,保持变量的内存可见性(cpu在处理的时候每次都重新从内存获取数据),防止编译器过度优化。
cpu处理一个数据的过程时从内存中将数据加载到寄存器上进行处理;
gcc编译器在编译的程序的时候,如果使用了代码优化 -Olevel选项,发现某个变量使用频率非常高,为了提高效率,则直接将变量值设置为某个寄存器的值,以后访问的时候直接从寄存器访问,则减少了内存访问的过程,提高效率。(但是这种优化有时候会造成代码的逻辑混乱)。
因此使用volatile关键字修饰变量,让cpu无论如何每次都重新到内存中获取数据。

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

volatile long long  a = 1;

void sigcb(int no)
{
    a=0;
    printf("a=%d\n", a);
}
int main()
{
    signal(SIGINT, sigcb);
    while(a) {
    }
    printf("exited a=%d\n", a);
    return 0;
}

函数的可重入和不可重入

函数的重入:在多个执行流程中,同时进入一个函数运行。
函数可重入:函数重入之后,不会造成数据二义或者逻辑混乱;
函数不可重入:函数重入之后,有可能会造成数据二义或者逻辑混乱。
在这里插入图片描述

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

int a = 1, b = 1;

int test() {
    a++;
    sleep(3);
    b++;
    return a+b;
}

void sigcb(int no) {
    printf("signal sum:%d\n", test());
}
int main()
{
    signal(SIGINT, sigcb);
    printf("main sum:%d\n", test());
    return 0;
}

函数是否可重入的判断基准:这个函数中是否对全局变量进行了非原子的操作,若有则不可重入。
操作的原子性:操作一次完成,中间不会被打断;
原子操作:操作要么一次完成,要么就不操作;
一个函数如果根本没有操作全局数据,则肯定是可重入的,因为每个函数调用的时候都有独立的函数栈。
一个函数若对全局数据进行操作,但是操作是原子性的,则也是可重入的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

进程信号(信号产生、注册、注销、处理),信号阻塞和volatile关键字 的相关文章

  • 在Linux上编译C# + WPF以便在Windows上运行

    我有一个 C 应用程序 其中某些部分是使用 WPF 编写的 Mono 不支持 可以在 Linux 上编译这个应用程序吗 最终 该应用程序将在 Windows 上运行 但它是更大框架的一部分 并且我们的整个构建过程在 Linux 上运行 因此
  • ansible 重新启动 2.1.1.0 失败

    我一直在尝试创建一个非常简单的 Ansible 剧本 它将重新启动服务器并等待它回来 我过去在 Ansible 1 9 上有一个可以运行的 但我最近升级到 2 1 1 0 并且失败了 我正在重新启动的主机名为 idm IP 为 192 16
  • 为什么我收到的数据包数据大小大于mss?

    我在两台 PC 上使用 ifconfig ethX mtu 300 修改了 MTU 并使用 netperf 测试网络 我用 WireShark 嗅探了 SYN 数据包中的 MSS 260 但我得到了一些大于 260 的数据包 为什么 嗅探器
  • 修改linux下的路径

    虽然我认为我已经接近 Linux 专业人士 但显然我仍然是一个初学者 当我登录服务器时 我需要使用最新版本的R 统计软件 R 安装在 2 个地方 当我运行以下命令时 which R I get usr bin R 进而 R version
  • bluetoothctl 到 hcitool 等效命令

    在 Linux 中 我曾经使用 hidd connect mmac 来连接 BT 设备 但自 Bluez5 以来 这种情况已经消失了 我可以使用 bluetoothctl 手动建立连接 但我需要从我的应用程序使用这些命令 并且使用 blue
  • 从 PL/SQL 调用 shell 脚本,但 shell 以 grid 用户而非 oracle 身份执行

    我正在尝试使用 Runtime getRuntime exec 从 Oracle 数据库内部执行 shell 脚本 在 Red Hat 5 5 上运行的 Oracle 11 2 0 4 EE CREATE OR REPLACE proced
  • 是否可以在Linux上将C转换为asm而不链接libc?

    测试平台为Linux 32位 但也欢迎 Windows 32 位上的某些解决方案 这是一个c代码片段 int a 0 printf d n a 如果我使用 gcc 生成汇编代码 gcc S test c 然后我会得到 movl 0 28 e
  • 如何检测并找出程序是否陷入死锁?

    这是一道面试题 如何检测并确定程序是否陷入死锁 是否有一些工具可用于在 Linux Unix 系统上执行此操作 我的想法 如果程序没有任何进展并且其状态为运行 则为死锁 但是 其他原因也可能导致此问题 开源工具有valgrind halgr
  • 通过特定分隔符删除字符串

    我的文件中有几列 其中第二列有 分隔符 我想删除第二列中的第一个 第三个和第四个字符串 并将第二个字符串留在该列中 但我有正常的分隔符空间 所以我不知道 input 22 16050075 A G 16050075 A G 22 16050
  • 使用 grep 查找包含所有搜索字符串的行

    我有一个文件 其中包含很多与此类似的行 id 2796 some model Profile message type MODEL SAVE fields account 14 address null modification times
  • Jenkins中找不到环境变量

    我想在詹金斯中设置很多变量 我试过把它们放进去 bashrc bash profile and profile of the jenkins用户 但 Jenkins 在构建发生时找不到它们 唯一有效的方法是将所有环境变量放入Jenkinsf
  • 如何查明CONFIG_FANOTIFY_ACCESS_PERMISSIONS是否启用?

    我想利用fanotify 7 http man7 org linux man pages man7 fanotify 7 html我遇到的问题是在某些内核上CONFIG FANOTIFY ACCESS PERMISSIONS不起作用 虽然C
  • 为什么内核需要虚拟寻址?

    在Linux中 每个进程都有其虚拟地址空间 例如 32位系统为4GB 其中3GB为进程保留 1GB为内核保留 这种虚拟寻址机制有助于隔离每个进程的地址空间 对于流程来说这是可以理解的 因为有很多流程 但既然我们只有 1 个内核 那么为什么我
  • 在哪里可以找到并安装 pygame 的依赖项?

    我对 Linux 比较陌生 正在尝试安装 python 的 pygame 开发环境 当我运行 setup py 时 它说我需要安装以下依赖项 我找到并安装了其中之一 SDL 然而 其他人则更加难以捉摸 Hunting dependencie
  • 所有平台上的java

    如果您想用 java 为 Windows Mac 和 Linux 编写桌面应用程序 那么所有这些代码都相同吗 您只需更改 GUI 即可使 Windows 应用程序更像 Windows 等等 如果不深入细节 它是如何工作的 Java 的卖点之
  • nslookup 报告“无法解析 '(null)': 名称无法解析”,尽管它成功解析了 DNS 名称

    我在 ubuntu 上 并且正在运行 docker 默认桥接网络 我有 Zookeeper kafka 的容器化版本 以及我编写的与 kafka 对话的应用程序 I do a docker exec it
  • 如何在 Linux shell 中将十六进制转换为 ASCII 字符?

    假设我有一个字符串5a 这是 ASCII 字母的十六进制表示Z 我需要找到一个 Linux shell 命令 它将接受一个十六进制字符串并输出该十六进制字符串代表的 ASCII 字符 所以如果我这样做 echo 5a command im
  • Linux 内核标识符中前导和尾随下划线的含义是什么?

    我不断遇到一些小约定 比如 KERNEL Are the 在这种情况下 是内核开发人员使用的命名约定 还是以这种方式命名宏的语法特定原因 整个代码中有很多这样的例子 例如 某些函数和变量以 甚至 这有什么具体原因吗 它似乎被广泛使用 我只需
  • Linux:在文件保存时触发 Shell 命令

    我想在修改文件时自动触发 shell 命令 我认为这可以通过注册 inotify 挂钩并调用来在代码中完成system 但是是否有更高级别的 bash 命令可以完成此任务 尝试 inotify 工具 我在复制链接时遇到问题 抱歉 但 Git
  • jpegtran 优化而不更改文件名

    我需要优化一些图像 但不更改它们的名称 jpegtran copy none optimize image jpg gt image jpg 但是 这似乎创建了 0 的文件大小 当我对不同的文件名执行此操作时 大小仍然完全相同 怎么样 jp

随机推荐

  • Python使用selenium设置无浏览器(界面)运行

    设置无界面 浏览器 运行代码 from selenium import webdriver from selenium webdriver import ChromeOptions from selenium webdriver suppo
  • HDR dump失败解决办法

    运行脚本 进HDR拍照 一般在 sdcard Android data com oplus camera files spdebug hdrdump 目录下 可以生成dump 若没有生成dump 清除相机缓存 重新运行脚本 重启手机即可 脚
  • Apache Solr入门教程(初学者之旅)

    Apache Solr入门教程 初学者之旅 写在前面 本文涉及solr入门的各方面 建议边思考边实践 相信能帮助你对solr有个清晰全面的了解并能简单实用 在Apache Solr初学者教程的这个例子中 我们将讨论有关如何安装最新版本的Ap
  • 0-1分布的方差和期望

    最后欢迎大家访问我的个人网站 1024s
  • CentOS7.4下C++开源日志库easyloggingpp的使用

    CentOS7 4下C 开源日志库easyloggingpp的使用 一 简单示例 二 多线程支持 Linux后台开发过程中经常需要日志记录一些运行信息 网上找到easyloggingpp只需要包含头文件和实现文件即可 使用很方便 现整理如下
  • vue:结合elementUI设计网站登录页

    这次主要是记录三个重点 1 组件间通信的方法 其一 2 脚手架搭建的vue工程的组件调用 3 elementUI 的轮播图与模态框的设计 先看效果图 简单的就做了这四个页面 总共四个组件实现这些效果 这个小网站需要的组件也挺多的 当然不止这
  • Pandas——读/写不同数据源的数据

    Pandas 读 写不同数据源的数据 一 读 写数据库数据 1 SQLAlchemy连接MySQL数据库 2 使用 read sql query read sql table read sql 函数 读 取数据库数据 3 使用 to sql
  • 全面总结机器学习超参数调优(附代码)

    公众号 尤而小屋作者 Peter编辑 Peter 大家好 我是Peter 本文的主题 机器学习建模的超参数调优 开局一张图 文章很长 建议直接收藏 一 什么是机器学习超参数 机器学习超参数是在开始学习过程之前设置值的参数 而不是通过训练得到
  • 导入elementui组件库

    1 在终端输入 vue add element 回车 选择按需加载 输入yes 回车 选择zh CN 回车 2 在plugins里面有一个element js 我们先去elementui官网看到快速上手 就可以看到按需引入 复制代码放到el
  • 搭建 react+ts+less+Antd 项目

    搭建 react ts less Antd 项目 一 文章目录 搭建 react ts less Antd 项目 一 前言 项目搭建流程 1 新建项目 2 暴露配置 3 支持less 4 支持ts 前言 项目搭建流程 1 新建项目 npx
  • 谷粒商城-分布式高级篇[商城业务-检索服务]

    谷粒商城 分布式基础篇 环境准备 谷粒商城 分布式基础 业务编写 谷粒商城 分布式高级篇 业务编写 持续更新 谷粒商城 分布式高级篇 ElasticSearch 谷粒商城 分布式高级篇 分布式锁与缓存 项目托管于gitee 一 商城业务 检
  • ckplayer html播放本地,vue中使用ckplayer播放器

    请选择视频源 获取视频中 请稍等 export default name components props sourceUrl type String default height type String default 550px dat
  • python统计套利_基于python的统计套利实战(二)之协整检验

    协整关系协整 Cointegration 理论是恩格尔 Engle 和格兰杰 Granger 在1978年提出的 平稳性是进行时间序列分析的一个很重要的前提 很多模型都是基于平稳下进行的 而现实中 很多时间序列都是非平稳的 所以协整是从分析
  • 智能优化算法改进-K-means聚类种群初始化附Matlab代码

    目录 0引言 一 K means聚类原理 二 K Means聚类算法步骤 三 K Means聚类原理图 编辑 四 K means聚类改进智能优化算法种群初始化效果图 4 1 初始种群数据图 4 2 K means聚类结果图 4 2 1 根据
  • VM虚拟机下 Ubuntu下摄像头显示安装

    安装 可通过PPA进行安装 ffmpeg sudo add apt repository ppa kirillshkrogalev ffmpeg next sudo apt get update sudo apt get install f
  • 浅谈Router和Route

    router 和 route 是在前端框架中用于管理和处理路由的两个关键概念 这两者之间的关系可以通过具体的代码来解释 在本示例中 我将使用 React 和 React Router 来说明它们之间的关系 Router 路由器 Router
  • DataPipeline丨DataOps的技术考量

    作者 DataPipeline CEO 陈诚 从 数据的资产负债表与现状 到 DataOps理念与设计原则 直至 DataOps的组织架构与挑战 我们对于DataOps的讨论已经进行了三周 不难发现 在此期间 我们探讨的话题始终围绕在上层建
  • SSR、SSE、SST、R2

    在MATLAB中 计算回归问题的拟合优度 或判定系数 可用 B BINT R RINT STATS regress Y X 指令 其中的STATS的第一个返回值即为R2 R2约接近于1 拟合效果越好 SSR为回归平方和 SSE为残差平方和
  • React官网入门项目井字棋游戏

    React官网里有很详细的教程 也有在线沙盒 但是写的东一榔头西一棒槌的 不适合新手入门 所以我还是建议大家可以先去看看阮一峰大神的React博客或者某硅谷的网课 这个网课讲的很详细 甚至详细到有些啰嗦 我大概是用20天把网课看完 然后再看
  • 进程信号(信号产生、注册、注销、处理),信号阻塞和volatile关键字

    文章目录 进程信号 信号产生 信号在进程中注册 信号在进程的注销 信号的处理 信号的处理方式 信号阻塞 如何阻塞一个信号 int sigprocmask int how sigset t set sigset t old int sigem