【Linux】信号

目录

一、信号的概念

1. 什么是信号

2. 信号的种类

二、信号的产生

1. kill命令

2. 硬件产生

3. 系统调用

4. 软件条件

 5. 程序异常

三、信号的捕捉与保存

1. 信号的捕捉

2. 信号的保存 

四、信号的执行

1. 默认动作

2. 忽略动作

3. 自定义动作


一、信号的概念

1. 什么是信号

从一个故事理解信号

  • 你在网上买了很多件商品,再等待不同商品快递的到来。但即便快递没有到来,你也知道快递来临时,你该怎么处理快递。也就是你能“识别快递”
  • 当快递员到了你楼下,你也收到快递到来的通知,但是你正在打游戏,需5min之后才能去取快递。那么在在这5min之内,你并没有下去去取快递,但是你是知道有快递到来了。也就是取快递的行为并不是一定要立即执行,可以理解成“在合适的时候去取”。
  • 在收到通知,再到你拿到快递期间,是有一个时间窗口的,在这段时间,你并没有拿到快递,但是你知道有一个快递已经来了。本质上是你“记住了有一个快递要去取” 当你时间合适,顺利拿到快递之后,就要开始处理快递了。而处理快递一般方式有三种:1. 执行默认动 作(幸福的打开快递,使用商品)2. 执行自定义动作(快递是零食,你要送给你你的女朋友)3. 忽略快递(快递拿上来之后,扔掉床头,继续开一把游戏)
  • 快递到来的整个过程,对你来讲是异步的,你不能准确断定快递员什么时候给你打电话

信号是进程之间事件异步通知的一种方式,属于软中断。如:

  • 输入命令,在Shell下启动一个前台进程。
  • 用户按下Ctrl-C,键盘输入产生一个硬件中断。
  • 如果CPU当前正在执行这个进程的代码,则该进程的用户空间代码暂停执行, CPU从用户态切换到内核态处理硬件中断。
  • 终端驱动程序将Ctrl-C解释成一个SIGINT信号,记在该进程的PCB中(也可以说发送了一个SIGINT信号给该进程)。
  • 当某个时刻要从内核返回到该进程的用户空间代码继续执行之前,首先处理PCB中记录的信号,发现有一个SIGINT信号待处理,而这个信号的默认处理动作是终止进程,所以直接终止进程而不再返回它的用户空间代码执行。

2. 信号的种类

使用命令查看:kill -l

本文我们只探究1-31号信号。

 1-31号信号的默认动作大部分都为终止进程。

二、信号的产生

1. kill命令

可以通过kill命令向指定进程发送指定信号。

如向pid为12345的命令发送9号命令SIGKILL:

kill -9 12345

2. 硬件产生

如在键盘上按ctrl + c,相当于向进程发送2号信号SIGINT。

3. 系统调用

kill函数

#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig); //用法参考kill命令

abort函数

#include <stdlib.h>

void abort(void); //向系统发生6号信号SIGABRT

4. 软件条件

 管道

但管道读端关闭时,os会向管道发送13号信号SIGPIPE,将管道销毁。

alarm函数

#include <unistd.h>

unsigned int alarm(unsigned int seconds);
//相当与一个闹钟,当alarm函数触发后,经seconds秒后os向系统发送14号信号SIGALRM

 

 5. 程序异常

当程序出现异常时,os会向程序发送8号信号SIGFPE或13号信号SIGEGV,导致程序退出。

我们看到的表现形式为程序奔溃。

三、信号的捕捉与保存

1. 信号的捕捉

2. 信号的保存 

进程的PCB中有一个专门用来保存信号的字段。信号保存的结构由block、pending两个位图和handler这一个函数指针数组三部分组成。

信号的编号本质就是数组下标。

当信号产生时,对应pending位便会由0置1。os定期扫描pending位图,如果扫描时发现有pending位为1,且block位不为1,责执行该位的handler方法。

block位图是记录信号阻塞的位图。一旦某一信号的对应block位为1,该信号就被阻塞,及收到信号后只进行记录而不执行。

handler数组记录的是信号动作。

信号集操作函数如下:

#include <signal.h>

int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset (sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo); 
  • sigset_t是一种位图数据结构
  • 函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含 任何有 效信号。
  • 函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置位,表示 该信号集的有效信号包括系 统支持的所有信号。
  • 注意,在使用sigset_ t类型的变量之前,一定要调用sigemptyset或sigfillset做初始化,使信号集处于确定的 状态。初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信号

四、信号的执行

信号有三种动作,默认、忽略和自定义,详细介绍如下:

1. 默认动作

即执行信号的默认功能。

2. 忽略动作

忽略该信号,即收到信号后pending位置0但不执行任何函数。

#include <signal.h>

sighandler_t signal(int signum, SIG_IGN); 
//signum为想忽略的信号
//如:signal(SIGINT, SIG_IGN); 

3. 自定义动作

收到信号后执行用户自定义的方法。

#include <signal.h>

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);
//signum为想忽略的信号,handler为函数指针
//如:

#include <iostream>
#include <signal.h>

void test(int signo)
{
    std::cout << "test sig, signo : " << signo << std::endl;
}

int main()
{
    signal(SIGINT, test);
    while(1);
    return 0;
}
  • 21
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值