Linux&C ——信号以及信号处理
- linux信号的简单介绍
- 信号的捕捉和处理
- 信号处理函数的返回
- 信号的发送
- 信号的屏蔽
一:linux信号的简单介绍。
信号提供给我们一种异步处理事件的方法,由于进程之间彼此的地址空间是独立的,所以进程之间的通信就需要特殊的机制,而信号是进程之间唯一的异步通信方式。我们平时可以接触到的信号来源一般有:用户从键盘键入、一些硬件的异常、用户使用kill命令或者函数发送或者当系统检测到某种软件已经具有发出信号的条件(比如alarm或settimer设置的定时器超时时将生成SIGALRAM信号),linux下的信号种类有60多种,我们可以输入下面的命令查看:
$ kill -l
其中1~31为不可靠信号(可能会丢失,信号不支持排队),33~64为可靠信号,也叫做实时信号。在linux中,当导致信号的事件发生时,内核就会产生一个信号,信号产生后,内核通常会在进程表中设置某种形式的标志,即内核向一个进程递送了一个信号,信号产生和递送之间的时间间隔叫做“信号未决”。我们用户在产生信号之后可以要求进程对信号做出以下处理,例如捕捉信号,处理信号,忽略信号等。
二:信号的捕捉和处理。
1:signal()函数
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
这个定义等价于:
void(*signal(int signum,void(*)(int)))(int)
signal函数的第一个参数是即将要处理的信号的编号,第二个参数是一个指向函数的指针,此函数为我们的信号处理函数,它是一个返回值为void,参数为int的函数。
2:sigaction()函数
int sigaction(int signum(指定的信号), const struct sigaction *act(若非空,则为指定信号设置新的处理函数), struct sigaction *oldact(若非空,则存储旧的信号处理函数));
可以说signal函数是sigaction函数的简化版,因为struct sigaction act这个结构体中除了信号处理函数之外,我们还可以指定一些其他的信息,比如一般情况下我们在一个信号处理函数没有返回的时候是默认阻塞此信号的,但是我们可以通过设置act中的sa_flags = SA_NOMASK来设置信号的状态为在信号处理函数结束之前允许再次递送。
3:pause()函数
int pause(void);
pause函数的作用是将当前的进程挂起,等待任意一个信号中断。下面这个程序先调用pause然后从右边的shell发送信号,一旦收到信号,就会结束进程,下面的代码不会执行。
三:信号处理函数的返回
信号处理函数可以自己正常返回,也可以调用下面四个函数返回
1:setjmp和longjmp函数
int setjmp(jmp_buf env);
env:一个全局变量,通过调用setjmp将目前的栈状态信息保存,当调用longjmp时能用来恢复栈状态的信息
int longjmp(jmp_buf env, int val);
val:此参数是setjmp的返回值。
两个函数配合使用,但是有缺点,因为我们在信号处理函数中会自动阻塞正在被处理的信号,如果我们调用longjmp函数返回,是不会解除阻塞的,即被处理过的信号会一直阻塞着,需要我们手动去处理,为了避免这种手动处理,我们会用到下面这两个函数:
int sigsetjmp(sigjmp_buf env, int savesigs);
savesigs:只要它的值非0,则sigsetjmp会在env中保存当前信号的屏蔽字,在调用siglongjmp时,会从其中恢复保存的信号屏蔽字。
void siglongjmp(sigjmp_buf env, int val);
四:信号的发送
1:kill函数:
int kill(pid_t pid, int sig);
pid:>0 : pid表示某一个进程的pid, =0 :给当前进程所属组中的所有进程发送, =-1 :把信号广播给系统中除了自己和init进程 <-1 :把信号发送给属于进程组-pid的所有进程。
注意:只有root用户才可以给所有的进程发送信号,普通用户只能同一组或者给自己发送信号。
2:raise()函数
int raise(int sig);
sig:表示要发送的信号,给调用raise的进程发送。
3:sigqueue()函数
int sigqueue(pid_t pid,int sig,const union sigval value);
union sigval //此共用体的第一个元素与siginfo_t中si_int 一样
{
int sival_int;
void *sival_ptr;
};
sigqueue函数是alarm函数的加强版本,它的第三个参数可以和sigaction配合使用在两个进程之间传输数据,即我们在信号发送的时候让它的第三个参数value.sival_int携带数值,而后我们在sigaction中将sa_flags = SA_SIGINFO,则信号处理函数由三参数的sa_sigaction指定,此时siginfo_t 结构体中的成员si_int 就可以接受发送的数据。
4:alarm()函数
unsigned int alarm(unsigned int seconds);
alarm函数设置定时器,时间过了就会给调用进程发送信号SIGALRM信号,只会发送一次,如果需要多次,则需要多次调用。
下面程序简单介绍了ping命令的实现:
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
//此程序的目的在于模拟ping的工作原理,
void handler_sigalarm(int signo)
{
printf("send a request packet\n");
alarm(1); //每隔一秒就会给进程发送一个SIGALRM信号
}
int main(int argc,char *argv[])
{
signal(SIGALRM,handler_sigalarm); //signal 函数在此处理SIGALRM,
raise(SIGALRM); //首先由raise函数给进程发送一个SIGALRM信号,signal函数捕获到发送的信号,进入信号处理函数,再次由alarm函数每隔一秒发送一个SIGALRM信号
while(1);
return 0;
}
五:信号的屏蔽
1:信号集:linux下信号有60多种,POSIX标准定义了数据类型sigset_t来表示信号集,并且定义了许多函数来操作信号集。
int sigemptyset(sigset_t *set); //初始化信号集,使其为空
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); //判断一个信号是否在信号集中
2:信号屏蔽:也可以说信号阻塞
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); //信号屏蔽
how:设置相关信号屏蔽码的操作。
set :非空的话为信号设置新的屏蔽码
oldset:非空的话将原来的信号屏蔽码返回int sigpending(sigset_t *set); //获取当前未决信号集,注意未决队列里面的信号如果没有被处理完毕,那么首先会执行信号处理函数
int sigsuspend(const sigset_t *mask); //将信号屏蔽码设置为mask,然后等待信号的发生并且执行信号处理函数,sigpending函数可以保证改变进程的屏蔽码和将进程挂起是原子操作。
版权声明:本文为博主原创文章,未经博主允许不得转载。
Linux&C ——信号以及信号处理的更多相关文章
- Linux&C ——信号以及信号处理
linux信号的简单介绍 信号的捕捉和处理 信号处理函数的返回 信号的发送 信号的屏蔽 一:linux信号的简单介绍. 信号提供给我们一种异步处理事件的方法,由于进程之间彼此的地址空间是独立的,所以进 ...
- Linux进程间通信——信号集函数
一.什么是信号 用过Windows的我们都知道,当我们无法正常结束一个程序时,可以用任务管理器强制结束这个进程,但这其实是怎么实现的呢?同样的功能在Linux上是通过生成信号和捕获信号来实现的,运行中 ...
- 详解linux进程间通信-信号
前言:之前说看<C++ Primer >暂时搁浅一下,迷上公司大神写的代码,想要明白,主要是socket.进程间通信! 知道进程间通信:信号.信号量.管道.消息队列.共享内存(共享存储), ...
- Linux线程编程之信号处理
前言 Linux多线程环境中的信号处理不同于进程的信号处理.一方面线程间信号处理函数的共享性使得信号处理更为复杂,另一方面普通异步信号又可转换为同步方式来简化处理. 本文首先介绍信号处理在进程中和线程 ...
- linux 异步信号的同步处理方式
关于代码的可重入性,设计开发人员一般只考虑到线程安全,异步信号处理函数的安全却往往被忽略.本文首先介绍如何编写安全的异步信号处理函数:然后举例说明在多线程应用中如何构建模型让异步信号在指定的线程中以同 ...
- Linux 的信号和线程
什么是线程 线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元.一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成,每一个程序都至少 ...
- linux kill信号列表
linux kill信号列表 $ kill -l1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL5) SIGTRAP 6) ...
- linux 自定义信号
从来没试过linux自定义信号,查了下,说是系统只提供了SIGUSR1和SIGUSR2两个,就两个够吗?更要命的是如果要自定义信号如#define SIG_MYSIG ....的话要改内核才行,哥 ...
- Linux之信号
产生信号五种方法: 按键产生:ctrl+c.ctrl+z.ctrl+\ 系统调用产生:如kill.raise.baort 软件条件产生:如定时器alarm 硬件异常产生:非法访问内存(段错误).除0( ...
随机推荐
- Jmeter扩展组件开发(8) - 函数助手扩展开发demo
前提条件 1.pom文件引用ApacheJMeter_functions包 <dependency> <groupId>org.apache.jmeter</groupI ...
- whistle手机抓包(以安卓手机为例)
环境:whistle:1.14.6 whistle手机抓包 以安卓手机为例 手机跟电脑要连同一个wifi. 1.启动whistle 使用w2 start启动whistle. 退出cmd后,whistl ...
- 鸿蒙内核源码分析(信号量篇) | 谁在负责解决任务的同步 | 百篇博客分析OpenHarmony源码 | v29.01
百篇博客系列篇.本篇为: v29.xx 鸿蒙内核源码分析(信号量篇) | 谁在负责解决任务的同步 | 51.c.h .o 进程通讯相关篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 自旋锁当立 ...
- 51nod1836-战忽局的手段【期望dp,矩阵乘法】
正题 题目连接:http://www.51nod.com/Challenge/Problem.html#problemId=1836 题目大意 \(n\)个点\(m\)次随机选择一个点标记(可以重复) ...
- YbtOJ#643-机器决斗【贪心,李超树】
正题 题目链接:https://www.ybtoj.com.cn/problem/643 题目大意 \(n\)个机器人,第\(i\)个攻击力为\(A_i\),防御为\(D_i\). 然后你每次可以对一 ...
- php页面 数组根据下标来排序
$a = [ ['id'=>1,'title'=>'星期二的早晨','author'=>'张三','date'=>'2021-6-1'], ['id'=>2,'title ...
- scheduler源码分析——preempt抢占
前言 之前探讨scheduler的调度流程时,提及过preempt抢占机制,它发生在预选调度失败的时候,当时由于篇幅限制就没有展开细说. 回顾一下抢占流程的主要逻辑在DefaultPreemption ...
- Spring配置文件结构对于生成Bean的影响
Spring配置文件结构对于生成Bean的影响 有段时间忙于毕设,导致Spring学习的东西忘了很多,所以最近又开始从头看Spring的基础.基础的Bean的装配不再多说了.这一次,主要是深入一点了解 ...
- 从零入门 Serverless | 一文讲透 Serverless Kubernetes 容器服务
作者 | 张维(贤维) 阿里云函数计算开发工程师 导读:Serverless Kubernetes 是以容器和 kubernetes 为基础的 Serverless 服务,它提供了一种简单易用.极致弹 ...
- 【分享】 一款自用的Anki卡片模板:黄子涵单词卡片 v1
[分享] 一款自用的Anki卡片模板:黄子涵单词卡片 v1 说明 第一代的功能 主要有两部分组成:英文和含义,目前主要是为自己记忆Web前端一些常用的单词而服务 有美美哒背景图,本来想修改为随机背景图 ...