来来来,进程信号了解一下

本篇博客将重点介绍一下进程信号的基本概念以及相关知识点。
在开始之前,我们先来看一张图:
这里写图片描述
先大体有些印象,然后我们开始后面的介绍。

信号的概念

我们通过一个熟悉的场景来理解一下什么是信号:

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

根据上面的例子,我们可以总结一下究竟什么是进程信号呢?
进程信号就是操作系统发给某个进程的一些信息。
我们需要注意的是,像Ctrl+c这样的信号只能发给前台进程。而shell可以同时运行一个前台进程和多个后台进程。前台进程在运行过程中随时可能收到Ctrl+C这样的信号,也就是说信号相对于进程的控制流程是异步的。
在Linux中我们可以通过kill -l 来查看信号列表:
这里写图片描述

如上图所示每个信号都有一个编号和一个宏定义名称,这些宏定义可以在singal.h中找到,34号信号之后我们称之为实时信号,这里先不讨论。关于上图这些信号的详细信息在signal(7)都有详细说明。
这里写图片描述

这里写图片描述

信号常见的处理方式:
1、忽略此信号
2、执行该信号的默认处理动作
3、提供一个信号处理函数,要求内核在处理该信号是切换到用户态执行这个处理函数,这种方式称为捕捉函数。

产生信号

1、通过终端按键产生信号
SiGINT的默认处理动作是终止进程,SiGQUIT的默认处理动作时候终止进程并产生Core Dump。
Core Dump就是核心转储,当一个进程终止时,可以选择把进程的用户空间内存数据全部保存到磁盘上,文件名通常是core,在bug发生之后可以通过调试器检查core文件查清数据文件,这叫做事后调试。一个进程能产生多大的core文件取决于进程的Resource Limit(这个信息保存在PCB中),在使用前通常使用命令 ulimit -c 1024。
使用core文件的命令是:gdb a.out core

但是core代码只允许在调试过程中使用,平时使用时不允许产生core文件的原因有两个:
1、core文件中可能包含用户的密码等敏感文件,所以在产品上线之后绝对不允许产生core文件。
2、服务器在产生bug之后通常的做法是由电脑立即重启服务,如果允许产生core文件,可能在一瞬间将把磁盘写满,导致服务器整个崩溃。

所以core文件慎用!

调用系统函数向进程发送信号
这里介绍两个向进程发送信号的系统函数

kill 命令是调用kill函数实现的。kill可以向指定进程发送指定信号。raise可以向当前函数发送指定的信号(自己给自己发信号)
#include <signal.h>
int kill(pid_t pid , int signo);
int raisr( int signo);

abort函数使当前进程收到信号而异常终止
#include<stdlib.h>
void abort(void);

像exit一样,abort函数总会成功,所以没有返回值

由软件条件产生的信号
SIGPIPE是一种由软件条件产生的信号,之前在管道已经介绍过,这里我们介绍另外一种alarm函数和SIGALRM信号。

#include<unisted.h>
unsigned int alarm(unsigned int seconds);
调用alarm函数可以设定一个闹钟,也就是告诉内核
在seconds秒之后向当前进程发送SIGALRM信号,该
信号的默认动作是终止当前进程。

这里写图片描述
这个函数的作用就是在1s内不停地计数,直到被SIGALRM信号终止。

阻塞信号

1、信号其他常见概念

  • 实际执行信号的处理动作称为信号递达(Delivery)。
  • 信号从产生到递达之间的状态,称为未决信号(Pending)。
  • 进程可以选择阻塞(block)某个信号.
  • 被阻塞的信号产生时将保持在未决状态,直到进程接触对此信号的阻塞,才执行抵达动作。
  • 阻塞和忽略是不同的,只要信号被阻塞就不会抵达,而忽略是信号递达之后可选的一种处理动作。

信号在内存中的表示方式
信号在内核中表示示意图:
这里写图片描述

从上图可以看到,每个信号只有一个bit未决标志,非0即1,阻塞标志也是这样表示,所以,未决和阻塞标志可以用sigset_t来存储,sigset_t称为信号集。阻塞信号集也叫当前进程的信号屏蔽字(signal Mask)。

信号集的操作函数
#include <signal.h>
int sigemptyset(sigset_t* set);
int siggillset(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);
  • 函数sigemptyset和sigfillset函数都是初始化set所指向的信号集,前者置1,后者置位。
  • 在使用sigset_t变量之前,一定要调用,sigemptyset或者sigfillset初始化,使信号集处于确定的而状态。
  • sigismember是一个布尔函数,用于判断某个信号是否在信号集中。

sigprocmask
用于读取或更改进程的信号屏蔽字。

#include<signal.h>
int sigprocmask(int how, const sigset_t* set, sigset* oset);
成功返回0,失败返回0

SIG_BLOCK set指向的信号集包含我们希望添加到当前信号屏蔽字的信号。
SIG_UNBLOCK set指向的屏蔽字中解除阻塞的信号
SIG_SETMASK 设置当前信号屏蔽字为set所指向的值

sigpending

#include <signal.h>
sigpending
读取当前信号的未决信号集,通过set参数传出。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void printsigset(sigset_t* set){
    int i = 1;
    for(;i < 32; i++){
        if(sigismember(set, i)){
            putchar('1');
        }else{
            putchar('0');
        }
    }
    puts("");
}

int main(){
    sigset_t s,p;
    sigemptyset(&s);
    sigaddset(&s, SIGINT);
    sigprocmask(SIG_BLOCK, &s, NULL);
    while(1){
        sigpending(&p);
        printsigset(&p);
        sleep(1);
    }
    return 0;
}

这里写图片描述
我们看到当按下ctrl+c后操作系统向进程发送了2号信号SIGINT,而我们已经将2号信号加到信号屏蔽字当中,因此当进程收到2号SIGINT信号时将使该信号处于未决状态。

捕捉信号

还记得我们在一开始看的那张图吗?
这里写图片描述
如果信号的处理动作是用户自定义函数,在信号递达是就调用这个函数,这称之为信号捕捉
我们来举一个栗子:
用户程序注册了SIGQUIT信号的处理函数sighandler。当前代码正在执行main函数,这时发生了中断或异常切换到内核态。在中断处理完毕之后,要返回用户态的main函数继续执行之前,检测到有信号SIGQUIT递达。内核决定返回用户态之后不是继续执行main函数的上下文,而是执行sighandler函数。sighandler函数和main函数使用不同的堆栈空间,因此两者之间没有调用与被调用的关系,是两个独立的控制流。

信号捕捉常用的函数有:
sigaction

#include<signal.h>
int  sigaction(int signo, const struct sigaction* act, struct sigaction* oact);
  • sigaction函数可以制定和修改指定信号的相关处理动作。调用成功则返回0,失败则返回-1。signo是指定信号的编号。若act指针飞空,则根据act修改信号的处理动作。若oact指针非空,则通过oact传出原来信号的处理动作。act和oact指向sigaction结构体。
  • 将sahandler赋值为常数SIFIGN传给sigaction表示忽略信号,赋值为常数SIF_DEL表示执行默认动作,赋值为一个函数指针表示自定义捕捉函数。

pause

#include<unisted.h>
int pause(void);

pause函数使调用进程处于挂起直到有信号递达。

由于本人能力有限,上面的介绍和代码可能有不妥或者BUG欢迎发邮件到我的邮箱(Cyrus_wen@163.com)批评指正!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值