Linux进程通信——信号(一)

原理

对于 Linux来说,实际信号是软中断,许多重要的程序都需要处理信号。
信号,为 Linux 提供了一种处理异步事件的方法。比如,终端用户输入了ctrl+c来中断程序,会通过信号机制停止一个程序。

概述

信号的名字和编号

每个信号都有一个名字和编号,这些名字都以“SIG”开头,例如“SIGUP(挂起) ”、“SIGINT(中断)、SIGQUIT(退出)”等等。
信号定义在signal.h头文件中,信号名都定义为正整数
具体的信号名称可以 使用kill -l查看信号的名字以及序号
信号是从1开始编号的,不存在0号信号。kill对于信号0有特殊的应用。

信号的处理

信号的处理方式有三种,分别是忽略、捕捉和默认动作

忽略信号

大多数信号可以使用这个方式来处理,但是有两种信号不能被忽略(分别是 SIGKILLSIGSTOP)。因为他们向内核和超级用户提供了进程终止和停止的可靠方法,如果忽略了,那么这个进程就变成了没人能管理的的进程,显然是内核设计者不希望看到的场景。

系统自带的忽略宏函数

 signal(SIGINT,SIG_IGN);//将SIGINT信号(ctrl+C、2)忽略

捕捉信号

需要告诉内核,用户希望如何处理某一种信号,说白了就是写一个信号处理函数,然后将这个函数告诉内核。当该信号产生时,由内核来调用用户自定义的函数,以此来实现某种信号的处理。( 在main函数外定义一个函数,用signal函数中的参数调用该函数并执行函数中的功能)

系统默认动作

对于每个信号来说,系统都对应由默认的处理动作,当发生了该信号,系统会自动执行。不过,对系统来说,大部分的处理方式都比较粗暴,就是直接杀死该进程。
具体的信号默认动作可以使用man 7 signal来查看系统的具体定义。在此,我就不详细展开了,需要查看的,可以自行查看。也可以参考 《UNIX 环境高级编程(第三部)》的 P251——P256中间对于每个信号有详细的说明。

例子如下:

其实对于常用的 kill 命令就是一个发送信号的工具,kill 9 PID 来杀死进程。比如,我在后台运行了一个 a.out 工具,通过 ps 命令可以查看他的 PID,通过 kill 9来发送了一个终止进程的信号来结束了 a.out 进程。如果查看信号编号和名称,可以发现9对应的是 SIGKILL,正是杀死该进程的信号。而以下的执行过程实际也就是执行了9号信号的默认动作——杀死进程。

kill -9 进程PID
kill -SIGKILL 进程PID

可见,两者的执行结果相同。说明kill命令是发送信号的工具

signal函数

功能

设置某一信号的对应动作

头文件

#include <signal.h>

原型

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

函数解读

第一行是真实处理信号的函数:中断函数的原型中,有一个参数是 int 类型,显然也是信号产生的类型,方便使用一个函数来处理多个信号,即注册函数的第二个参数可以调用信号处理函数并执行其中的功能。

第二行是信号处理注册的函数:

signum信号的编号,如SIGKILL的编号是9
handler中断函数的指针,写入后可以调用编写的真实处理信号函数并执行功能

signal()会依参数signum指定的信号编号来设置该信号的处理函数。当指定的信号到达时就会跳转到参数handler指定的函数执行

返回值

成功则返回先前的信号处理函数指针,如果有错误则返回SIG_ERR(-1)。

代码示例

信号处理函数的注册

signal1.c

#include <stdio.h>
#include <signal.h>void handler(int signum)
{printf("get signum is %d\n",signum);printf("not quit\n");switch(signum){case 2:printf("SIGINT\n");break;case 9:printf("SIGKILL\n");break;case 10:printf("SIGUSR1\n");break;}
}int main()
{signal(SIGINT,handler);signal(SIGUSR1,handler);while(1);return 0;
}

代码编译后查看运行a.out工具,通过ps查看其编号

运用kill指令分别对信号进行处理

注:第一种按下crtl+c执行结果相同。

可见调用signal函数后匹配的正确编号后会执行handler中的功能(将函数编号打印出来)。第三个与前两个结果不一样是因为SIGKILL指令无法被忽略,这里的kill -9发出的是指令,由于代码为死循环,若SIGKILL被忽视,则会导致代码无法终止循环,所以一旦SIGKILL指令发出,程序立刻停止(被杀死)。

发送信号处理函数

signal2.c

#include <stdio.h>
#include <signal.h>int main(int argc,char **argv)//由于需要此代码发送指令另一部分代码才会执行,所以需要进行传参,参数为kill参数,格式为./a.out pid signum
{int signum;int pid;signum = atoi(argv[1]);pid = atoi(argv[2]);kill(pid,signum);//调用kill函数,将信号处理编号和工具的pid值输入即可printf("send signal success\n");return 0;
}

 先编译signal1.c(上一模块的代码)并运行

调用ps指令查看该程序的信号值

编译运行signal2.c中的代码传参即可运行signal1.c代码中的功能

将signum与pid输入后即可实现signal1.c中的功能,实现信号捕捉处理。

功能与signal2.c一样的代码:

#include <stdio.h>
#include <signal.h>int main(int argc,char **argv)
{int signum;int pid;char cmd[128] = {0};signum = atoi(argv[1]);pid = atoi(argv[2]);sprintf(cmd,"kill -%d %d",pid,signum);//cmd的指令格式为“”里的格式,即调用kill指令system(cmd);//调用cmd指令printf("send signal success\n");return 0;
}

注:
1、atoi (表示 ascii to integer)是把字符串转换成整型数的一个函数
2、sprintf指的是字符串格式化命令,函数原型为

 int sprintf(char *string, char *format [,argument,…]);

主要功能是把格式化的数据写入某个字符串中,即发送格式化输出到 string 所指向的字符串

信号忽略函数的补充

代码展示

#include<stdio.h>
#include <signal.h>
void handler(int signum)
{printf("get signum=%d\n",signum);switch(signum){case 2:printf("SIGINT\n");break;case 10:printf("SIGUSR1\n");break;}
}
int main()
{signal(SIGINT,SIG_IGN);//将SIGINT信号(ctrl+C、2)忽略signal(SIGUSR1,SIG_IGN);//将SIGUSR1信号(10)忽略while(1);return 0;
}

可见crtl+c和kill -10和kill -2都被忽略了,只有kill -9才能使该程序终止,印证的信号处理中的忽略部分不能忽略SIGKILL。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/212719.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

nodejs 如何将 Buffer 数据转为 String

问题说明 使用webSocket的时候出现了一个问题&#xff0c;前端小程序和nodejs后端建立websocket连接后&#xff0c;使用send方法发送到后端为buffer格式&#xff0c;以下为我前后端代码 1、前端小程序代码 //创建webSocket连接 const socket uni.connectSocket({url: wss…

畅捷通T6 客户端登录提示 运行时错误 372 加载控件cfloatmenu失败

客户单win10电脑, T6版本是 V7.1 不知道操作了什么每个电脑提示 运行时错误372: 从加载控件CFloatMenu失败。您的版本可能已过期。确认您使用的控件版本是同您的应用程序一起提供的。 ******* 解决办法: 找个其他电脑复制mscomctl.ocx 到操作系统目录里面注册一下,即可. …

linux网络之网络层与数据链路层

文章目录 一、网络层 1.IP协议 2.IP协议头格式 3.网段划分 4.特殊ip地址 5.IP地址的数量限制 6.私有ip和公网IP 7.路由 二、数据链路层 1.以太网 2.以太网帧格式 3.MAC地址 4.对比理解MAC地址和IP地址 5.MTU 6.ARP协议 ARP协议的工作流程 ARP数据报的格式 7.DNS 8.ICMP协议 9.N…

【Python】

Python 一、入门语法1.1 字符串的多种定义方式1.2 Python中的运算符1.3 input语句1.4 字符串格式化的方式 二、Python判断语句2.1 布尔类型和比较运算符2.2 if判断语句的基础格式2.3 if else语句的使用2.4 if_elif_else语句的使用2.5 判断语句的嵌套 三、Python循环语句3.1 whi…

操作系统(七)| 设备管理-- 端口 驱动程序 基本I/O控制 磁盘I/O

文章目录 1 设备管理概述1.1 系统总线结构1.2 设备控制器通用结构1.3 I/O设备的模型 2 I/O端口2.1 寻址方式 3 驱动程序4 基本I/O控制方式4.1 程序直接控制4.2 中断I/O方式4.3 DMA方式4.4 I/O通道控制方式 5 I/O管理中的重要思想5.1 设备独立性5.2 SPOOLing技术5.3 I/O软件的多…

C++类与对象(4)—日期类的实现

目录 一、类的创建和方法声明 二 、输出&运算符重载 三、检查合法性 1、获取对应年月的天数 2、初始化 四、实现加等和加操作 1、先写再写 2、先写再写 3、两种方式对比 五、实现自增和--自减 1、自增 2、自减 六、 实现减等和减操作 1、减等天数 2、加负数…

【开源】基于Vue.js的数据可视化的智慧河南大屏

项目编号&#xff1a; S 059 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S059&#xff0c;文末获取源码。} 项目编号&#xff1a;S059&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示四、核心代码4.1 数据模块 …

Dubbo3使用Zookeeper作为注册中心的方案讨论!详解DubboAdmin与PrettyZoo来监控服务的优劣!

文章目录 一&#xff1a;Dubbo注册中心的基本使用 二&#xff1a;Zookeeper注册中心的使用 1&#xff1a;依赖引入 2&#xff1a;实际开发 三&#xff1a;Zookeeper作为注册中心的使用展示 1&#xff1a;启动注册Zookeeper服务 2&#xff1a;引入注册中心 (一)&#xf…

CTF-PWN-QEMU-前置知识

文章目录 QEMU 内存管理(QEMU 如何管理某个特定 VM 的内存)MemoryRegion gpa->hpaFlatView&#xff1a;表示MR 树对应的地址空间FlatRange&#xff1a;存储不同MR对应的地址信息AddressSpace&#xff1a;不同类型的 MemoryRegion树RAMBlock总体简化图 QEMU 设备模拟 &#x…

Redis-Redis高可用集群之水平扩展

Redis3.0以后的版本虽然有了集群功能&#xff0c;提供了比之前版本的哨兵模式更高的性能与可用性&#xff0c;但是集群的水平扩展却比较麻烦&#xff0c;今天就来带大家看看redis高可用集群如何做水平扩展&#xff0c;原始集群(见下图)由6个节点组成&#xff0c;6个节点分布在三…

基于IDEA+Tomcat+Mysql开发的企业资产管理

基于Tomcat的企业资产管理信息系统 项目介绍&#x1f481;&#x1f3fb; 企业资产管理系统”项目是一个管理和维护企业资源的平台&#xff0c;能够提供系统管理、内容管理、用户管理、组织架构维护、流程管理、模块管理、角色管理、调度管理和个人设置等功能。 本项目所述的全…

linux的基础命令

文章目录 linux的基础命令一、linux的目录结构&#xff08;一&#xff09;Linux路径的描述方式 二、Linux命令入门&#xff08;一&#xff09;Linux命令基础格式 三、ls命令&#xff08;一&#xff09;HOME目录和工作目录&#xff08;二&#xff09;ls命令的参数1.ls命令的-a选…