12.3 Linux_进程间通信_信号机制

概述

什么是信号:

信号是在软件层次上对中断机制的模拟(软中断),是一种异步通信方式。 

进程对信号的响应方式:

  • 缺省方式:根据默认行为响应信号
  • 忽略信号:不响应信号
  • 捕捉信号:根据指定行为响应信号

常用信号及含义:

见博文"4.Linux_Shell命令"-"进程管理"-"2、向进程发送信号",博文连接如下:

4.Linux_Shell命令-CSDN博客

信号相关命令:

见博文"4.Linux_Shell命令"-"进程管理"-"2、向进程发送信号",博文连接如下:

4.Linux_Shell命令-CSDN博客

信号的状态:

信号递达:代表信号已经被进程接收到,不论进程的处理方式是缺省、忽略还是捕捉。

信号未决:代表信号产生到被进程接收这一段过程。

信号集:

信号屏蔽字mask:设置哪一个信号被屏蔽,bit位与kill -l查询出的相对应。

                              bit1=1代表SIGHUP被屏蔽

未决信号集:当相应信号被屏蔽时,信号产生后,对应bit位被置1。bit位与kill -l查询出的相对应。

                      当mask bit1=1时,SIGHUP信号产生后,未决信号集的bit1=1

                      当信号被取走后,bit位被自动清零

信号相关函数

1、发送信号

//像指定进程发送信号
int kill(pid_t pid, int sig);
//给自己发送信号
int raise(int sig);

返回值:成功返回0,失败返回EOF

pid:进程号

pid值含义
> 0发送给指定的进程
= 0发送给该进程所在的进程组中的进程
= -1

取绝对值,发送给指定进程所在的进程组中的所有进程

例如:pid = -2,那么发送给pid=2的进程所在的进程组中的进程

< -1发送给所有进程

sig:信号类型,由kill -l 查出,比如要发送SIGINT,该值就为2

2、定时器

注意:一个进程中只能设定一个定时器,定时到达时产生SIGALRM信号

注意:有时sleep函数内部实现也会用到定时器,因此使用定时器时需要把sleep注释掉 

2.1 一次性定时器

//一次性定时器
unsigned int alarm(unsigned int seconds);

返回值:成功返回上个定时器的剩余时间,失败返回EOF

seconds:定时时间,单位s

2.2 循环发送定时器 

//循环发送定时器
useconds_t ualarm(useconds_t usecs, useconds_t interval);

usecs:第一次产生的时间,单位us

interval:之后定时结束的时间间隔,单位us

2.3 更通用的循环发送定时器 

//更通用的循环发送定时器 
int setitimer(int which, const struct itimerval *restrict value,struct itimerval *restrict ovalue);

which:选项,写入ITIMER_REAL,代表真正逝去的时间

value:新的超时时间

ovalue:旧的超时时间,写入NULL即可

struct itimerva结构体成员:

struct itimerval {struct timeval it_value;    /* 闹钟触发时间 */struct timeval it_interval; /* 闹钟触发周期 */
};struct timeval {time_t tv_sec;       /* 秒数 */suseconds_t tv_usec; /* 微秒数 */
};

3、捕捉信号自定义函数

3.1 signal(不建议使用)

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

返回值:成功返回原信号处理函数,失败返回SIG_ERR 

signum:改变哪一个信号的行为

handler:信号行为改变成什么,SIG_DFL代表缺省,SIG_IGN代表忽略

示例代码如下:

具体代码实现如下:

#include <stdio.h>
#include <signal.h>
#include <errno.h>typedef void (*sighandler_t)(int);
void SIG_Handler(int);
int main(){if(signal(SIGINT,SIG_Handler) == SIG_ERR){perror("signal");}while(1);return 0;
}
void SIG_Handler(int sig){printf("this is SIG_Handler,SIG num = %d\n",sig);if(signal(SIGINT,SIG_DFL) == SIG_ERR){perror("signal");}
}

代码执行结果如下:

3.2 sigaction(建议使用)

int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

返回值:成功返回0,失败返回-1 

signum:改变哪一个信号的行为

act:信号行为改变成什么

oldact:原信号处理函数,不关心可以写NULL

struct sigaction结构体成员:

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:屏蔽位,使用 sigemptyset(&act.sa_mask) 去清空它即可。            

sa_flags:设置回调函数选择哪一个函数指针

sa_flags值含义
SA_SIGINFO信号回调函数使用sa_sigaction
0信号回调函数使用sa_handler

示例1:实现signal相同的功能

#include <stdio.h>
#include <signal.h>
#include <errno.h>void SIG_Handler(int);
int main(){struct sigaction act;//设置信号处理函数act.sa_handler = SIG_Handler;//设置信号回调函数sigemptyset(&act.sa_mask); 	 //清空屏蔽位act.sa_flags = 0; 			 //0代表使用sa_handler作为信号回调函数//改变信号处理函数if(sigaction(SIGINT,&act,NULL) != 0){perror("sigaction");}while(1);return 0;
}
void SIG_Handler(int sig){struct sigaction act;act.sa_handler = SIG_DFL;//设为默认形式sigemptyset(&act.sa_mask); 	 act.sa_flags = 0; 			printf("this is SIG_Handler,SIG num = %d\n",sig);if(sigaction(SIGINT,&act,NULL) != 0){perror("sigaction");}
}

示例2:实现定时器

实现定时器,首先设置好定时器发出的SIGALRM信号该如何处理,之后设置好定时器该如何定时即可。

具体代码实现如下:

#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <sys/time.h>void SIG_Handler(int);
int main(){struct sigaction act;struct itimerval value;//设置信号处理函数act.sa_handler = SIG_Handler;//设置信号回调函数sigemptyset(&act.sa_mask); 	 //清空屏蔽位act.sa_flags = 0; 			 //0代表使用sa_handler作为信号回调函数if(sigaction(SIGALRM,&act,NULL) != 0){perror("sigaction");}//设置定时器value.it_value.tv_sec = 5;//第一次定时5svalue.it_value.tv_usec = 0;value.it_interval.tv_sec = 1;//之后定时1svalue.it_interval.tv_usec = 0;printf("now time start\n");setitimer(ITIMER_REAL,&value,NULL);//启动定时器while(1);return 0;
}
void SIG_Handler(int sig){printf("time over,sig = %d\n",sig);
}

代码运行结果如下:

示例3:通过SIGCHLD信号回收子进程

回收子进程最终都要调用wait函数,但wait会进行阻塞直到子进程退出,通过捕捉SIGCHLD信号,可以使得父进程不进入阻塞,当子进程终止时中断父进程,从而实现不阻塞回收子进程。

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>void SIG_Handler(int);
int main(){pid_t pid;struct sigaction act;if((pid = fork())<0){perror("fork");}else if(pid == 0){while(1){printf("child is running\n");sleep(1);}}else{//设置信号处理函数act.sa_handler = SIG_Handler;//设置信号回调函数sigemptyset(&act.sa_mask); 	 //清空屏蔽位act.sa_flags = 0; 			 //0代表使用sa_handler作为信号回调函数if(sigaction(SIGCHLD,&act,NULL) != 0){perror("sigaction");}while(1){printf("father is running\n");sleep(1);}}return 0;
}
void SIG_Handler(int sig){int wstatus;printf("get SIG_Handler\n");waitpid(-1,&wstatus,WNOHANG);//以非阻塞方式,等待子进程结束if(WIFEXITED(wstatus)){      //判断子进程是否正常退出printf("子进程的返回值为%d\n",WEXITSTATUS(wstatus));}else{printf("子进程是否被信号结束%d\n",WIFSIGNALED(wstatus));printf("结束子进程的信号类型%d\n",WTERMSIG(wstatus));}
}

信号集相关函数

1、设置信号集

//自定义信号集
sigset_t set;
//清空信号集,即:全部写0
int sigemptyset(sigset_t *set);
//填充信号集,即:全部写1
int sigfillset(sigset_t *set);
//向信号集中添加指定信号
int sigaddset(sigset_t *set, int signum);
//从信号集中删除指定信号
int sigdelset(sigset_t *set, int signum);
//判断一个信号是否在集合中
int sigismember(const sigset_t *set, int signum);

set:信号集

signum:信号类型,如:SIGINT

2、屏蔽信号

int sigprocmask(int how,const sigset_t *restrict set,sigset_t *restrict oset);

返回值:成功返回0,失败返回-1

how:信号设置后,做什么事情

           注意:不能阻塞SIGKILL和SIGSTOP

指令含义
SIG_BLOCK

将set中的信号添加到信号屏蔽字中,即:信号阻塞

添加:最终结果 = set | oldset

SIG_UNBLOCK解除信号阻塞,如果之前有信号产生,也会被检测到
SIG_SETMASK

将信号屏蔽字设置为set

设置:最终结果 = set,与oldset无关

set:新的信号集

oset:旧的信号集,不关系可以写NULL

使用示例:

具体代码实现如下:

#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>void SIG_Handler(int);
int main(){struct sigaction act;sigset_t set;//设置信号处理函数act.sa_handler = SIG_Handler;//设置信号回调函数sigemptyset(&act.sa_mask); 	 //清空屏蔽位act.sa_flags = 0; 			 //0代表使用sa_handler作为信号回调函数if(sigaction(SIGINT,&act,NULL) != 0){perror("sigaction");}//控制信号集sigemptyset(&set);                 //清空信号集sigaddset(&set,SIGINT);            //添加想要阻塞的信号sigprocmask(SIG_BLOCK,&set,NULL);  //设置信号阻塞sleep(5);sigprocmask(SIG_UNBLOCK,&set,NULL);//设置信号不阻塞,即:响应信号while(1);return 0;
}
void SIG_Handler(int sig){printf(" get sig\n");
}

代码运行结果如下:

信号驱动任务

信号驱动任务的方式:

  • 捕捉信号,改变信号处理函数(类似中断)
  • 使用pause实现(不是中断,信号只是作为开始信号)
  • 使用sigsuspend改善pause的实现代码

1、捕捉信号实现

捕捉信号实现就是"信号相关函数"-"3、捕捉信号自定义函数"中的示例代码使用方法。

2、 pause实现

//进程一直阻塞,直到被信号中断
int pause(void);

返回值:-1

pause的行为:

  • 若信号默认行为是终止,则进程终止,pause没有机会返回
  • 若信号默认行为是忽略,则进程继续阻塞,pause不返回
  • 若信号处理动作为捕捉,则调用完信号处理函数后,pause返回-1
  • 若信号被屏蔽,则进程继续阻塞,pause不返回(这其实就是没收到信号)

示例代码:

具体代码实现如下:

#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>void SIG_Handler(int);
void* Task(void* arg);
int main(){struct sigaction act;sigset_t set;int ret;//设置信号处理函数act.sa_handler = SIG_Handler;//设置信号回调函数sigemptyset(&act.sa_mask); 	 //清空屏蔽位act.sa_flags = 0; 			 //0代表使用sa_handler作为信号回调函数if(sigaction(SIGINT,&act,NULL) != 0){perror("sigaction");}//控制信号集sigemptyset(&set);                 //清空信号集sigaddset(&set,SIGINT);            //添加想要阻塞的信号//信号驱动任务while(1){printf("now is waiting sig\n");ret = pause();                     //等待信号到达sigprocmask(SIG_BLOCK,&set,NULL);  //设置信号阻塞Task((void*)ret);                  //执行相应任务sigprocmask(SIG_UNBLOCK,&set,NULL);//设置信号不阻塞,即:响应信号}return 0;
}
void SIG_Handler(int sig){printf(" get sig\n");
}
void* Task(void* arg){printf("Now is Task Fun,arg = %d\n",(int)arg);sleep(5); 
}

代码执行结果如下:

该情况可由"3、sigsuspend改善pause代码"实现

3、sigsuspend改善pause代码

int sigsuspend(const sigset_t *sigmask);

sigmask:想要屏蔽的信号,设置该值使用"信号集相关函数"-"1、设置信号集"中的函数。

                 该值被sigemptyset设置后代表全部信号都不屏蔽

示例代码:

具体代码实现如下:

#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>void SIG_Handler(int);
void* Task(void* arg);
int main(){struct sigaction act;sigset_t set,unblockSet;int ret;//设置信号处理函数act.sa_handler = SIG_Handler;//设置信号回调函数sigemptyset(&act.sa_mask); 	 //清空屏蔽位act.sa_flags = 0; 			 //0代表使用sa_handler作为信号回调函数if(sigaction(SIGINT,&act,NULL) != 0){perror("sigaction");}//控制信号集sigemptyset(&set);                 //清空信号集sigaddset(&set,SIGINT);            //添加想要阻塞的信号//信号驱动任务printf("now is waiting sig\n");ret = pause(); 						   //在while外使用pause等待第一次的信号sigemptyset(&unblockSet);         	   //清空信号集,该信号集用于解除信号阻塞while(1){sigprocmask(SIG_BLOCK,&set,NULL);  //设置信号阻塞Task((void*)ret);sigsuspend(&unblockSet); 		   //解除信号阻塞并且可以让pause接收到阻塞的信号//该函数相当于sigprocmask和pause}return 0;
}
void SIG_Handler(int sig){printf(" get sig\n");
}
void* Task(void* arg){printf("Now is Task Fun,arg = %d\n",(int)arg);sleep(5); 
}

代码执行结果如下:

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

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

相关文章

SpringBoot系列 启动流程

文章目录 SpringApplicationSpringApplication#run 启动流程BootstrapContextSpringApplicationRunListenersprepareEnvironmentconfigureEnvironmentconfigurePropertySourcesconfigureProfiles 上下文初始化prepareContextrefreshContextprepareRefreshobtainFreshBeanFactor…

MISC - 第13天(python脚本 重命名文件,拼接二维码,cloacked-pixel工具,中文电码,五笔编码)

前言 各位师傅大家好&#xff0c;我是qmx_07&#xff0c;今天继续讲解MISC的相关知识 [安洵杯 2019]吹着贝斯扫二维码 附件信息: 使用APCHPR暴力破解工具&#xff0c;flag.zip 破解失败可能线索在其他文件&#xff0c;放到hxd查看 在hxd中&#xff0c;发现该文件头JFIF 是j…

晶体规格书及匹配测试

一、晶体参数介绍 晶体的电气规格相对比较简单,如下: 我们逐一看看每个参数, FL就是晶体的振动频率,这个晶体是24.576MHz的。 CL就是负载电容,决定了晶体频率是否准确,包括外接的实际电容、芯片的等效电容以及PCB走线的寄生电容等,核心参数。 Frequency Tolerance是…

matlab碳交易机制下考虑需求响应的综合能源系统优化运行

目录 1 主要内容 架构模型&#xff1a; 需求响应模型&#xff1a; 目标函数&#xff1a; 对比算例设计&#xff1a; 2 部分程序 3 程序结果 4 下载链接 1 主要内容 该程序复现文献《碳交易机制下考虑需求响应的综合能源系统优化运行》&#xff0c;解决碳交易机制下考虑…

工业缺陷检测深度学习方法

工业缺陷检测深度学习方法 基于深度学习的工业缺陷检测方法可以降低传统人工质检的成本, 提升检测的准确性与效率, 因而在智能制造中扮演重要角色, 并逐渐成为计算机视觉领域新兴的研究热点之一. 其被广泛地应用 于无人质检、智能巡检、质量控制等各种生产与运维场景中. 本综述…

Spring Boot驱动的足球青训俱乐部管理解决方案

1 绪论 1.1研究背景 随着科技的发展&#xff0c;计算机的应用&#xff0c;人们的生活方方面面都和互联网密不可分。计算机的普及使得人们的生活更加方便快捷&#xff0c;网络也遍及到我们生活的每个角落&#xff0c;二十一世纪信息化时代的到来&#xff0c;随着社会科技的不断…

241007深度学习之LeNet

目录 1.LeNet介绍2.组成3.代码实现 1.LeNet介绍 LeNet是最早发布的卷积神经网络之一,他是由AT&T贝尔实验室的研究员Yann LeCun在1989年提出的(并且以其命名),目的是识别图像中手写数字.当时,Yann LeCun发表了第一篇通过反向传播成功训练卷积神经网络的研究论文,这项工作代…

关于CSS Grid布局

关于CSS Grid布局 实际效果参考 参考代码 <template><view class"baseInfo"><up-image class"cover" height"160rpx" width"120rpx" :src"bookInfo.cover"><template #error><view style"…

基于Zynq SDIO WiFi移植二(支持2.4/5G)

1 SDIO设备识别 经过编译&#xff0c;将移植好的uboot、kernel、rootFS、ramdisk等烧录到Flash中&#xff0c;上电启动&#xff0c;在log中&#xff0c;可看到sdio设备 [ 1.747059] mmc1: queuing unknown CIS tuple 0x01 (3 bytes) [ 1.761842] mmc1: queuing unknown…

卫星测绘AI技术-立哥尖端科研

分布式微波干涉测绘卫星是以多颗满足一定编队构形的卫星为平台&#xff0c;以合成孔径雷达 和高精度星间相对状态测量设备等为有效载荷&#xff0c;具备全天时、全天候获取雷达干涉影像数 据&#xff0c;快速测制全球数字表面模型、数字雷达正射影像等测绘产品能力的卫星系统…

点可云ERP进销存V8版本——其他支出单使用说明

其他支出单用于记录除采购内容外其支出资金&#xff0c;如&#xff1a;人工运输费、安装维修服务、差旅报销等。新增保存之后&#xff0c;对应资金账户将减少金额额度&#xff0c;并做存储记录&#xff0c;可在现金银行报表中体现。 新增操作 接下来我们讲解新增单据步骤。如上…

PHP 基础语法详解

PHP 基础语法详解 PHP&#xff08;全称&#xff1a;PHP: Hypertext Preprocessor&#xff09;是一种广泛应用的服务器端脚本语言&#xff0c;特别适用于 Web 开发。它易于学习&#xff0c;且能够快速构建动态网站。本篇博客将详细介绍 PHP 的基础语法&#xff0c;帮助初学者理…

[OS] 编译 Linux 内核

编译 Linux 内核&#xff1a;详细教程与 Kthreads 入门结合 我们将学习如何编译 Linux 内核&#xff0c;同时结合 Kthreads 的知识来理解各个步骤的目的。对于虚拟环境下的开发环境配置&#xff0c;本文将为你提供逐步指导。 1. 下载内核源代码 首先&#xff0c;我们需要从官…

第 1 章 MyBatis快速入门

1.1 ORM简介 ORM&#xff08;Object Relational Mapping&#xff0c;对象——关系映射&#xff09;框架的主要功能是根据映射配置文件&#xff0c;完成数据在对象模型与关系模型之间的映射&#xff0c;同时出屏蔽了连接数据库、创建 Statement 对象、执行 SQL、读取 ResultSet…

(Linux驱动学习 - 8).信号异步通知

一.异步通知简介 1.信号简介 信号类似于我们硬件上使用的“中断”&#xff0c;只不过信号是软件层次上的。算是在软件层次上对中断的一种模拟&#xff0c;驱动可以通过主动向应用程序发送信号的方式来报告自己可以访问了&#xff0c;应用程序获取到信号以后就可以从驱动设备中…

【技术】Jaskson的序列化与反序列化

文章目录 概念解释1.Jasksona.JSONJSON 的基本特点JSON 的基本结构JSON 示例 b.ObjectMapper类 2.序列化与反序列化a.序列化对象序列化集合序列化ListSetMap b.反序列化反序列化单个对象反序列化集合对象 概念解释 1.Jaskson Jackson 是一个用于处理 JSON 数据的 Java 库,所以…

k8s实战-1

k8s实战-1 一、资源创建方式1.命令行2.yaml 二、命名空间三、Pod总结 一、资源创建方式 1.命令行 就是直接通过命令的方式创建&#xff0c;比如我要创建namespace&#xff0c; kubectl create namespace hello删除&#xff1a; kubectl delete -f hello2.yaml 简单来说&am…

用java编写飞机大战

游戏界面使用JFrame和JPanel构建。背景图通过BG类绘制。英雄机和敌机在界面上显示并移动。子弹从英雄机发射并在屏幕上移动。游戏有四种状态&#xff1a;READY、RUNNING、PAUSE、GAMEOVER。状态通过鼠标点击进行切换&#xff1a;点击开始游戏&#xff08;从READY变为RUNNING&am…

无人机单目+激光+IMU复杂弧形(隧道)退化场景SLAM技术详解

无人机在复杂弧形&#xff08;如隧道&#xff09;退化场景中的SLAM&#xff08;同时定位与地图构建&#xff09;技术&#xff0c;结合单目相机、激光雷达&#xff08;LiDAR&#xff09;和惯性测量单元&#xff08;IMU&#xff09;时&#xff0c;能够显著提升定位与建图的准确性…

在忘记密码的情况下重新访问手机?5种忘记密码解锁Android手机的方法

无需密码即可访问Android手机。 即使你忘记了密码&#xff0c;你也可以解锁你的Android手机&#xff0c;但你通常需要将手机恢复出厂设置。 您可以通过执行出厂恢复或使用“查找我的设备”网站解锁大多数Android手机。 如果你不再有密码&#xff0c;这里有五种解锁安卓手机的…