Linux信号解析和心得

本文介绍了Linux中的信号机制,包括信号的定义、产生方式、种类和阻塞。信号作为进程间通信的一种方式,可通过终端按键、系统函数或软件触发。核心转储在程序异常终止时记录内存状态,有助于调试。文章还探讨了信号的阻塞流程和信号集操作,并通过示例程序进行验证。

1. 什么是信号?

网上给出的定义是:在计算机科学中,信号(英语:Signals)是Unix、类Unix以及其他POSIX兼容的操作系统中进程间通讯的一种有限制的方式。它是一种异步的通知机制,用来提醒进程一个事件已经发生。当一个信号发送给一个进程,操作系统中断了进程正常的控制流程,此时,任何非原子操作都将被中断。如果进程定义了信号的处理函数,那么它将被执行,否则就执行默认的处理函数。

简单的理解信号就是一种Linux环境下进程通讯的一种机制。

2. 信号怎么产生的?

信号一般是有以下3个方面产生:

  1. 通过终端按键产生信号
  2. 调用系统函数产生信号
  3. 软件产生的信号

1>终端按键就是我们在Linux中写程序是想要终止某个程序,就会按ctrl+c按键,还有类似ctrl+d、ctrl+\、ctrl+z等。
2>调用系统函数可以产生信号,比如
这里写图片描述

这里我们将一个死循环的进程放在后台,而我们可以使用一个kill 命令向该进程发送信号。

int kill(pid_t pid, int sig);
int raise(int sig);
//返回值:成功为0,失败为-1;

这里写图片描述

在上面的图上我们可以看到一个进程可以调用系统函数来发送信号。
3>有软件产生的信号,我们在管道的时候,有一个管道产生的信号SIGPIPE,这个就是软件信号。
还有我们的alarm函数,相当于定一个闹钟,时间到了会发送一个SIGALRM信号。

unsigned int alarm(unsigned int seconds);
//返回值:0或者是当前还剩余的秒数。

如图:
这里写图片描述

扩展:Core Dump(核心转储)
定义为:核心转储(core dump),在汉语中有时戏称为吐核,是操作系统在进程收到某些信号而终止运行时,将此时进程地址空间的内容以及有关进程状态的其他信息写出的一个磁盘文件。这种信息往往用于调试。

当程序运行的过程中异常终止或崩溃,操作系统会将程序当时的内存状态记录下来,保存在一个文件中,这种行为就叫做Core Dump(中文有的翻译成“核心转储”)。我们可以认为 core dump 是“内存快照”,但实际上,除了内存信息之外,还有些关键的程序运行状态也会同时 dump 下来,例如寄存器信息(包括程序指针、栈指针等)、内存管理信息、其他处理器和操作系统状态和信息。core dump 对于编程人员诊断和调试程序是非常有帮助的,因为对于有些程序错误是很难重现的,例如指针异常,而 core dump 文件可以再现程序出错时的情景。

而Linux操作系统默认不会产生core文件的,因为core文件有可能会包括用户的信息,不安全。而我们如果是在测试间断可以使用ulimie命令改变这个命令,使其产生core文件

ulimit -c 1024//创建1024大小的core文件,你以后生成的core大小就是这么大。

这里写图片描述

3. 信号有哪些?

在命令行中输入kill -l就可看到信号了:

这里写图片描述

我们可以看到共有1-31 34-64共62个信号,其中1-31是普通信号,34-64为实时信号。

4. 信号的阻塞

  • 信号相关的其他概念
    实际执行信号的动作叫“递达
    信号从产生到递达之间叫“未决
    信号可以被阻塞起来
    这个流程示意图如下:
    这里写图片描述
  • 信号在内核中的示意图

这里写图片描述

  • sigset_t信号集
typedef struct {
unsigned long sig[_NSIG_WORDS];
} sigset_t

我们从上面的图可以看到阻塞,未决都是由0 1 来控制他们是否被响应。

  • 信号集操作函数
       int sigemptyset(sigset_t *set);//初始化set指向的信号集,使所有的bit为清0,
                                             //表示该信号集不包含任何有效信号。

       int sigfillset(sigset_t *set);//初始化set指向的信号集,使所有的bit置位
                                           //表示该信号集的有效信号包含所有信号。

       int sigaddset(sigset_t *set, int signum);//修改pending表

       int sigdelset(sigset_t *set, int signum);

       int sigismember(const sigset_t *set, int signum);//得到信号集
//返回值,前四个函数成功为0。失败为-1;
//sigismember是一个布尔函数,用来判断某个信号集中是否包含某个信号,
//包含为1,不包含为0,出错为-1;
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
//这个函数可以读取或者更改进程的信号屏蔽字(阻塞信号集)
//返回值:成功为0,失败为-1;
int sigpending(sigset_t *set);
//读取该进程的未决信号集,通过set参数传出
//返回值:成功为0;失败为-1;

通过上述我们可以试着写个程序验证一下:

#include <stdio.h>
#include <unistd.h>
#include <signal.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 i,j;
    sigemptyset(&i);//定义信号集对象,并且清空初始化
    sigaddset(&i,SIGINT);//将ctrl+c定义成未决
    sigprocmask(SIG_BLOCK,&i,NULL);//将ctrl+c阻塞
    while(1)
    {
        sigpending(&j);//获取未决信号集
        printsigset(&j);
        sleep(1);
    }
    return 0;
}

效果图:
这里写图片描述

  • 信号的捕捉
    这里写图片描述
    如上图所示,信号捕捉是在内核态返回用户态时的自定义处理。
int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);
//signum是指定信号的编号
//act是指向你所需要修改的信号处理动作
//oldact是用来传出你之前的信号处理动作
//返回值:成功为0,出错为-1
struct sigaction {
               void     (*sa_handler)(int);
               void     (*sa_sigaction)(int, siginfo_t *, void *);
               sigset_t   sa_mask;
               int        sa_flags;
               void     (*sa_restorer)(void);
           };
int pause(void);
//只有EINTR 有信号到达中断此函数的执行。
//pause()会令目前的进程暂停(进入睡眠状态), 直到被信号(signal)所中断.
//返回值:只错误返回-1,成功不返回

.接下来我们自己写一个测试验证一下:

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

void my_sig(int signum)
{
    ;
}

unsigned int my_sleep(int num)
{
    struct sigaction act,oldact;//定义新旧信号结构体
    unsigned int unslept = 0;//
    act.sa_handler = my_sig;//修改alarm信号自定义函数
    sigemptyset(&(act.sa_mask));//将act信号集初始化
    act.sa_flags = 0;//信号标志位
    sigaction(SIGALRM,&act,&oldact);//修改闹钟函数的信号处理函数
    alarm(num);//定闹钟
    pause();//挂起
    unslept = alarm(0);
    sigaction(SIGALRM,&oldact,NULL);//将alarm的信号出库函数改回来
    return unslept;
}

int main()
{
    while(1)
    {
        my_sleep(2);
        printf("2秒时间到\n");
    }
    return 0;
}

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值