【Linux】进程 信号

基本概念

软件中断:
通知一个进程发生了某个事件,打断进程当前操作,去处理这个事件

信号:多种多样且能够被识别的

查看信号种类

kill -l
62 种信号
1 ~ 31 :非可靠信号,非实时信号
34 ~ 64:可靠信号,实时信号

在这里插入图片描述

信号的生命周期

信号的生命周期:产生、注册、注销、处理、阻塞

产生

硬件产生

ctrl+c:终止一个进程
ctrl+z:停止一个进程,只是暂停运行
ctrl+\:quit

软件产生

(1)kill 指令

kill 指令
kill 进程号:杀死一个进程,本质上是给指定进程发送一个终止信号,而进程收到信号后,对信号的处理就是退出运行

在这里插入图片描述

int kill(pid_t pid,int signum);
给指定进程发送一个指定的信号值

getpid() :获取当前程序进程号

在这里插入图片描述
在这里插入图片描述

bt:查看函数调用栈

(2)raise

int raise(int signum);
给进程自身发送一个指定的信号值

(3)abort

abort();
给进程自身发送一个 SIGABRT 信号
在这里插入图片描述

(4)alarm

int alarm(int sec);
sec 秒之后给进程发送 SIGALRM 信号
定时器,时间到了之和给进程自身发送一个 SIGALRM 信号值

在这里插入图片描述

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

void mysleep(int n)
{
  alarm(n);     //设置 alarm 信号
  pause();
}

void sigcb()
{
  printf("3s 已经结束!\n");
}

int main()
{
  signal(SIGALRM,sigcb);  //信号中断
  
  mysleep(3);       // alarm 

  while(1){            //此时进行了信号中断操作,因此会继续执行下列代码
    printf("use alarm!\n");
    sleep(2);
  }

  return 0;
}

在这里插入图片描述

有个进程 KILL 杀不死,原因是什么?

(1)当前进程是一个僵尸进程

(2)可能是停止状态(T)----- 对信号不出来

(3)信号可能被阻塞,或者被自定义处理了。

注册

让一个进程能够知道自己收到了某个信号-----在进程中做记号

在进程 pcb 中有个 sigpending 未决信号集合(是个位图);
进程的 PCB 中有个 sigqueue 链表,添加收到的信号链表,收到一次信号添加一个节点。

未决信号---------还没有被处理的信号
在这里插入图片描述

非可靠信号

若没有收到这个信号则注册一下(添加一个新节点,位图置为 1),若之前已经收到了这个信号还未处理则什么都不做(丢弃)

可靠信号

不管位图是否为 1(是否已经注册,还没处理),都会添加一个信号节点

flag 1 : 将一个进程调到前台运行

注销

在信号被处理之前,消除信号存在的痕迹(主要是防止信号被重复处理)

(1)非可靠信号的注销

删除信号的信息节点,位图置为 0

(2)可靠信号的注销

删除信号的一个信息节点,当没有相同节点则位图置为 0

处理

调用信号的事件处理函数

三种信号处理方式

(1)默认处理:系统中已经预定好的处理方式

(2)忽略处理:空的处理方式

(3)自定义处理方式:自己定义一个处理函数,替换掉处理函数

接口

sighandler_t signal(int signum,sighandler_t handler);

typedef void(*sighandler_t)(int); //函数

handler:
SIG_DFL:信号默认处理方式
SIG_IGN:忽略处理

功能:使用 handler 函数,替换掉 signum 信号当前的处理函数-----意味着进程收到 signum 信号,则使用 handler 函数进行处理

练习:
在这里插入图片描述

发现当前程序运行使用 ctrl+c 中断不起作用------因为使用的信号中断----------- ctrl + / 退出 或者 kill (ps -ef | grep signal)

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

自定义处理方式的信号捕捉流程

在这里插入图片描述

阻塞

信号依然会注册,但是暂时不处理(直到被解除阻塞)

进程 PCB 中有个 block 阻塞信号集合,哪个信号被添加到了 block 集合中,就表示如果收到了也暂时不处理

接口

int sigprocmask(int how,sigset_t *set,sigset_t *old);

(1) how:要对 PCB 中信号阻塞集合进行的操作
SIG_BLOCK:block |=set ; 将 set 集合中的信号添加到 block 中
SIG_UNBLOCK:block &=~set ;将 set 中的信号解除阻塞
SIG_SETMASK:block=set
(2)old:将修改前 block 集合中的信息添加到 old ,便于还原
返回值:失败返回 -1,成功 0

#include <signal.h>

   int sigemptyset(sigset_t *set);    //清空集合 set

   int sigfillset(sigset_t *set);          //将所有信号加载到 set 集合

   int sigaddset(sigset_t *set, int signum);   //将某一个信号添加到 set 

   int sigdelset(sigset_t *set, int signum);   //删除 set 中某个信号

   int sigismember(const sigset_t *set, int signum);   //查找某个信号

代码练习:

  1 #include<stdio.h>                                                     
  2 #include<signal.h>
  3 #include<unistd.h>
  4 #include<stdlib.h>
  5 
  6 void sigcb(int no)
  7 {
  8   printf("no = %d\n",no);
  9 }
 10 
 11 int main()
 12 {
 13   signal(SIGINT,sigcb);   //修改 2 号信号处理方式-----------非可靠信号    ,多次收到但只会注册一次
 14   signal(SIGRTMIN+5,sigcb);  //修改 39 号信号的处理方式----------可靠>    信号,多次收到就会注册多次
 15 
 16   sigset_t set,old;
 17   sigemptyset(&set);  //清空集合
 18   sigemptyset(&old);
 19   sigfillset(&set);  //将所有信号填充到 set 集合
 20 
 21   sigprocmask(SIG_BLOCK,&set,&old);  //信号阻塞
 22   printf("按回车继续运行!\n");
 23   getchar();
 24 
 25   sigprocmask(SIG_UNBLOCK,&set,NULL);   //解除阻塞
 26  // sigprocmask(SIG_SETMASK,&old,NULL); //还原旧信号
 27 
 28   while(1)
 29     sleep(1);
 30   return 0;
 31 }                      

在这里插入图片描述

实时信号先进行处理,非实时信号后处理

注意

两个特殊信号--------不可被阻塞,不可被自定义,不可被忽略:

kill -9 信号编号:杀死信号
kill -19 信号编号

信号应用

(1)自定义或忽略 SIGPIPE 信号
管道所有读端被关闭,继续 write 会触发异常
(2)自定义或忽略 SIGCHLD 信号
子进程退出时给父进程发送信号,然后成为僵尸进程,只不过因为信号默认处理为忽略,因此子进程退出并没有得到处理--------------进程等待 wait
signal(SIGCHLD,SIG_IGN);

关键字 volatile

作用:修饰一个变量,保持变量的内存可见性。

要求 CPU 在进行变量处理时候,每次都重新到内存获取数据进行处理

编译器若认为某个值 cpu 经常访问到,多次从内存读取太慢--------编译器优化:直接将这个值放在寄存器当中,每次不需要从内存中读取数据--------------但这种做法有时候是不合理的

函数重入

一个程序的运行,可能存在多个执行流程
若一个函数同时在多个执行流程中,进入执行,就叫做函数重入

可重人

一个函数在多个执行流程中重入之后,并不会产生一些异常或预期之外的情况。

不可重入

一个函数在多个执行流程中重入之后,有可能会产生一些数据二义性、导致产生预期之外的结果。

    1 #include<stdio.h>                                                   
    2 #include<signal.h>
    3 #include<stdlib.h>
    4 #include<unistd.h>
    5 
    6 int a=1,b=1;
    7 void test(char* str)
    8 {
    9   printf("%s----start-------\n",str);
   10   a++;
   11   b++;
   12   printf("%s-%d\n",str,a+b);        //产生数据二义性
   13  
   14   printf("%s-----end-------\n",str);
   15 }
W> 16 void sigcb(int no)
   17 {
W> 18   test("sigcb");
   19 }
   20 int main()
   21 {
   22   signal(SIGINT,sigcb);
W> 23   test("main");
   24   sleep(2);
   25   return 0;
   26 }              

在这里插入图片描述

ps:
欢迎评论留言哦~~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值