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系列(30)- 性能指标(3) | 性能指标峰值
性能指标峰值 简述 彻底理解了性能指标(1)(2)的内容,这一篇随笔其实就不用看了,而且大家也能猜到这一篇内容是啥:二八原则 性能指标不要硬性的往那些性能指标上去靠,要根据业务来,熟悉业务,明白了解你 ...
- Linux系列(38) - 源码包安装(2)
安装前准备 安装C语言编译器"gcc" yum -y install gcc --c 源码包语言编译器 下载源码包 安装注意事项 源代码保存位置:/usr/local/src/ 软 ...
- Spring Cloud Gateway 没有链路信息,我 TM 人傻了(上)
本系列是 我TM人傻了 系列第五期[捂脸],往期精彩回顾: 升级到Spring 5.3.x之后,GC次数急剧增加,我TM人傻了 这个大表走索引字段查询的 SQL 怎么就成全扫描了,我TM人傻了 获取异 ...
- P2611-[ZJOI2012]小蓝的好友【Treap,扫描线】
正题 题目链接:https://www.luogu.com.cn/problem/P2611 题目大意 \(r*c\)的网格上有\(n\)个标记点,然后求有多少个矩形包含至少一个标记点. \(1\le ...
- 使用VUE+原生PHP完成搜索后分页的效果
html代码: <!doctype html> <html lang="en"> <head> <meta charset="U ...
- 在Vue中使用JSX,很easy的
摘要:JSX 是一种 Javascript 的语法扩展,JSX = Javascript + XML,即在 Javascript 里面写 XML,因为 JSX 的这个特性,所以他即具备了 Javasc ...
- websocket方案调研及实践
目录 webscoket方案调研及实践 一.使用场景 二.方案调研 1.Ajax短轮询 2.long-polling长轮询 3.iframe长连接 4.XHR-streaming 5.Websocke ...
- Hive SQL的底层编译过程详解
本文结构采用宏观着眼,微观入手,从整体到细节的方式剖析 Hive SQL 底层原理.第一节先介绍 Hive 底层的整体执行流程,然后第二节介绍执行流程中的 SQL 编译成 MapReduce 的过程, ...
- Visual Studio 安装 C++
Visual Studio 安装 C++
- Netty学习笔记(2)ByteBuffer
1. 测试ByteBuffer 1.1 依赖 <dependencies> <dependency> <groupId>io.netty</groupId&g ...