进程信号

2023-05-16

一、信号概念

1.一个信号产生及处理实例
1)在shell下,启动一个进程。
2)按下Ctrl+c,键盘输入产生一个硬件中断。
3)如果CPU正在运行这个进程则代码暂停执行,CPU从用户态返回到内核态。
4)终端程序将该动作解释成一个SIGINT信号,写在该进程的PCB中。
5)在某个时刻,CPU从内核返回到该进程的用户空间代码继续执行之前,先处理PCB记录的信号,发现是SIGINT待处理,这个信号的默认处理动作是终止进程,就完成一个信号的产生及处理过程。
2.备注
1)Ctrl+c产生的信号只能发给前台进程。一个命令后面加&,可以将该命令放到后台运行。
2)Shell只能运行一个前台进程或者多个后台进程
3)在用户代码段任意位置使用Ctrl+c,都有可能受到SIGINT信号而终止进程。
4)查看系统定义的信号列表

kill -l
//一共有62个信号,31以下是常规信号,以上是实时信号

这里写图片描述
3.信号的产生方式
1)通过终端按键产生信号。
2)硬件异常产生信号,除零,访问非法地址。
3)调用系统函数向进程发送信号。闹钟超时产生SIGALRM信号。当往管道写数据也会产生SIGPIPE信号。
4)软件条件产生。
4。信号处理操作方式

1.忽略
2.默认执行
3.自定义动作,也称为信号的捕捉,通过处理函数实现。

5.一些基本的信号
1)SIGINT
默认处理动作是终止进程
2)SIGQUIT
默认处理动作是终止进程并产生Core Dump。当一个进程发生异常并终止时,可以把用户空间内存数据全部保存在硬盘上,文件名通常为Core。这个Core文件可以用来调试检查错误。默认是不允许产生Core文件的,因为这个文件有可能包含用户的一些私人信息,如密码。在开发调试阶段可以突破这个限制。

ulimit: 限制系统用户对shell资源的访问,是一种临时限制,当一个会话结束时,便结束限制。
ulimit -a: 查看限制的各项资源
ulimit -c 1024: 允许产生最大的Core文件是1024个字节。

当你异常终止一个进程时就会产生文件名为Core,后缀为当前进程的pid。
3)SIGSEGV

//当一个进程产生无效内存或产生段错误时,SIGERGV发送信号
//发送某种信号的kill命令,可以使用两种方法
kill -SIGERGV 进程号 
kill -11 进程号

6.有关函数
1)kill函数与raise函数

#include <signal.h>
int kill(pid_t pid,int signo);
int raise(int signo);
//kill可以给一个指定进程发送指定信号,raise可以给当前进程发送信号(自己给自己发信号)
//成功返回0,错误返回-1;

2)abort函数可使当前进程收到信号并终止

#include <signal.h>
void abort(void);
//像exit一样。

3)alarm闹钟函数

unsigned int alarm(unsigned int seconds);
//可以设定一个闹钟,当多少秒之后给当前进程发送信号,该信号默认动作为终止进程
//返回值:返回零或者是以前设定闹钟时间还余下的秒数。如果参数为0,代表取消以前设定的闹钟。

二、信号阻塞

1、信号递达(Delivery)
实际执行信号处理动作。
2.信号未决(Pending)
信号从产生到递达之间的状态。
3.信号阻塞(Block)
进程可以选择阻塞某个信号,被阻塞的进程一直处于Pending状态,一旦阻塞被解除,就会执行递达。
这里写图片描述
4.内核中的表示
这里写图片描述
5.sigset_t:信号集
1)每一个信号使用一个位图表示,用0或1表示阻塞或未决,这些标志可以使用数据类型sigset_t表示。代表有效或无效。
2)阻塞信号集也被称为当前进程的信号屏蔽字(Signal Mask)。
3)信号集操作函数

#include <signal.h>
int sigemptyset(sigset_t *set);//清空并初始化,使其中所有信号的对应bit为0
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set,int signo);//添加信号
int sigdelest(sigset_t *set,int signo);//删除信号
int sigismember(const sigset_t *set,int signo);//判断指定信号集是否在目标集合中

//两个重要的函数
int sigprocmask(int how,const sigset_t *set,sigset_t *oset)
如果set为非空指针,则指定how进行什么动作,参数how有三个可选值,可以对信号进行阻塞操作

int sigpending(sigset_t *set);
获取未决信号集

三、信号的捕捉

1.捕捉信号
如果信号的处理动作是用户自定义函数,那么在信号递达时时就调用这个函数,这称为捕捉信号。
这里写图片描述
注:
1)main和sighandler属于两个独立的控制流程。
2)为什么内核要将自定义函数交给用户模式执行呢?
因为操作系统不相信任何人,外部函数有可能含有危险代码,影响操作系统的稳定性,所以将一些涉及用户自定义函数,在用户模式下运行,这也就很容易理解了子进程的另一个作用。
2.sigaction
查询或设置信号处理方式

int sigaction(int signo,const struct sigaction *act ,struct sigaction *oact);
//结构体
struct sigaction {
    void (*sa_handler)(int);
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_restorer)(void);
};
sa_handler此参数和signal()的参数handler相同,代表新的信号处理函数。
sa_mask 用来设置在处理该信号时暂时将sa_mask 指定的信号集搁置。
sa_flags 用来设置信号处理的其他相关操作,下列的数值可用。
//SA_RESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL
//SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用
//SA_NODEFER :在该信号处理函数运行时,内核将不会阻塞该信号

3.pause
pause函数使调用进程挂起直到有信号递达。为了不浪费宝贵的CPU资源,先去执行其他进程,当信号递达时,CPU再去处理它。

int pause(void);
//信号处理动作是终止进程,则pause没有机会返回。
//如果是忽略,则继续处于挂起状态,pause不返回。
//如果是捕捉,则调用自定义函数后,返回-1,error设置为EINTR,被信号中断
所以pause只有出错的返回值。

4.实现sleep(5)函数,利用alarm和pause实现。

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

void sig_alrm(int signo)
{
     //do nothing,虽然自定义处理函数什么也不做,但是如果你不指定,alarm函数的默认
     //处理动作是终止当前进程,当前这个进程直接就挂掉了。
}

unsigned int mysleep(unsigned int seconds)
{
     struct sigaction new,old;
     unsigned int unslept=0;
     new.sa_handler=sig_alrm;
     sigemptyset(&new.sa_mask);
     new.sa_flags=0;
     sigaction(SIGALRM,&new,&old);  
     //注册信号处理函数,第二个参数代表修改该信号的处理动作,第三个代表原来信号的处理动作。
     alarm(seconds);  //设置闹钟
     pause();  //挂起当前进程进行等待,直到有信号递达。
     unslept=alarm(0);  //关闭闹钟,返回0。当挂起提起结束时,返回剩下的时间。
     sigaction(SIGALRM,&old,NULL);  //恢复默认信号处理动作
     return unslept;
 }

int main()
{
     while(1){
         int k=mysleep(5);
         printf("5 seconds passed\n");
         printf("%d\n",k);
     }
     return 0;
}

注意事项
1)在mysleep函数返回前应该恢复默认信号处理动作,因为调度函数是不可以影响别人的。
2)缺陷:在设置闹钟alarm的时候,由于可能发生异步操作(有更高的优先级进程),系统先去执行了其他进程,没有先去执行pause。所以有可能alarm已经过去五秒就结束了然后信号递达,可它此时才挂起等待,所以永远等不到信号。要解决这个问题,就可以先屏蔽这个信号,在解除屏蔽的那一瞬间,立刻去执行pause,前提是,这个屏蔽信号与解除信号操作必须合并为一个原子操作,下文有相关函数可以解决这个问题。

四、有关知识点

1.可重入函数
  1)如果一个函数只访问自己的局部变量或参数,就称为可重入函数。简单来说就是可以被中断的函数。
  2)不可重入函数因为使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的。
2.不可重入实例
  在信号的捕捉中,假设有一个主控制流程正在调用一个链表插入结点函数,当代码执行到了一半,刚把新节点N1插入,还没有连接,结果受到中断返回内核态,内核处理异常,处理后准备返回,先去处理当前进程可以处理的递达信号,返回用户态去执行自定义函数,自定义函数又调用了这个插入函数将N2插入,执行完毕后,又调用systemreturn返回内核态执行,最后返回用户区。结果最后只有N1插入了。这种函数就称为不可重入函数。
3.分析
  造成这种现象,是因为这个插入函数是访问的一个全局链表,因为重入而造成的错乱。根本原因在于,这个插入函数代码不是一个原子操作,它是分成两步来执行。可就算代码是一行,它也不是原子操作,读者可以自行去查看给一个整型变量a的反汇编过程,如int a=10;它还是分两步完成。往后我们就可以去了解怎样去实现原子操作,从而避免不可重入函数错乱。
4.不可重入的条件
1)调用了malloc和free函数。使用全局链表来管理的。
2)调用了标准的I/O库函数。使用了全局数据结构。
5.注意事项
1)编写可重入函数时,若使用全局变量,则应通过关中断、信号量(即P、V操作)等手段对其加以保护。
2)若对所使用的全局变量不加以保护,则此函数就不具有可重入性,即当多个进程调用此函数时,很有可能使有关全局变量变为不可知状态。

五、竞态条件与SIGCHLD信号

1.竞态条件
因为时序问题而导致错误,叫做竞态条件。比如在执行alarm(seconds)时,虽然紧接着下一行代码是挂起等待函数pause,但是系统不一定就先执行它,这就会引发时序问题。所以在对时序严格要求的情况下,可以调用sigsuspend而不是pause。
2.SIGCHLD
1)由来
在子进程终止时,其实会给父进程发送一个SIGCHLD信号,该信号的默认处理动作是忽略。
2)僵尸进程
为了解决僵尸进程,我们提供了wait和waitpid函数,父进程可以阻塞等待子进程结束,但这样父进程就没法工作了;还可以采用轮询的方式,就是父进程一边干活,一边询问子进程的状态,这种程序实现复杂。
3)更好的解决僵尸问题
利用好这个SIGCHLD信号,自定义函数操作,里面可以执行wait函数操作,这样父进程就可以安心工作,不必关心子进程了。当子进程退出时,收到SIGCHLD信号,自动捕捉信号。

哪里有错误,欢迎指出一起讨论学习交流。

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

进程信号 的相关文章

  • QML入门----C++与QML交互快速应用

    文章目录 前言一 Qt中有关QML的C 43 43 类1 QQmlEngine2 QQmlContext3 QQmlComponent4 QQmlExpresssssion 二 其他1 使用C 43 43 属性 xff08 Q PROPER
  • QML错误:Component is not ready

    一 原因 终极原因 xff1a 组件没有构建好 xff0c 有可能是加载的QML路径不对 xff0c 或者是QML代码错误 xff0c 或者是QML组件还没有加载完 二 解决办法 打印详细错误 QQmlEngine engine span
  • QT 打开程序闪烁cmd窗口

    包含多种原因 xff0c 我的原因是Pro文件多写了一些其他的 xff0c 删除了下面这句OK了 DISTFILES span class token operator 43 span span class token operator 6
  • QT UTC(T和Z格式)时间转换为北京时间

    一 UTC 协调世界时 xff0c 又称世界统一时间 世界标准时间 国际协调时间 由于英文 xff08 CUT xff09 和法文 xff08 TUC xff09 的缩写不同 xff0c 作为妥协 xff0c 简称UTC 和北京时间相差八小
  • QT 文件操作大全

    文章目录 常用文件模式一 创建文件二 读文件三 写文件四 清空文件夹五 计算文件夹个数六 计算文件夹总大小七 转换大小为B KB M G八 批量修改文件名 常用文件模式 模式含义QIODevice ReadOnly只读方式QIODevice
  • QT QScrollArea 滑动到指定item位置

    一 QT自带的api QListWidget QTableWidget QTreeWidget都有自带的api可以调用 xff0c 如下示例 但是当自定义一个QScrollArea区域 xff0c 布局中插入多个item时 xff0c 就需
  • 马克飞象常用操作(markdown )

  • QT 移入控件展示卡片

    功能 xff1a 移入widget显示卡片 xff0c 并且可以进入卡片不消失 xff08 widget与卡片距离离得很近 xff09 xff0c 移出卡片才离开 span class token keyword bool span spa
  • 树莓派pico入门第一站:让主板上的小灯闪起来。(附代码)

    首先配置你的树莓派pico xff0c 把它插在你的电脑上 xff0c 你的电脑会多出来一个 U盘 xff0c 把这个文件复制 xff0c 粘贴 到你的树莓派pico里面 xff0c 你多出来的 U盘 会自动 消失 xff0c 这时候 xf
  • QT 网格布局插入固定列数的item

    一 场景 在网格布局插入固定列数的item xff0c 比如三列item xff0c 根据item的总数计算 span class token macro property span class token directive hash s
  • QT QMetaEnum枚举与字符串互转

    一 示例 span class token macro property span class token directive hash span span class token directive keyword include spa
  • QT 抓取widget转换为图片

    QString folder span class token operator 61 span span class token class name QStandardPaths span span class token operat
  • window11 安装linux子系统(一键安装)并连接到vs code

    文章目录 一 window 使用linux环境的几种方式二 安装wsl1 进入这个目录下 xff0c 将cmd exe已管理员身份运行2 命令行输入以下命令 xff0c 然后重启计算机3 再次已管理员身份打开 xff0c 执行命令 xff0
  • QT 利用URL Protocol实现网页调起本地程序

    一 QT 安装时脚本注入注册表或者自己添加 span class token comment 依次为目录 键 值 xff0c 34 URL Protocol 34 这个键必须有 span WriteRegStr HKCR span clas
  • PC 配置jenkins自动打包

    文章目录 一 下载jenkins运行环境二 下载jenkins三 安装 qt 5 12 2 和 VS 2017四 安装git并配置gitlab五 jenkins配置git 一 下载jenkins运行环境 java jdk 11 镜像下载地址
  • 心系Flyme

    我来自陕西省神木县 xff0c 大学我考入了陕西科技大学 xff0c 成为了一名信息与计算科学专业的学生 xff0c 希望在以后的道路中 xff0c 通过我自己的努力 xff0c 提升自己的价值 在大二大三学习编程 xff0c 希望自己可以
  • C语言的编译链接过程

    编写的一个C程序 xff08 源程序 xff09 xff0c 转换成可以在硬件上运行的程序 xff08 可执行程序 xff09 xff0c 需要进行翻译环境和运行环境 翻译环境则包括两大过程编译和链接 xff0c 经过编译和链接过程便可形成

随机推荐

  • 函数的调用过程(栈帧的创建和销毁)

    为了更好地认识函数的调用过程 xff0c 我们可以用反汇编代码去理解学习 一 基本概念 1 栈帧 xff08 过程活动记录 xff09 xff1a 是编译器用来实现函数调用的一种数据结构 xff0c 每个栈帧对应一个未运行完的函数 xff0
  • 树莓派pico刚买来怎么用?

    第一次使用 xff0c 首先按住主板上的白色按钮 xff0c 然后另一只手把数据线插在主板上 xff0c 直到你的电脑提示有新设备输入 xff0c 提示可以是声音 xff0c 可以是设备管理器多了一个U盘 要想得到提示 xff0c 你要打开
  • C语言动态顺序表

    顺序表是将表中的节点依次存放在计算机内存中一组地址连续的存储单元中 xff0c 表可以动态增长 xff0c 尾插元素 xff0c 尾删元素 xff0c 头插元素 xff0c 头删元素 xff0c 打印元素 xff0c 查找元素 xff0c
  • C语言笔记1

    假定程序运行环境为VC6 0 xff0c 缺省为四字节对齐 xff0c CPU xff08 32小字节序处理器 xff09 1 char x 61 34 ab0defg 34 char y 61 39 a 39 39 b 39 39 0 3
  • 【C++三大特性】继承

    如有疑问 xff0c 欢迎讨论 xff0c QQ xff1a 1140004920 一 继承的概念 1 原有的类为基类 xff0c 又称父类 xff0c 对基类进行扩展产生的新类称为派生类 xff0c 又称子类 xff0c 继承可以使代码复
  • C++实现顺序表及双向链表

    顺序表 include lt iostream gt include lt assert h gt using namespace std typedef int DataType class SeqList public 默认的构造函数
  • 二叉树

    一 二叉树 是结点的一个有限集合 xff0c 每个根结点最多只有两颗子树 xff0c 二叉树有左右之分 xff0c 子树的次序不能颠倒 二 二叉树的种类 1 满二叉树 xff1a 每个结点都有左右子树 xff0c 且叶结点都在同一层 2 完
  • 进程间通信----管道、消息队列、共享内存、信号量

    一 进程间通信 xff08 Inter Process Communication xff09 1 目的 1 数据传输 2 资源共享 3 通知事件 4 进程控制 注 xff1a 每个进程都有各自不同的用户地址空间 xff0c 进程之间要交换
  • 进程基本概念、进程地址空间

    强调内容今天来谈一谈进程的一些基本概念 xff0c 认识一些进程状态 xff0c 重新认识一下程序地址空间 xff08 进程地址空间 xff09 xff0c 进程调度算法 xff0c 环境变量等属性 一 进程 1 什么是进程 xff1f 程
  • 何为缓存?

    一 缓存 xff08 cache xff09 1 概念 xff1a 数据交换的缓冲区 xff08 称作Cache xff09 缓存是一块内存芯片 xff0c 具有极快的存取速率 xff0c 它是硬盘内部存储和外界接口之间的缓冲器 xff0c
  • 计算机的组成

    一 冯诺依曼系统 1 计算机硬件 由运算器 控制器 存储器 输入设备 输出设备组成 2 计算机内部采用二进制表示指令和数据 3 注 xff1a 1 输入设备 xff1a 键盘和鼠标等 2 输出设备 xff1a 显示屏 xff0c 打印机等
  • fd与FILE以及fork缓冲问题

    一 文件描述符 fd 1 文件描述符其实就是一个非负的小整数 是文件指针数组的下标 2 让我们看一看0 xff0c 1 xff0c 2 xff0c 代表什么 xff1f span class hljs preprocessor includ
  • Kali Linux使用体验简述

    在以前的版本里Kali Linux默认用户是root用户 xff0c 这样设计的目的是避免每次都要输入root密码 xff0c 而如今需要root密码的程序明显少于从前 xff0c Kali Linux也做出了相应的改革 xff0c 默认用
  • 随身WiFi410的板子刷Debian安装青龙面板+狗东脚本最详细教程

    前几天 xff0c 我发布了一个410刷入debian的教程 很多老哥可能觉得刷入debian没有什么用 xff0c 今天我就教大家如何安装青龙面板 xff0c 并且安装脚本实现自动白嫖狗东的豆子 青龙面板 43 狗东脚本 自动领豆子红包
  • inode以及软硬链接

    一 inode 使用ls l查看文件元数据 xff0c 用来描述数据属性 模式 硬链接数 文件所有组 组 大小 最后修改时间 文件名 使用stat查看 xff0c 查看文件信息 span class hljs comment Access
  • 静态库与动态库

    一 库 由于版权原因 xff0c 库函数的源代码一般是不可见的 xff0c 但在暴露的头文件中你可以看到它对外的接口 库函数简介 xff0c 使用的时候 xff0c 直接引入头文件 include lt gt 即可 二 静态库 1 概念 程
  • 【进程控制上】创建、终止、等待、程序替换

    进程的创建 终止 等待 程序替换 以及popen system与fork之间的区别 一 进程的创建 init进程将系统启动后 xff0c init将成为此后所有进程的祖先 xff0c 此后的进程都是直接或间接从init进程 复制 而来 完成
  • 【进程控制下】实现一个简易的shell

    1 shell原理 运用程序替换的原理来实现的 xff0c shell自己就是一个进程 span class hljs number 1 span 获取命令行 span class hljs number 2 span 解析命令行 span
  • VIM的基本使用

    一 VIM 1 概念 是一款文本编辑器 xff0c 和Emacs并列成为类Unix系统用户最喜欢的文本编辑器 2 优点 可以完成复杂的编辑与格式化功能 3 模式 其模式共有十二种 xff0c 基本模式有六种 span class hljs
  • 进程信号

    一 信号概念 1 一个信号产生及处理实例 1 在shell下 xff0c 启动一个进程 2 按下Ctrl 43 c xff0c 键盘输入产生一个硬件中断 3 如果CPU正在运行这个进程则代码暂停执行 xff0c CPU从用户态返回到内核态