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---进程信号 的相关文章

  • Linux C++ 错误:未定义对“dlopen”的引用

    我在 Linux 上使用 C Eclipse 工作 并且想要使用一个库 Eclipse 向我显示一个错误 undefined reference to dlopen 你知道解决办法吗 这是我的代码 include
  • 如何在 Vim 中突出显示 Bash 脚本?

    我的 Vim 编辑器自动突出显示 PHP 文件 vim file php HTML 文件 vim file html 等等 但是当我输入 vim file在里面写一个Bash脚本 它不会突出显示它 我如何告诉 Vim 将其突出显示为 Bas
  • 如何通过ssh获取远程命令的退出代码

    我正在通过 ssh 从远程计算机运行脚本 ssh some cmd my script 现在 我想在本地计算机上存储 shell 脚本的退出状态 我该怎么做 假设没有任何问题ssh其本身 其退出状态是在远程主机上执行的最后一个命令的退出状态
  • 应用程序中两个不同版本的库

    考虑一个场景 其中有两个不同版本的共享库 考虑 A 1 so 链接到 B so A 2 so 链接到 C so 现在 B so 和 C so 都链接到 d exe 当 B so 想要调用 A 1 so 中的函数时 它最终会调用 A 2 so
  • 我想在 Red Hat Linux 服务器中执行 .ps1 powershell 脚本

    我有一个在窗口中执行的 ps1 powershell 脚本 但我的整个数据都在 Linux 服务器中 有什么可能的方法可以让我在红帽服务器中执行 powershell 脚本 powershell脚本是 Clear Host path D D
  • 如何在Linux上用C/C++编写Web服务器[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我正在考虑在 Linux 平台上开发一个小型 阅读 初级 Web 服务器 但我不知道从哪里开始 我希望它能够做的是 监听特定端口 接受
  • 在 Linux 中重新启动时,新创建的文件变为 0 kb(数据被覆盖为空)

    我遇到了一个奇怪的问题 这让我发疯 当前的任务是在 root 用户第一次登录时启动一组文件 并在同一用户第二次登录时启动另一组文件 我决定使用 profile 和 bashrc 文件 并在第一次登录期间发生的任务结束时重新加载 bashrc
  • MySQL 与 PHP 的连接无法正常工作

    这是我的情况 我正在尝试使用 Apache 服务器上的 PHP 文件连接到 MySQL 数据库 现在 当我从终端运行 PHP 时 我的 PHP 可以连接到 MySQL 数据库 使用 php f file php 但是当我从网页执行它时 它只
  • Docker忽略limits.conf(试图解决“打开文件太多”错误)

    我正在运行一个 Web 服务器 该服务器正在处理数千个并发 Web 套接字连接 为了实现这一点 在 Debian linux 我的基本镜像是 google debian wheezy 在 GCE 上运行 上 打开文件的默认数量设置为 100
  • bash while 循环的布尔表达式中的 -lt 意味着什么?

    我猜测它代表小于基于输出 但是我在哪里可以找到有关此语法的文档 bin bash COUNTER 0 while COUNTER lt 10 do echo The counter is COUNTER let COUNTER COUNTE
  • Ruby:在 Ubuntu 上安装 rmagick

    我正在尝试在 Ubuntu 10 04 上安装 RMagick 看起来here https stackoverflow com questions 1482823 is there an easy way to install rmagic
  • Linux shell 从用户输入中获取设备 ID

    我正在为一个程序编写安装脚本 该程序需要在其配置中使用 lsusb 的设备 ID 因此我正在考虑执行以下操作 usblist lsusb put the list into a array for each line use the arr
  • 在 x86 汇编语言中获取文件大小的简单方法

    假设我已经在汇编中打开了一个文件 并且在寄存器 eax 中有该文件的文件句柄 我将如何获取文件的大小 以便为其分配足够的缓冲区空间 我在这里研究了另一个讨论 建议使用sys fstat 28 系统调用来获取文件统计信息但无法实现它 My a
  • 在 docker 中重定向命令输出

    我想为我的服务器做一些简单的日志记录 它是一个在 Docker 容器中运行的小型 Flask 应用程序 这是 Dockerfile Dockerfile FROM dreen flask MAINTAINER dreen WORKDIR s
  • ioctl 命令的用户权限检查

    我正在实现 char 驱动程序 Linux 并且我的驱动程序中有某些 IOCTL 命令仅需要由 ADMIN 执行 我的问题是如何在 ioctl 命令实现下检查用户权限并限制非特权用户访问 IOCTL 您可以使用bool capable in
  • 批量删除文件名中包含 BASH 中特殊字符的子字符串

    我的目录中有一个文件列表 opencv calib3d so2410 so opencv contrib so2410 so opencv core so2410 so opencv features2d so2410 so opencv
  • 找不到包“gdk-pixbuf-2.0”

    我正在尝试在 Amazon Linux 发行版实例上构建 librsvg 我已经通过 yum 安装了大部分依赖项 其中一些在实例上启用的默认 yum 存储库中不可用 因此必须从头开始构建它们 我已经走了很远 但还停留在最后一点 跑步时sud
  • SSH,运行进程然后忽略输出

    我有一个命令可以使用 SSH 并在 SSH 后运行脚本 该脚本运行一个二进制文件 脚本完成后 我可以输入任意键 本地终端将恢复到正常状态 但是 由于该进程仍在我通过 SSH 连接的计算机中运行 因此任何时候它都会登录到stdout我在本地终
  • 相当于Linux中的导入库

    在 Windows C 中 当您想要链接 DLL 时 您必须提供导入库 但是在 GNU 构建系统中 当您想要链接 so 文件 相当于 dll 时 您就不需要链接 为什么是这样 是否有等效的 Windows 导入库 注意 我不会谈论在 Win
  • FileOutputStream.close() 中的设备 ioctl 不合适

    我有一些代码可以使用以下命令将一些首选项保存到文件中FileOutputStream 这是我已经写了一千遍的标准代码 FileOutputStream out new FileOutputStream file try BufferedOu

随机推荐

  • 中断与回调

    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