[Linux]进程信号

概述:

进程信号是一种中断机制,或者说是一种事件通知机制,这里指的是软件中断,通过信号通知进程发生了某个事件,打断进程当前操作,去处理事件。
一个信号对应一个事件,并且信号必须能够被识别

进程信号的分类

kill -l 命令可以查看所有信号

在这里插入图片描述

信号一共有62钟:(没有32和33号信号)
1~31号:非可靠信号
34~64号:可靠信号

信号的生命周期

信号的生命周期包括:产生注册注销处理

产生:

        硬件产生:ctrl+c (SIGINT) 、 ctrl+\ (SIGQUIT)、ctrl+z(SIGSTOP----挂起)

        软件产生:kill命令
             示例:kill -sigid pid 向pid进程发送 sigid信号,如果不指定则默认发送15号终止信号
             kill杀死进程的原理就是向进程发送一个终止信号,进程处理该信号的方式就是退出进程
代码操作:

int kill(pid_t pid ,int sig); 给指定进程发送指定信号
int raise(int sig); 给调用该接口的进程发送进程信号
unsigned int alarm(unsigned int seconds); 设置一个定时器,等时间到了就给进程发送SIGALRM信号-----退出进程
void abort(void); 给进程发送一个SIGABRT信号,使异常进程终止

注册:

注册:让进程能够知道自己收到了哪个信号

原理:修改pcb中的未决信号集合位图,并且添加信号信息结点

pending-----未决信号集合:没有被处理的信号集合,是一个位图,用于标记有哪些信号待处理
sigqueue-----双向链表:用于添加信息结点,相同的结点有多少个就表示有多少个相同信号待处理

非可靠信号:如果信号没有注册,则注册;如果已经注册,则不再注册(只有一个结点)
可靠信号:不管信号有没有注册,都会注册一次(可以有多个结点)

注销

注销:删除信号的痕迹,即删除信号的结点和位图

非可靠信号:删除信息节点后直接重置位图
可靠信号:删除一个信息结点后,确定没有相同结点了,才重置位图

处理

处理:打断进程当前操作,执行收到的信号的处理函数,执行完毕后,返回原来的主控流程,继续运行

处理方式:默认处理方式、忽略处理方式、自定义处理方式;
默认处理方式:执行默认的处理函数
忽略处理方式:信号依然会注册,只是处理方式变为空操作;
自定义处理方式:自己定义信号处理函数,修改信号的处理函数指针。

自定义处理:signal()
sighandler_t signal(int signum, sighandler_t handler);

用于修改信号的处理函数,让该信号转而实现用户自定义的功能
signum:要修改的信号
sighandler_t是一个函数指针类型
handler:传入的新的处理方式:SIG_DFL(默认处理方式) / SIF_IGN(忽略处理方式) / 自定义函数
返回值:成功,返回原来的处理方式;失败,返回SIG_ERR

代码示例:

使用signal() 修改SIGALRM信号的处理函数,去实现用户自定义的函数功能

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

void sigcd(int no)
{
  printf("吃饭不?\n");
  printf("吃个der!\n");
  alarm(3);
}

int main()
{
  alarm(3);
  signal(SIGALRM,sigcd);
  while(1){
      printf("学习!学习!学习!\n");
      sleep(1);
  }
  return 0;
}

输出结果:
在这里插入图片描述

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

在这里插入图片描述

用户态与内核态:
当程序运行的都是用户自己写的代码访问的都是自己定义的变量时,程序运行在用户态
当程序运行要访问内核空间或者完成内核中的功能是,就需要切换到内核态运行。

程序从用户态切换到内核态的方法:系统调用接口、中断、异常

信号的阻塞

信号阻塞:信号依然可以注册,只是阻止信号被处理,待解除阻塞后再去处理。

原理:在pcb中还有一个信号集合----阻塞集合;哪个信号在这个集合中被标记,则表示这个信号要阻塞
在这里插入图片描述

在所有信号中,由两个信号比较特殊:SIGKILL -9 / SIGSTOP -19
这两个信号不能被阻塞,不能被修改处理方式,不能被忽略。

代码操作:

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

how:要对阻塞集合进行的操作

SIG_BLOCK:将set集合中的信号添加到阻塞集合;block|=set;
SIG_UNBLOCK:从阻塞集合中移除set中的信号;block&=(~set);
SIG_SETMASK:将set集合中的信号设置为阻塞集合;block=set;

oldset:用于保存修改前阻塞集合中的数据,以便于能够还原;不需要还原则置空。

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);	//判断信号是否在set集合中

代码实例:

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


void sigcb(int no)
{
  printf("recv signal:%d\n",no);
}

int main(){
  signal(SIGINT,sigcb);
  signal(40,sigcb);
  sigset_t set;//定义一个阻塞集合
  sigemptyset(&set);//清空
  sigfillset(&set);//填充

  sigprocmask(SIG_BLOCK,&set,NULL);
  printf("信号全部阻塞!输入回车继续执行:");
  getchar();
  sigprocmask(SIG_UNBLOCK,&set,NULL);

  while(1)sleep(1);

  return 0;
}

在这里插入图片描述

在这里插入图片描述

向该进程调用数次ctrl+c和5次kill -40命令后,可以看到ctrl+c,即2号信号只打印一次,40号信号打印了五次
因为非可靠信号只注册一次,可靠信号每次都注册

注意!!!
以下情况进程无法被杀死:
1、僵尸进程;2、信号被阻塞或者自定义或忽略;3、进程是停止状态。
除了使用9号进程外,后两种无法被其它方法杀死。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值