Linux下 进程信号

本文介绍了信号在操作系统中的概念,如SIGINT、SIGTSTP和SIGQUIT等,以及信号的处理方式,包括默认处理、忽略处理和自定义处理。信号分为非实时和实时两种,非实时信号可能丢失,而实时信号则不会。信号的注册和注销涉及位图修改,实时信号注册会始终添加到队列。文章还提到了信号阻塞和如何避免子进程成为僵尸进程,以及volatile关键字在内存可见性中的作用。
摘要由CSDN通过智能技术生成

信号的概念:信号是一个软件中断(信号灯)

只是告诉有这样一个信号,但是具体信号怎样处理,啥时候处理由进程决定,所以是软中断

信号的产生

ctrl+c :
  2号信号 SIGINT

ctrl+z : 
  20号信号 SIGTSTP

ctrl+ | : 
   3号信号 SIGQUIT
kill -l  //看一下操作系统  的   信号定义

kill  -[信号值]  pid  //可以让该进程执行该信号

kill 函数

 raise函数   //谁调用给谁发送信号

 信号的种类

非可靠信号(非实时信号)

1~31    //可能会丢失信号

可靠信号 (实时信号)

34~64  //一定不会丢失信号

总共有 62个信号

 信号的处理方式

操作系统对信号的处理方式(man 7 signal)

 term core cont ign stop

默认处理方式:SIG_DFL//操作系统已经定义好信号的处理方式
忽略处理方式:SIG IGN//该信号为忽略处理


//面试官问我们僵尸进程如何产生

//我们的回答:子进程先与父进程退出,并告知父进程退出状态信息,发送了SIGCHLD信号,父进程对号
//忽略处理

信号的注册

一个进程收到一个信号,这个过程称之为注册(内核给我们进程的信号)

信号的注册和注销并不是一个过程,是两个独立的过程

信号的注册:信号对应的比特位修改成为 1 ,添加sigqueue节点到sigqueue队列当中

区分:
          //修改位图就是将sig数组的比特位进行修改(比如发送2号信号,那就修改2号信号比特位)
     非实时信号
         第一次注册
               修改sig位图 (0-1) , 添加signqueue节点
                
                eg :17号信号的比特位从0改1 , 并添加signqueue节点

         第二次注册
               前提:同一个非实时信号,在前一个信号未被处理时再次注册会修改位图(0-1),但是不会添加sigqueue节点
         
         //那末后面这个信号就会被注释掉//这也就是为啥非实时信号会丢失的缘故



-------------------------------------------------------------------------------------------

实时信号注册
   
  第一次注册修改位图(0-1),添加signqueue节点

  第二次注册修改位图(1-1),添加signqueue节点

信号注销

 信号的自定义处理方式(让收到的信号变为我们自己程序员自己定义的函数以达到我们期望实现的功能)

 

9号信号不能被自定义处理


//如果操作系统所有的信号都被我们程序员自定义处理了
//我们自定义的函数又啥都不干,操作系统一点办法都没有
//操作系统规定9号进程不能被自定义处理

kill -9 pid  依然可以强制杀你这个进程

 

 

sched.h  // 操作系统内核代码

小技巧 

 处理进程信号的时机

我们写的代码让它开始跑,创建进程(并且操作系统给它一个信号)开始它是在用户态的等到我们需要内核为我们执行某些功能时我们进入到内核中,内核中执行完功能,我们返回用户态,这时调用do_signal函数
看一下有别的信号没

(1.默认处理2.忽略处理)
(均在操作系统中),执行信号,再do_signal若无信号回到用户态继续执行代码






(3.用户自定义处理方式在用户态下完成)执行完处理方式代码
回到操作系统内核 do_signal...

 信号阻塞

int sigemptyset(sigset_t *set); // 清空位图, 将比特位全部置为0


int sigfillset(sigset_t *set); // 将比特位全部置为1


int sigaddset(sigset_t *set,int signum): //将某个信号对应的比特位置为1


int sigdelset(sigset_t *set,int signum); //将某个信号对应的比特位置为0


int sigismember(const sigset_t *set, int signum); // 判断某个信号是否在set当中,其实就是判断某个信号的比特位是否为1

 

对位图的修改的函数

int sigemptyset(sigset_t *set); // 清空位图, 将比特位全部置为0

int sigfillset(sigset_t *set); // 将比特位全部置为1

int sigaddset(sigset_t *set,int signum); //将某个信号对应的比特位置为1

int sigdelset(sigset_t *set,int signum); //将某个信号对应的比特位置为0

int sigismember(const sigset_t *set, int signum); // 判断某个信号是否在set当中,其实就是判断某个信号的比特位是否为1

接口

int sigprocmask(int how, const sigset t *set, sigset t *oldset)

how: 想让sigprocmask做什么事情

SIG_BLOCK: 设置某个信号为阻塞状态

SIG_UNBLOCK : 设置某个信号为非阻寒状态

SIG_SETMASK : 用第二个参数“set”, 替换原来的阻塞位图。 (替换的意思)

set : 新设置的阻塞位图

oldset : 原来老的阻塞位图









原理解析:

当how为SIG_BLOCK时, 函数会根据set, 计算新的阻塞位图, 方式为:block(new)=block (old) | set:

当how为为SIG_UNBLOCK时,函数会根据set,计算新的阻塞位图,方式为:block(new) = block(old)|(~set):

当how为为SIG_SETMASK时,函数会根据set,计算新的阻塞位图, 方式为:block(new) = set:

 

//可以看得出非实时信号2号信号,只处理了1次发生了信号丢失

父进程如何避免子进程成为僵尸进程

处理方法:

子进程退出时给父进程发送SIG_CHLD信号,对该信号我们自己定义处理方式(使用wait函数)
回收子进程的退出状态信息

//避免父进程啥都不干只为了等待子进程的退出(解放父进程避免父进程ri han gan)

 volatile关键字

作用:
    保持内存可见性


计算机cpu在执行代码时,从寄存器中读取数据,而非内存(缓和速度矛盾)

volatile 确保它一定读的是我们内存中的数据//内存--》寄存器--》cpu

 插入gcc/g++优化编译 “-O0”  "-O1"   "-O2"   "-O3"数字越高级别越高,优化越高,代码执行速度越快

//优化等级高的话我们一些数据是不会从内存中读取而是直接读寄存器的值
                                                                                                    #include<stdio.h>
     #include<unistd.h>
     #include<signal.h>
   volatile int val=1;
    void callback(int sig)
     {
       printf("signal=%d had been revised\n",sig);
       val=0;
     }
 void main()
    {
      signal(2,callback);
      while(val)
      {
        printf("i am sleep\n");
        sleep(1);
      }
      printf("hhhh jump down\n");                                                                                              
   }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值