linux下的信号是怎么回事

信号的产生

Linux下信号这个概念可以来说是非常重要的,先来说下如何产生信号,然后在逐一解释:

  1. 键盘组合键
  2. 硬件异常错误
  3. 通过一些指令
  4. 软件条件
  5. 调用系统函数

1、键盘组合键这个很好理解,下面以一个简单的实例来说明。

#include<stdio.h>

int main()
{
    printf("pid is: %d\n",getpid());
    while(1);
    return 0;
}

这里写图片描述
程序是一个死循环,当这个程序在运行的时候,你的其它指令是不起作用的,那么可以通过Ctrl+c和ctrl+\来终止到这个进程。这里需要注意的是,键盘组合键只对前台程序起作用。ctrl+c产生SIGINT信号,ctrl+z产生SIGTSTP信号,ctrl+\产生SIGQUIT信号。
Linux下的信号可以通过kill -l命令查看:

这里写图片描述
这些信号里面34号以上的信号都为实时信号,这里先讨论前面的基础信号。

再回到上面的例子,当一个进程收到SIGQUIT这个信号时,会终止该进程并且core dump(段错误)解释下core dump,当一个进程异常终止时,会将内存数据全部保存在磁盘上,文件名为core,那么上面的程序我们来试一下。
这里写图片描述
结果并没有产生core文件,那么看下面的东西:

通过这个命令可以查看一些内存分配问题

ulimit -a

列表内容
第一行,默认core文件的大小是0,所以没有生成core文件,可以修改core的值。

ulimit -c 1024

给core文件默认分配1024大小,这样,然后再次Ctrl+\。
这里写图片描述
可见生成了core文件,后缀是该进程的PID,细心的你应该发现了,这个core文件特别的大,所以默认情况下才不会生成这个东西。


2、硬件错误的话,因为Linux下一切皆文件的思想,当你的硬件出问题后,操作系统就会收到该硬件文件发来的信号,这个没什么太大的问题。


3、 通过指令直接产生信号,比如杀死一个进程,直接调用kill -9 后面跟上要删除进程的PID,这样直接就产生信号杀死该进程。


4、由软件条件产生的信号,如SIGPIPE,SIGALRM
具体的alarm函数可以参见alarm实现sleep这篇文章


5、调用系统函数产生,下面介绍一下相关函数:

#include<signal.h>
int kill(pid_t pid,int signo);
int raise(int signo);

kill指令其实也是通过kill函数来实现的,而raise函数可以给当前进程发送指定的信号(可以自己给自己发信号)
成功返回0值,错误返回-1;

#include<signal.h>
void abort(void);

abort函数时当前进程收到信号而异常终止,类似于exit函数一样,该函数总是会成功的,所以没有返回值。


信号集函数

接下来到了信号重点中的重点了,在实际执行信号动作时,称为信号递达。信号从产生到递达之间的状态,称为未决(pending),进程也可以被堵塞,即被堵塞的信号将一直处于未决状态,直到进程解除对该信号的阻塞。这里需要注意的是,阻塞和忽略是不同的,因为阻塞就永远不会被递达,而忽略是在递达后作出的一种处理。
下面介绍信号集操作函数:

#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);//判断是否含有有个信号

信号屏蔽字

#include<signal.h>
int sigprocmask(int how,const sigset_t *set,sigset_t *oset);

成功返回0,错误返回-1;
how为如何更改,oset和set都是非空指针,现将原来的信号屏蔽字备份到oset里。

读取未决信号集

#include<signal.h>
int sigpending(sigset_t *set);

读取当前进程的未决信号集,成功返回0,出错返回-1。

以下面的代码来更好的理解上面这些函数:

#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void printsigset(sigset_t *set)
{
    int i = 0;
    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;
}

这里写图片描述
这里我们阻塞了SIGINT信号,使该信号处于未决状态。所以ctrl+c并没有终止该进程,只能另起一个终端直接kill掉了。
如下:
这里写图片描述


捕捉信号

对于信号的处理动作有忽略,默认,和自定义。捕捉信号首先得先要识别该信号,并且要知道怎么处理该信号。
信号的整个捕捉过程如下:

  1. 用户注册SIGQUIT信号,处理sighandler
  2. 正在执行main函数,发生中断或异常切入内核态
  3. 处理完异常,返回用户态前检查有否有信号递达
  4. 返回用户态后不是先继续main函数,而是执行sighandler函数,由于存在不同的堆栈,所以是独立的控制流程
  5. sighander函数返回后执行特殊的系统调用sigreturn再次进入内核态
  6. 如果没有信号递达了就返回至main函数继续上下文执行
    这里写图片描述

这里可以参考实现mysleep函数的实现代码理解。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值