[Linux] 信号(singal)详解(一)

标题:[Linux] 信号(singal)详解

@水墨不写bug

(图片来源于网络)

目录

一、认识信号

1、认识信号

2、信号特点

3、基本概念

二、信号的产生(5种方式)

三、信号的保存


正文开始:

一、认识信号

1、认识信号

信号,联系生活,比如你看到钟表知道现在的时间,于是提醒自己这会儿要要干啥了;再比如,在过马路时,看到信号灯是红色,你会停下来等待,直到信号灯变绿。这都是信号的例子,其实信号简而言之,就是一种简易的信息传递方式。

2、信号特点

随时可能产生,信号的产生是没有预告的,与你的生活是异步的。

你能识别信号(可以想想一下,如果不能识别信号,生活会变得怎么样),并且知道得到这个信号之后该怎么做。

当你收到信号的时候,你可能正在做更加重要的事(比如在期末考),这时需要把信号保存起来,暂不处理。

转到OS层面:

在进程中,信号可能随时产生,(这意味着OS需要不停的检测信号是否产生),信号的产生和进程异步。

进程能够识别信号,并且知道得到这个信号之后该怎么做(处理信号)。

进程可能正在做更加重要的事情,于是可能会把信号暂不处理。
综上,OS需要组织信号,并且需要在合适的时候处理信号。

3、基本概念

信号是LINUX OS 提供的一种,向指定进程发送特定信息的方式。目的是让进程做识别和处理。

二、信号的产生(5种方式)

1.通过kill指令,向特定的进程发送信号。

首先可以通过 kill -l查看所有的信号类型:

对于不同种类的信号,OS会有不同的处理方式(后文将会有详细解释)

2.键盘可以产生信号。

ctrl + c :SIGINT(2号信号)

ctrl + \ = | : SIGQUIT(3号信号)

(见上图)

3.系统调用可以产生信号。

int kill(pid_t id,int sig);------kill命令的底层

void abort(void); ------产生 SIGABRT;

4.软件条件可以产生信号。

比如:

管道read fd关闭,write fd还在写入,OS发送SIGPIPE (13号信号)来杀死write进程。

alarm系统调用可以产生信号:

5.异常可以产生信号。

对于非法访问操作,被OS检测到,OS会发送信号。


OS为什么可以检测到信号?

以发生除0错误为例,当发生除0时,CPU内部的eflag(寄存器)的溢出标记位被置1,表明本次运算的结果发生溢出,计算结果不可信。

CPU是硬件,OS要管理硬件,于是OS会检测到CPU的这个标记位的错误信息,并通过发送信号的方式,终止发生错误的进程。

总结:OS通过检测硬件的标记位信息,来判断是否发生了错误。


三、信号的保存

信号的保存和处理其实是密切联系的。信号的保存通过三张表实现的------block位图、pending位图、handler函数指针表。

概念引入:

**信号递达:**实际执行信号的过程称为信号的递达(Delivery)。

**信号未决:**信号从产生到递达之间状态称为信号未决(pending)。

**信号阻塞:**进程可以阻塞某一信号,意味着这个信号一旦产生,永远不递达,一直是未决状态,直到主动解除阻塞为止,才执行递达的动作。

注意:

信号的阻塞与其是否未决无关。

阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。


通过查看kill -l:

发现:

普通信号一共31个 。如果用一个位图来存储,需要31个bit位,于是Linux OS提供了一个专门的数据类型用作位图:sigset_t 。


本章开头所说的2张位图和一张函数指针表实际就是存储在进程的task_struct中的:

每个信号都有两个标志位分别表示阻塞(block)未决(pending),和**函数指针表(handler)**表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志(设置为1),直到信号递达才清除该标志(设置为0)。

在上图的例子中:

  • SIGHUP信号未阻塞也未产生过;当它递达时执行默认处理动作。
  • SIGINT信号产生过,但正在被阻塞,所以暂时不能递达;虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有可能会对SIGINT进行自定义处理,进而解除阻塞之后可能会产生意想不到的结果。
  • SIGQUIT信号未产生过;SIGQUIT产生之后,一旦产生SIGQUIT信号将被解除阻塞,它的处理动作是用户自定义函数sighandler。
    到这里,我们可以总结:

两张位图和一张函数指针数组,可以实现让进程识别信号!!

实验:检测信号的保存

注意:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。如何验证(常规信号在递达之前产生多次只记一次)呢?

通过下面的这一份实验测试,可以验证。但是你可能会对实验中使用的关于信号的函数接口有疑惑,鉴于此,我会在注释中尽可能详细注明;并在后续的文章内容中详细讲解系统的信号部分的接口的使用。

cpp 复制代码
#include <signal.h>
#include <unistd.h>
#include <iostream>

using std::cout;
using std::endl;
bool loop = true;

void Print(sigset_t &pending)
{
    for (int sig = 31; sig > 0; sig--) // 没有0号信号,信号的范围1------31,两闭
    {
        if (sigismember(&pending, sig))
        {
            cout << 1;
        }
        else
        {
            cout << 0;
        }
    }
    cout << endl;
}
/*一旦检测到3号信号,会走这里的处理逻辑,此时吧loop置false,使得对2号信号的处理逻辑结束。*/
void donesig2(int sig)
{
    cout << "get sig 3" << endl;
    cout << "loop = false, done sig2" << endl;
    loop = false;
}
void sigcb(int sig)
{
    loop = true;
    cout << "get a sig:" << sig << endl;
    while (loop)
    {
        sigset_t pending;
        sigpending(&pending);//函数接口:获取目前的pending位图
        Print(pending);//打印出pending位图,便于观察
        sleep(1);
        signal(3, donesig2);//检测3号信号------在处理信号的同时依然可以接受并处理信号
    }
}

int main()
{
    struct sigaction ac, oac;//一个结构体类型,内部存储有维护信号系统的一系列变量
    ac.sa_flags = 0;//暂时设为0

    /*sa_mask是一个sigset_t类型的位图(sigset_t是一个专门用于维护31个信号位的类型)
    此处这个函数的作用是把这个位图的所有位置全置0(初始化)
    但是这个位图目前还没有被设置进操作系统的信号位图*/
    sigemptyset(&ac.sa_mask);

    /*sa_handler是结构体内部的一个成员,是一个函数指针类型,需要用户自定义实现,
    也就是当进程接受到特定信号之后需要做的处理动作*/
    ac.sa_handler = sigcb;

    while (true)
    {
        //这是一个和上述的结构体类型同名称的一个函数
        //参数:(需要屏蔽的信号,需要设置结构体类型,老的结构体类型,目的是为了保存设置之前的数据,防止用户想要撤回操作)
        sigaction(2, &ac, &oac);//对2号信号进行特殊处理
        sleep(1);
        cout << "I am process:" << getpid() << endl;
    }

    return 0;
}

完~

未经作者同意禁止转载

相关推荐
风静如云19 分钟前
OpenBMC:BmcWeb定义service
linux
sszdzq33 分钟前
Docker
运维·docker·容器
book012136 分钟前
MySql数据库运维学习笔记
运维·数据库·mysql
leoufung40 分钟前
VIM FZF 安裝和使用
linux·编辑器·vim
bugtraq20212 小时前
XiaoMi Mi5(gemini) 刷入Ubuntu Touch 16.04——安卓手机刷入Linux
linux·运维·ubuntu
xmweisi2 小时前
【华为】报文统计的技术NetStream
运维·服务器·网络·华为认证
VVVVWeiYee2 小时前
BGP配置华为——路径优选验证
运维·网络·华为·信息与通信
陆鳐LuLu2 小时前
日志管理利器:基于 ELK 的日志收集、存储与可视化实战
运维·elk·jenkins
CodeWithMe2 小时前
[ Vim ] 常用命令 and 配置
linux·编辑器·vim
DC_BLOG2 小时前
Linux-GlusterFS进阶分布式卷
linux·运维·服务器·分布式