【Linux】进程信号

一、首先,用 kill -l 命令可以查看系统定义的信号列表:
这里写图片描述
每个信号都有一个编号和一个宏定义名称,这些宏定义可以在 signal.h 中查看,例如:#define SIGINT 2 。编号34以上的信号为实时信号。

二、信号常见的三种处理方式:

(1)忽略此信号。(SIG_IGN)
(2)执行信号的默认处理动作。(SIG_DFL)
(3)提供一个信号处理函数,捕捉信号进行自定义处理。

三、信号的产生:

1、常见的几种产生信号方式:

        a.用户产生信号
        b.系统异常产生信号
        c.自定义捕捉

2、通过终端按键产生信号:

SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理动作是终止进程,并且Core Dump

Core Dump (核心转储):当一个进程异常终止时,可以选择把进程的用户空间内存数据全部保存到磁盘上,文件名通常是Core,这叫做 Core Dump。


系统默认情况不予 Core 分配空间:
这里写图片描述
可使用命令 ulimit -c 1024 给Core 分配 1024个字节空间:
这里写图片描述
实例:写一个死循环

[lize-h@localhost 0409_Siganl]$ cat core_Dump.c 
#include<stdio.h>

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

这里写图片描述
3、调用系统函数向进程发信号:

(1)我们可以用 kill 命令向进程发信号:

[lize-h@localhost 0409_Siganl]$ ./a.out &     
[1] 3102
[lize-h@localhost 0409_Siganl]$ kill -SIGSEGV 3102
[lize-h@localhost 0409_Siganl]$ 
[1]+  段错误               (core dumped) ./a.out
[lize-h@localhost 0409_Siganl]$ cat core_Dump.c 
#include<stdio.h>
int main()
{
    //printf("pid is %d\n",getpid());
    while(1);
    return 0;
}

令程序在后台执行,用 kill 命令向进程发送信号,令进程异常终止,异常信息存入core文件。

(2)发送信号的函数有:

#include<signal.h>

int kill(pid_t pid,int signo);  //向任意进程发送任意信号;成功返回0,出错返回-1。
int raise(int signo);    //向自己发送任意信号;成功返回0,出错返回-1。

#include<stdlib.h>

void abort(void);   //向自己发送 abort 信号

(3)alarm函数:

#include<unistd.h>

unsigned int alarm( unsigned int seconds);
/*
调用alarm函数可设置一个闹钟;当参数seconds设置为0表示取消闹钟,返回值为以前设置的
时间剩余的秒数;当参数seconds设置为8表示8秒后触发闹钟,向当前进程发送SIGALRM信号,
该信号的默认处理动作是终止当前进程。
*/

四、阻塞信号

1、信号的相关概念:

(1)实际执行信号的处理动作称为信号递达。
(2)信号从产生到递达之间的状态,称为信号未决(pending)。
(3)进程可以选择阻塞(Block)某个信号。
(4)被阻塞的信号产生时将保持在未决状态,直到信号解除对此信号的阻塞,才执行递达的动作。
(5)阻塞和忽略不同,只要信号被阻塞就不会递达,而忽略是在递达后可选的一种处理动作。
(6)发送信号相当于更改PCB中的位图

2、在内核中的表示:
这里写图片描述
Linux信号的实现:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。

3、sigset_t 信号集:
未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略。

4、信号集操作函数:

#include<signal.h>

int sigemptyset(sigset_t* set); //初始化set所指向的信号集,使其中所有信号的对应bit清零。
int sigfillset(sigset_t* set);  //初始化set所指向的信号集,使其中所有信号的对应bit置1。
int sigaddset(sigset_t* set,int signo);  //在信号集中添加编号为signo的信号
int sigdelset(sigset_t* set,int signo);  //删除信号集中编号为signo的信号
   //以上四个函数成功返回0,出错返回-1。  
int sigismember(const sigset_t* set,int signo);  
//sigismember是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种信号,若包含则返回1,不包含返回0,出错返回-1。


sigprocmask函数:

调用该函数可以读取或更改进程的信号屏蔽字(阻塞信号集)

#include<signal.h>
int sigprocmask(int how,const sigset_t* set,sigset_t* oset);
//若调用成功返回0,出错返回-1

如果oset为空指针,则读取进程的当前信号屏蔽字通过oset参数传出。如果set是非空指针,则更改进程的信号屏蔽字,参数how指示如何更改。如果oset和set都是非空指针,则先将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。假设当前的信号屏蔽字为mask,以下为how参数的可选值。
这里写图片描述

sigpending函数:

#include<signal.h>
int sigpending(sigset_t* set)
//读取当前进程的未决信号集,通过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);
    gprocmask(SIG_BLOCK,&s,NULL);
    while(1)
    {
        sigpending(&p);
        printsigset(&p);
        sleep(1);
    }
    return 0;
}

运行结果:

[lize-h@localhost 0409_Siganl]$ ./a.out
10000000000000000000000000000000
10000000000000000000000000000000
10000000000000000000000000000000
10000000000000000000000000000000
10000000000000000000000000000000
^C10100000000000000000000000000000
10100000000000000000000000000000
10100000000000000000000000000000
10100000000000000000000000000000
^Z
[1]+  Stopped                 ./a.out
[lize-h@localhost 0409_Siganl]$ 

信号捕捉: https://mp.csdn.net/mdeditor/80044465

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lz201788/article/details/79968402
个人分类: 操作系统
上一篇一个简易的shell
下一篇【数据结构】图的模拟实现(临接矩阵法)
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭