Linux信号

目录

前言

 一、信号的产生

1.1 热键

1.2 kill函数

1.3 异常出错

1.4 软件条件

 二、信号保存(阻塞信号)

2.1 信号过程状态

2.2 信号在内核的表示

2.3 信号阻塞设置

 三、信号捕捉

3.1 进程状态(用户&内核)

3.2 内核级内存空间

3.3 内核态中捕捉信号

3.3.1默认处理信号进程用户状态变化

3.3.2 自定义方式处理信号进程用户状态变化

 3.4 信号处理

3.4.1 signal函数

 3.4.2 sigaction函数

四、信号相关题目

Q1:一个进程无法被kill杀死的可能有哪些?[多选](ABCD)

Q2:以下描述正确的有:(B)


前言

学习信号之前,我们联系一下生活中有哪些信号?比如红绿灯告诉你行驶条件、上课老师朝你瞪了一眼“暗示”你别东张西望、烽火狼烟告知边境战火等等。这些信号本身具有通知作用,告诉我们接下来应该怎么做(1.默认处理 2.自定义 3.忽略 ),做之前前提是我们对信号有所认识。带着这些生活的基本常识,我们联系计算机术语,开始学习Linux信号,深入了解信号的产生、保存、处理。


Linux信号种类: 

Linux信号_第1张图片

 一、信号的产生

1.1 热键

当我们运行程序后按下 ctl + c 组合键,程序会终止!这里我们的组合键OS会识别成2号信号!2号的默认处理方式是终止前台进程。


1.2 kill函数

Linux信号_第2张图片

通过命令行参数获取kill参数:

Linux信号_第3张图片

基于kill,又衍生出raise、abort函数,他们本质是回调kill,只不过pid确认为本进程!

raise:可以给自己本进程发送任意信号

abort:可以给自己本进程发送指定的6号信号 


 1.3 异常出错

我们常遇到÷0错误、野指针非法访问

当我们除零,OS会发出8号SIGFPE信号默认终止进程!

OS怎么知道我们除零了呢?--》硬件CPU内有状态寄存器,如果CPU发现÷0则将该寄存器内的位图溢出标记位设置为1!OS检查溢出标记位发现其为1-》发送8号信号!

当我们非法访问,OS会发出11号SIGSEGV信号终止进程!


1.4 软件条件

基于代码满足某些条件时就会触发信号!

定时器软件条件:

#include
#include
#include

int cnt = 0;
static void Handler(int signo)
{
    std::cout<<"我收到了一个信号,cnt = "<

Linux信号_第4张图片


 二、信号保存(阻塞信号)

2.1 信号过程状态

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

2.2 信号在内核的表示

Linux信号_第5张图片

 每个信号都有两个位图标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。在上图的例子中,SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作。

2.3 信号阻塞设置

 相关函数:http://t.csdn.cn/ufns9

#include
#include
#include

#define BLOCK_SIGNAL 2
#define MAX_SIGNUM 31 
static void show_pending(const sigset_t &pending)
{
    for(int signo=MAX_SIGNUM;signo>=1;signo--)
    {
        if(sigismember(&pending,signo))
            std::cout<<"1";
        else std::cout<<"0";
    }
    std::cout<<"\n";
}
int main()
{
    sigset_t block,oblock,pending;
    //1.1初始化 必须要!
    sigemptyset(&block);
    sigemptyset(&oblock);
    //1.2 添加要屏蔽的信号 这里我屏蔽2号信号
    sigaddset(&block,BLOCK_SIGNAL);
    //1.3 开始屏蔽,设置进内核
    sigprocmask(SIG_SETMASK,&block,&oblock);

    //2.遍历打印pending信号集
    while(true)
    {
        sigemptyset(&pending);

        //获取信号集
        sigpending(&pending);


        //打印位图
        show_pending(pending);

        sleep(1);
    }
    return 0;
}

 Linux信号_第6张图片


 三、信号捕捉

3.1 进程状态(用户&内核)

进程状态分为内核态与用户态,我们一般在没有申请内存资源或者访问底层硬件时的状态是用户态,当我们使用系统调用时,OS会将我们的进程状态由用户态切换为内核态,此时这个切换的过程是有时间成本的!

可以很明显由下列图中看出系统调用可以决定用户状态的切换

Linux信号_第7张图片

但需要注意的是系统调用并不是唯一的切换条件:

Linux信号_第8张图片


 3.2 内核级内存空间

每个进程都会向OS申请4G的物理内存空间,其中用户态内存每个进程独有3G空间,都有独立的页表映射!但内核态内存空间是每个进程所共有的,内核空间页表映射是只有一份被所有进程所共有的!

Linux信号_第9张图片


 3.3 内核态中捕捉信号

当我们因为系统调用由用户态进入内核态后,系统将会进行查看信号的位图结构,如果是处于未达且未阻塞,那么OS会进行对该信号的捕捉,执行信号的处理方式!当信号被捕捉后,信号位图状态由1->0!

3.3.1默认处理信号进程用户状态变化

Linux信号_第10张图片

在CPU内,有一个寄存器作用是判定进程用户状态!


3.3.2 自定义方式处理信号进程用户状态变化

Linux信号_第11张图片


 3.4 信号处理

3.4.1 signal函数

Linux信号_第12张图片


 3.4.2 sigaction函数

Linux信号_第13张图片

 sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回0,出错则返回- 1。

signo是指定信号的编号。若act指针非空,则根据act修改该信号的处理动作。若oact指针非 空,则通过oact传出该信号原来的处理动作。act和oact指向sigaction结构体

当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么 它会被阻塞到当前处理结束为止。 如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。

#include
#include
#include
#include
using namespace std;

void Count(int cnt)
{
    while(cnt)
    {
        printf("cnt: %2d\r",cnt);
        fflush(stdout);
        cnt--;
        sleep(1);
    }
    printf("\n");
}
void handler(int signo)
{
    int cnt = 10;
    cout << "get a signo: "<


四、信号相关题目

Q1:一个进程无法被kill杀死的可能有哪些?[多选](ABCD)

A.这个进程阻塞了信号

B.用户有可能自定义了信号的处理方式

C.这个进程有可能是僵尸进程

D.这个进程当前状态是停止状态

  A正确 信号被阻塞,则暂时不被处理(SIGKILL/SIGSTOP除外,因为无法被阻塞,这里说的是可能性,因此不做太多纠结)

  B正确 自定义处理之后,信号的处理方式有可能不再是进程退出

  C正确 僵尸进程因为已经退出,因此不做任何处理

  D正确 进程停止运行,则将不再处理信号


Q2:以下描述正确的有:(B)

A.未决信号指的是已经被处理的信号

B.未决信号指的是还未被处理的信号

C.同一个信号可以在未决信号集合中添加多次

D.每一个信号处理完毕后都会从pending集合中移除

未决信号指的是,进程收到了信号,被添加到未决信号集合中,但是暂时还没有被处理的信号。

非可靠信号在进行注册时,会查看是否已经有相同信号添加到未决集合中,如果有则什么都不做,因此非可靠信号只会添加一次,因此处理完毕后会直接移除(准确来说是先移除,后处理)。而可靠信号会重复添加信号信息到sigqueue链表中,相当于可靠信号可以重复添加,处理完毕后,因为有可能还有相同的信号信息待处理,因此并不会直接移除,而是检测没有相同信号信息后才会从pending集合中移除

你可能感兴趣的:(Linux,linux)