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( ...
随机推荐
- Shell系列(33) - 多分支if语句简介及计算器例子
多分支if条件语句 if [ 条件判断式1 ] then 当条件判断式1成立时,执行程序1 elif [ 条件判断式2 ] then 当条件判断式2成立时,执行程序2 ...省略更多条件... els ...
- MSSQL数据库安全实验
管理SQL Server认证模式 (1)确认 SQL Server 验证 ①在桌面上单击"开始",选择"程序"→"Microsoft SQL Serv ...
- 一文让你快速入门pytest框架
pytest是什么 官方文档描述: pytest is a framework that makes building simple and scalable tests easy. Tests ar ...
- [转载]PHP命名规则
PHP命名规则 引用地址:http://www.cnblogs.com/pengxl/p/3571157.html 就一般约定而言,类.函数和变量的名字应该是能够让代码阅读者能够容易地知道这些代码的作 ...
- YbtOJ#903-染色方案【拉格朗日插值,NTT,分治】
正题 题目链接:https://www.ybtoj.com.cn/contest/115/problem/3 题目大意 两个长度为\(n+1\)的序列\(a,b\) \(a_i\)表示涂了\(i\)个 ...
- 【问题记录】Java服务发起HTTPS请求报错:PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException
问题报错 今天上线了我开发的一个OAuth2单点登录客户端的实现,在测试系统验证没问题,到生产环境由于单点登录服务端HTTPS协议,报错如下: I/O error on POST request fo ...
- 从一个舒服的姿势插入 HttpClient 拦截器技能点
马甲哥继续写一点大前端,阅读耗时5 minute,行文耗时5 Days 今天我们来了解一下如何拦截axios请求/响应? 这次我们举一反三,用一个最舒适的姿势插入这个技能点. axios是一个基于 p ...
- Django基础1
一,web框架的本质 web应用的本质就是一个socket的服务端.而用户的浏览器就是一个客户端,具体事例如下: import socket sk = socket.socket() sk.bind( ...
- 洛谷4219 BJOI2014大融合(LCT维护子树信息)
QWQ 这个题目是LCT维护子树信息的经典应用 根据题目信息来看,对于一个这条边的两个端点各自的\(size\)乘起来,不过这个应该算呢? 我们可以考虑在LCT上多维护一个\(xv[i]\)表示\(i ...
- Endian
Endian 寻址 多字节对象被存储为连续的字节序列,对象的地址为所使用字节中最小的地址. 例如,假设一个类型为 int 的变量 a 的地址为 0x100,也就是说,地址表达式 &a 的值为 ...