linux---进程信号

2023-05-16

  • 进程的功能以及概念
  • 信号的生命周期以及相关的接口
  • 自定义信号的捕捉流程
  • 信号集以及阻塞信号集
  • 了解一个SIGCHLD信号
  1. 信号的功能以及概念
    信号的功能:信号就是通知我们某一个事件的发生
    信号的概念:信号就是一个软件中断,在一个合适的时机打断我们当前的操作去处理我们信号的。
    查询我们进程信号的命令:kill -l
    在这里插入图片描述得到我们一共62种信号,其中1-31号信号是非可靠的信号,就是信号容易丢失。也称为非实时信号,34-64号信号是可靠信号,就是信号不会丢失,也称为实时信号。
  2. 信号的生命周期:从信号的产生-信号在进程中的注册-信号在进程中的注销-信号的处理。
  • 信号的产生
    信号产生可以从两个方面来说,一个是硬件产生信号,一个是软件产生信号
    1. 硬件产生信号: ctrl+c ctrl+\ ctrl+z,其实对应的就是产生的2号信号SIGINT、3号信号SIGQUIT和10号信号SIGTSTP。
    2. 软件产生信号
  • kill命令产生信号
    #include <sys/types.h>
   #include <signal.h>
   int kill(pid_t pid, int sig)

就是给进程id为pid的进程发送我们的sig信号

  • raise产生信号
       #include <signal.h>
       int raise(int sig);

给自身进程发送一个sig信号

  • abort产生信号
       #include <stdlib.h>
       void abort(void);

给自身发送一个SIGABRT信号

  • alarm定时器
       #include <unistd.h>
       unsigned int alarm(unsigned int seconds);

在seconds的时间后给进程发送一个SIGALRM信号,其实就是一个定时器,当参数seconds为0的时候就是取消上一个定时器,不为0的时候如果是第一个定时的话就实现为闹钟,不是就替代前一个闹钟。返回值第一次返回0,不是第一次就返回上一个定时器剩余的时间数。

  • 信号的注册
    其实我们是用128位的位图来对信号进行标记的,其实是一个结构体sigset_t,我们使用链表qigqueue来接收我们的信号,信号到来的时候会组织一个对应信号的节点添加到队列中,对于可靠信号和非可靠信号我们有着不同的处理方式,对于非可靠信号,到信号到来的时候我们先判断我们的位图是否已经被标记,如果已经被标记了,什么都不用做,如果没有标记我们将信号添加到我们的队列中,并且标记我们的位图此信号已经被添加进来了。对于我们可靠信号,当信号来临的时候我们检查位图是否被标记,如果没有将对饮的位置进行标记,如果已经被标记了就不用管理位图,对于两种情况都需要添加节点到我们的队列中。(其实修改位图就是修改我们的味觉信号集)。
    味觉信号:注册了但是没有被处理的信号
    未决:是一个信号从产生到信号处理之间的过程
    对应的还有一个阻塞信号集(等会儿讲解)

  • 注销信号
    注销信号其实是我们信号注册的相对,对于我们的可靠信号来说,注销信号是寻找我们链表上是否含有要被删除的节点,如果没有什么都不做,如果有的话我们就删除该节点,并且遍历我们的链表判断是否在链表中还含有我们要被删除的节点吗,如果有就直接退出,如果没有还要修改对应的位图。对于我们非可靠的信号就相对简单一点,如果有的话就删除节点并且修改位图,没有的话就什么都不做。

  • 信号的处理

  1. signal
       #include <signal.h>
       typedef void (*sighandler_t)(int);
       sighandler_t signal(int signum, sighandler_t handler);

signum:信号编号
handler:函数指针,用于我们信号处理函数。

  1. sigaction
       #include <signal.h>
       int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);

signum:是信号编号
act:是一个结构体

           struct sigaction {
               void     (*sa_handler)(int);//函数指针
               void     (*sa_sigaction)(int, siginfo_t *, void *);//带参数的函数指针
               sigset_t   sa_mask;//l临时阻塞信号集
               int        sa_flags;//选择方式,一般为0
               void     (*sa_restorer)(void);
           };

act就是我们实现的信号处理,old就是用来接收以前此信号的处理方式。old一般用来恢复我们的原来的操作。
例子:捕捉我们定时器产生的信号

  1 #include <stdio.h>                                                          
  2 #include <unistd.h>
  3 #include <signal.h>
  4 #include <sys/types.h>
  5 #include <sys/time.h>
  6 //捕捉函数
  7 void catch_sig(int num){
  8     printf("%d信号已经被捕捉了\n",num);
  9 }
 10 int main(){
 11     //使用sigaction信号捕捉信号
 12     //定义一个act
 13     struct sigaction act;
 14     act.sa_flags = 0;
 15     act.sa_handler = catch_sig;
 16     sigemptyset(&act.sa_mask);
 17     sigaction(SIGALRM,&act,NULL);
 18     //setitimer产生周期信号
 19     struct itimerval myit = {{3,0},{5,0}};//第一次5s,再之后进行3s一次
 20     setitimer(ITIMER_REAL,&myit,NULL);
 21     while(1){
 22         printf("who can kill me!\n");
 23         sleep(1);
 24     }
 25 
 26     return 0;
 27 }

  • 自定义信号的处理流程
    当我们的main函数中出现了系统调用,中断或者异常的时候就会从用户态切换到内核,完成内核功能返回用户态的时候就会判断是否有信号待处理(此时调用一个do_signal),如果有并且这个信号的处理方式是自定义的话就返回用户态执行信号处理函数,处理完成之后执行sig_return返回内核中,并且再次检测我们内核中是否还有待处理的信号,如果没有的话就返回main函数,如果还有就继续执行处理我们的信号。
    在这里插入图片描述

  • 信号集与阻塞信号集
    信号集就是利用我们的位图对我们的信号进行标记,实际中使用一个sigset_t结构体中有一个数组long int_val[16];来表示,
    阻塞信号集:就是用来表示我们阻止我们信号被递达。
    在这里插入图片描述其实含有三个我们信号集相关的数组,也就是我们的未决信号集和阻塞信号集和我们信号处理函数的集合。信号处理函数的集合是存储的我们信号处理函数的函数指针。
    在我们信号到来的时候我们先去判断我们的阻塞信号集是否是阻塞的,如果已经是阻塞的话我们将不再进行操作了,没有没有被阻塞的话我们将在未决信号集中寻找我们的信号并且可以在处理函数的集合中寻找相对应的处理函数。

      	注意:9号和19号信号是不能被阻塞的。
    

信号集处理接口:

		1.sigprocmask设置我们的阻塞集
       #include <signal.h>
       int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

how:
a.SIG_BLOCK,让set中的集合加入都加入block集合中
b.SIG_UNBLOCK,让set中的集合从block集合中解除
c.SIG_SETMASK,将block集合设置为set
old:用来接收原来的阻塞集合(一般用来恢复我们的原有状态)

		2.获取未决信号集
       #include <signal.h>
       int sigpending(sigset_t *set);

set:传出参数,用来获取我们的味觉信号集

		3. 添加、删除、判断一个信号是否在一个集合中
       #include <signal.h>
       int sigemptyset(sigset_t *set);
       //清空set集合,将所有信号移除阻塞信号集
       
       int sigfillset(sigset_t *set);
       //将set集合全部置为1,也就是将所有的信号添加为阻塞信号集
       
       int sigaddset(sigset_t *set, int signum);
       //将某一个信号添加到我们的阻塞信号集中
       
       int sigdelset(sigset_t *set, int signum);
       //将某一个信号从阻塞信号集中删除
       
       int sigismember(const sigset_t *set, int signum);
	   //判断某一个信号是否在阻塞信号集中

例子:打印我们的信号集,并且在我们硬件产生信号后我们的信号集对应改变

  1 #include <stdio.h>                                                                                                               
  2 #include <unistd.h>
  3 #include <signal.h>
  4 
  5 int main(){
  6 
  7     sigset_t pen,sigproc;
  8     //设置一个阻塞信号
  9     sigemptyset(&sigproc);
 10     sigaddset(&sigproc,SIGINT);//按键信号ctrl c;    
 11     //设置阻塞信号集
 12     sigprocmask(SIG_BLOCK,&sigproc,NULL);
 13 
 14     while(1){
 15         sigpending(&pen);
 16         int i = 1;
 17         for(i = 1;i < 32;i++){
 18             if(sigismember(&pen,i)){//判断信号是不是在信号集中
 19                 printf("1");
 20             }
 21             else{
 22                 printf("0");
 23             }
 24         }
 25     printf("\n");
 26     sleep(1);
 27     }
 28     return 0;
 29 }  
  • SIGCHLD信号
    SIGCHLD信号是子进程退出信号
    在我们的进程章节我们使用wait和waitpid来等待我们的子进程退出,虽然我们能够对子进程进行回收,但是我们的父进程需要一直的等待我们子进程的退出,虽然我们也可以使用waitpid设置非阻塞等待,但是我们知道我们子进程在退出的时候都会发送一个SIGCHLD信号,我们就可以使用此信号对子进程进行回收,由于我们SIGCHLD信号的默认的处理方式是忽略,所以我们在以前此信号到达之后就被忽略了。我们需要自定义去处理我们的SIGCHLD信号。但是我们处理的时候需要注意,我们的SIGCHLD是非可靠信号,当我们同时有多个子进程退出的时候在信号处理函数中调用wait回收函数的时候只能回收一个,所以我们需要一定的逻辑处理,使用while循环进行处理即可。
    例子:回收10个子进程
  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <signal.h>
  4 #include <sys/wait.h>
  5 void catch_sig(int num){//每次只能进行一个回收。当多次执行的时候我们不能全部回收
  6 
  7     printf("现在回收退出了\n");
  8     pid_t pid ;
  9     while((pid = waitpid(-1,NULL,WNOHANG))>0);
 10     if(pid>0){
 11         printf("wait child%dok",pid);
 12     }
 13     printf("子进程回收完毕\n");
 14 }
 15 int main(){
 16     int i = 0;
 17     pid_t pid;
 18     for(i = 0;i<10;i++){
 19         pid = fork();
 20         if(pid == 0){
 21             break;
 22         }
 23     }
 24     if(i == 10){
 25         //父进程,注册捕捉函数
 26         struct sigaction act;
 27         act.sa_flags = 0;
 28         sigemptyset(&act.sa_mask);
 29         act.sa_handler = catch_sig;
 30         sigaction(SIGCHLD,&act,NULL);
 31         while(1){
 32             sleep(1);
 33         }
 34     }else if(i<10){
 35         printf("i am %d child,pid = %d\n",i,getpid());
 36         sleep(1);//此时不睡觉的话会产生最后一个不会被回收,会产生僵尸进程
 37     }
 38     return 0;
 39 }
 40      

看我们的程序,在执行过后出现结果:
在这里插入图片描述
你也许会产生疑问,为什么10个子进程没有回收10次呢,这是因为我们有点进程在同一个时间同时被回收了,你可以尝试将信号处理函数中的while循环后面的分号去掉,会出现如下的结果
在这里插入图片描述

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

linux---进程信号 的相关文章

  • 如何在特定时间后从“std::cin”读取超时

    我写了一个小程序 int main int argc char argv int n std cout lt lt Before reading from cin lt lt std endl Below reading from cin
  • 如何从纯文本中查找键值存储中的值

    给定一个纯文本文件 其中包含 FOO foo BAR bar BAZ baz 我们如何grep使用键获取值 Use a 向后看 https regex101 com r q3FNpe 1 grep Po lt FOO w file foo
  • Linux C 如何打开目录并获取文件描述符

    include
  • Nvcc 的版本与 CUDA 不同

    我安装了 cuda 7 但是当我点击 nvcc version 时 它打印出 6 5 我想在 GTX 960 卡上安装 Theano 库 但它需要 nvcc 7 0 我尝试重新安装cuda 但它没有更新nvcc 当我运行 apt get i
  • 执行 grails/groovy 时,Linux 上没有可用的控制台输出

    当执行 groovy 脚本或 grails 应用程序时 没有可用的输出 输入 gt 只有一个清晰的控制台屏幕 即使不启动 X Window System 输出也是不可见的 我也尝试过 grailscompile plain output 也
  • 为什么链接器要修改--defsym“绝对地址”

    目标 一个使用可执行文件中的函数 不导出符号 的共享库 Means gcc Wl defsym function 0x432238 手册页指出 defsym symbol expression Create a global symbol
  • 调试严重的 SIGILL 崩溃:文本段损坏

    我们的系统是基于 PowerPC 的运行 Linux 的嵌入式系统 我们遇到了随机的 SIGILL 崩溃 这种情况在各种应用程序中都会出现 崩溃的根本原因是将要执行的指令归零 这表明内存中的文本段已损坏 由于文本段是以只读方式加载的 因此应
  • 删除损坏的链接 Unix

    我想删除损坏的链接 但在此之前我想确认目录中是否存在链接文件 我们来调用一下链接A if a A then print ya A is ther fi But if A链接已损坏 那么我该如何检查 find L type l找到损坏的符号链
  • 如何在生产环境中运行 spring boot 可执行 jar?

    Spring Boot 的首选部署方法是通过内部包含 tomcat 的可执行 jar 文件 它是从一个简单的开始java jar myapp jar 现在 我想将该 jar 部署到 EC2 上的 Linux 服务器上 我是否遗漏了某些内容
  • 来自外部 .diff 文件的交互式补丁

    Linux 是否有命令或程序允许交互式地修补源代码 在屏幕上打印每个块并在将其应用到文件之前等待确认 就像是git add p 但是从另一个 diff 文件中进行更改 您始终可以在 shell ruby python 中编写脚本 逐行读取该
  • find 命令的“exec”功能可以在后台启动程序吗?

    我想做这样的事情 find iname Advanced Linux Program exec kpdf 可能的 还有其他类似的方法吗 首先 它不会像你输入的那样工作 因为 shell 会将其解释为 find iname Advanced
  • bcrypt_lib.node:未定义符号:node_module_register

    Error home george Desktop myProject node modules bcrypt build Release bcrypt lib node undefined symbol node module regis
  • 如何重新安装最新的cmake版本?

    我想在Linux环境下安装cmake的最新版本 我安装了 cmake 3 5 版 但某些应用程序不支持 我尝试通过卸载当前版本来升级它 但是当我使用 sudo apt get install cmake 重新安装时 我重新安装了相同的版本
  • linux命令行:如何简单地将任意字符串输入管道?

    我正在寻找一个非常简单的技巧 假设文件中error txt 有一个很长的字符串 errorMessage key1 some message key2 message 2 由于它的格式不适合读者 如果我想在终端中以格式良好的形式查看它 我可
  • 让子进程等待直到收到父进程的信号

    我想从父级创建 N 个子级 我希望所有的孩子同时开始 一个功能 测量时间 因此 我将该函数放入信号处理程序中 当父级完成创建 分叉 所有子级时 它会向所有子级发送信号 使用kill children id 以让 make 开始 代码如下 但
  • ModemManager:mmcli 获取信号强度

    我无法检索 Sierre Wireless MC7304 上的信号强度 另外通过 mmcli 发送 AT 命令似乎不起作用 sudo mmcli m org freedesktop ModemManager1 Modem 0 signal
  • Android Studio无法更新

    我正在运行 Mint 14 Linux 并且在使用 Android Studio 时遇到了一个主要问题 运行更新功能时 所有软件包都会返回 访问被拒绝 状态 我怎样才能解决这个问题 如何授予下载和安装的权限 附加信息 studio sh 正
  • 检查 VT-x 是否已激活而无需在 Linux 中重新启动?

    我有一台配备 Intel Core i5 M 450 2 40GHz 的笔记本电脑 显然有 VT x 但没有 VT d 我有 Ubuntu 12 04 32 位 但希望在其上运行基于虚拟 64 位终端的 Linux 我如何知道 BIOS 是
  • 如何有效地将许多文件移动到新服务器?

    我正在更换托管提供商 需要将数百万个上传的文件传输到新服务器 所有文件都位于同一目录中 是的 你读对了 过去我这样做过 压缩源服务器中的所有文件 scp到新服务器的 zip Unzip Move directory to appropria
  • pthread_create 编译返回错误

    我使用以下代码创建两个线程 header files include

随机推荐

  • 中断与回调

    1 xff0c 回调函数 回调函数的原理是使用函数指针实现类似 软中断 的概念 比如在上层的两个函数A和B xff0c 把自己的函数指针传给了C xff0c C通过调用A和B的函数指针达到 当做了什么 xff0c 通知上层来调用A或者B 的
  • CUDA 程序的优化(3) 任务划分

    4 3 1任务划分原则 首先 xff0c 需要将要处理的任务划分为几个连续的步骤 xff0c 并将其划分为CPU端程序和GPU端程序 划分时需要考虑的原则有 列出每个步骤的所有可以选择的算法 xff0c 并比较不同算法在效率和计算复杂度上的
  • C++ Matlab混合编程时“函数或变量 ‘matlabrc‘ 无法识别”

    在QT中调用Matlab初始化时 xff0c 出现了 函数或变量 matlabrc 无法识别 的情况 xff0c 接着崩溃 而且比较神奇的是 xff0c 前一次是可以初始化的 xff0c 但运行过程中发生了崩溃 直接搜解决办法 xff0c
  • Notepad++全选一整列的靠谱办法

    遇到行数较少的可以直接按住ALT手动选取 xff0c 但遇到行数较多 xff0c 就得这么干 xff1a 鼠标放在第一行某一列 xff0c 按住Alt 43 Shift xff0c 然后鼠标选择最后一行该列 xff0c 输入内容即可 xff
  • 对象转xml格式工具类

    import com ruiyun gui store haikang haikang bean FCSearchDescription import com ruiyun gui store haikang haikang bean FD
  • 【无标题】MQ静态图片获取

    public void getImageV40 String path Integer buildingProjectId HttpServletResponse response JSONObject param 61 new JSONO
  • 数据加解密时Base64异常:Illegal base64 character 3a

    现象 用base64工具类对中文进行处理时出现异常 xff0c 在数据加解密场景中经常使用 java lang IllegalArgumentException Illegal base64 character 3a at java uti
  • Winsock编程实例---TCP&UDP

    0x1 基于TCP的通信 1 服务端 1 1 创建基本流程 创建一个TCP服务端的程序需要调用的函数流程 xff1a 初始化函数库 gt gt WSAStartup 创建套接字 gt gt socket 绑定套接字 gt gt bind 监
  • 数据结构---选择排序(直接选择排序和堆排序图解)

    选择排序思想 xff1a 每一次从待排序的数据元素中选出最小 xff08 或最大 xff09 的一个元素 xff0c 存放在序列的起始位置 xff0c 直到全部待排序的 数据元素排完 直接选择排序 在元素集合array i array n
  • Java HttpUtils类

    Java HttpUtils类 Java HttpUtils类 定义 Public class HttpUtils 收集HTTP Servlet使用的静态的有效方法 方法 1 getRequestURL public static Stri
  • Ubuntu打造家用NAS三——网盘与影视中心

    Ubuntu打造家用NAS三 网盘与影视中心 一 Ubuntu 挂载硬盘 通过 Putty 连接 NAS查看硬盘位置 xff1a sudo fdisk l找到需要挂载的硬盘 xff0c 我的是 Disk dev sdb xff1a xxx
  • Ardupilot笔记:Rover auto模式下的执行流程

    先从mode auto cpp的update 开始分析 流程如图 xff1a 进入函数update 后会执行函数navigate to waypoint mode auto cpp span class token keyword void
  • 串口通信协议 UART+I2C+SPI

    UART 异步 串行 全双工 I2C SPI 不同通信协议比较 UART UART协议详解 UART通信 xff0c 接收与发送 xff08 详细版 xff0c 附代码 xff09 UART串行通信详解 待整理 UART是Universal
  • c语言---宏

    宏 1 仅仅替换 2 不能定义宏参类型 3 不会检查宏参有没有定义 定义带参数的宏 define JH a b t t 61 a a 61 b b 61 t xff0c 对两个参数a b的值进行交换 xff0c 下列表述中正确的是 xff0
  • Ros安装rosdep update出错,解决办法(从根本入手)

    博主作为一个ros刚入门的新手 xff0c 之前也安装过ros ros2但是在Ros安装在进行rosdep update 时运气与网络是成功的关键 xff0c 在尝试了好多次 xff0c 运气好一次就成功了 xff0c 运气不好得不停的试错
  • vscode使用方法

    01 ctrl 43 u 返回上一个光标焦点 02 发送请求插件 到VSCode插件中搜索REST Client 搜索到 xff0c 点击install进行安装 创建一个 http文件 编写测试接口文件 右键选择 发送请求 xff0c 测试
  • 自主飞行无人机开发--SALM cartographer开源框架 rplidar A2/3

    参考学习网站 xff1a https google cartographer ros readthedocs io en latest 问题提出 xff1a 四旋翼搭载激光雷达A3进行SLAM室内定位 xff0c 其怎样Running Ca
  • C#串口通信中的奇偶性校验、CRC校验函数

    一般来说 xff0c 通信协议中的通用数据格式是 包头 43 指令码 43 数据长度 43 有效数据 43 校验码 43 包尾 其中 xff0c 校验方式有多种 xff0c 最流行的是CRC校验方式 xff0c 其次是简单的奇偶性校验 校验
  • 测试软件安装步骤

    目录 Ja 目录 Java安装 jdk下载 jdk环境配置 phpstudy安装 禅道的安装 xampp安装 postman安装 requests parameterized jmeter安装 JVM监控 Locust SecureCRT软
  • linux---进程信号

    进程的功能以及概念信号的生命周期以及相关的接口自定义信号的捕捉流程信号集以及阻塞信号集了解一个SIGCHLD信号 信号的功能以及概念 信号的功能 xff1a 信号就是通知我们某一个事件的发生 信号的概念 xff1a 信号就是一个软件中断 x