欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【Linux】进程信号

程序员文章站 2022-07-12 11:18:01
...

一、首先,用 kill -l 命令可以查看系统定义的信号列表:
【Linux】进程信号
每个信号都有一个编号和一个宏定义名称,这些宏定义可以在 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 分配空间:
【Linux】进程信号
可使用命令 ulimit -c 1024 给Core 分配 1024个字节空间:
【Linux】进程信号
实例:写一个死循环

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

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

【Linux】进程信号
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】进程信号
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参数的可选值。
【Linux】进程信号

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