进程信号

一、信号

1、作用:通知别人,发生了某件事情,尽快的去处理这件事情(操作系统通知进程发生了某个事件,打断进程当前操作,去处理这个事件)
2、信号是什么:软件中断
信号想要成为一个中断,首先我们必须认识这个信号,并且知道如何去处理它
事件多种多样,因此信号也是多种多样

3、查看操作系统中定义好的信号:

使用kil -l命令可以查看信号种类:
用户所能看到并使用的信号共有62种,两大分类:
1~31号信号:每个信号都有具体对应的事件---- 非可靠信号
34~64号信号:在操作系统中当前就没有具体对应的事件了,因此命名也稍微草率一点— 可靠信号

4、信号的生命周期:
信号的产生->信号在进程中注册->信号在进程中注销->处理信号的阻塞
5、信号的产生:

  • 硬件: ctrl+c ctrl+z
  • 软件: 命令:
    kill -signum pid
    1、kill命令能够杀死一个进程,主要是因为ill命令功能是向进程发送一个信号,默认发送的15 号终止信号函数:
    2、int kl(pid_t pid, int sig);向指定的进程发送指定的信号 int raise(int sig):向调用进程自身发送指定的信号
    3、void abort(void);向调用进程自身发送SIGABRT信号,使一个异常的进程退出
    4、unsigned int alarm(unsigned int seconds); seconed秒之后给调用进程发送一个 SIGALRM信号, 告诉进程时间到了。
    core dump:程序异常退出时,操作系统会保存这个进程的运行信息,便于这个进程的事后 调试(默认是关闭的)

6、信号在进程中的注册:让进程知道自己收到了这么一个信号

进程就是一个pcb, linux下是一 个task struct结构体;在pcb结构体中定义了一个信号的集合 (位图) ;
若给进程发送一个信号, 则将这个信号对应位置的二进制位置1,进程通过查看位图判断是 否有信号到来,进而去处理

  • pending信号集合:未决信号集合
    未决是一个状态,指的是信号产生了,但是还没有被处理的一个区间状态
    信号的注册,就是组织一个信号的信息, 添加到信号链表中,并且将信号pending位图进行置位

非可靠信号的注册:在注册信号时,判断当前信号是否已经注册过(位图是否已经为1) ;若没有注册,则添加节点,修改位图;反之,若已经注册过了,则什么也不干(这种信号在链表中就永远顶多只有一个节点,后来到达的信号就会被丢弃,事件丢失)
可靠信号的注册:在注册信号时,每次针对新到来的信号都会创建一个节点添加到链表中,并且位图置1; (链表中有可能有多 个相同节点)

7、信号在进程中的注销:抹除信号存在痕迹

非可靠信号:删除节点,位图置0 (因为非可靠信号只会注册一次,顶多只有一个节点)
可靠信号:删除节点,判断是否还有相同节点,若没有,则位图置0;表示没有这个信号了, 反之则位图依然为1;表示还有信号待处理

8、信号的处理:信号的递达

  • 进程处理一个事件,事件可以理解为就是一个功能,在程序中一个功能的实现单位就是一个函数,进程在收到信号之后,针对这个信号的事件找到它对应的处理函数,调用函数
  • 在操作系统中,每个信号都有其已经定义好的默认处理动作—信号的默认处理方式

信号的处理方式:
1>默认处理—系统中已经定义好的默认函数
2>忽略处理—处理的动作就是什么都不做(依然能够信号注册,只是处理动作中什么也不做而已)
3>自定义处理—用户自己定义信号回调函数,然后使用这个函数的地址替换原有信号动作数 组中的函数地址也就是说,替换了信号处理动作中的回调函数

  • 信号处理方式的修改:
  • sighandler_t signal(int signum, sighandler_t handler); //修改一个信号的处理动作 中的回调函数
    1、signum:信号值
    2、handler:函数指针sighandler_t–函数指针类型
  • typedef void (*sighandler_t) (int signo)
    SIG_ IGN—信号进行忽略处理
    SIG_ DFL—信号的默认处理
    signo:当信号到来时,操作系统向回调函数中传入的信号值—告诉用户本次调用这个函 数,是哪一个信号触发的自定义处理方式的信号捕捉流程:
    在这里插入图片描述

信号的处理,是当前进程运行从内核态切换用户态之前进行处理;

9、信号的阻塞:

  • 在进程中标记,哪些信号注册之后,暂时不去处理,直到信号解除阻塞 阻塞一个信号被递达

  • 进程pcb中有一个信号block集合----用户可以在这个集合中标记哪些信号将被阻塞

  • 如何阻塞一个信号: 将信号添加到pcb的block集合中,则表示文个信号將会被阻塞;到来则 暂时吋不被处理

  • 代码中如何阻塞一个信号:

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
1、 how:即将要対pcb中block集合所作的操作
SIG_BLOCK : 將第二个参数set集合中信号添加到block集合中/將set集合中信号加入阻塞 block = block I set
SIG_UNBLOCK : 将第二个参数set集合中信号从block集合中移除/将set集合中信号解除阻塞block = block &(~set);
SIG_SETMASK 使用第二个参数set集合中信号替換block集合中的数据/ block = set
2、set:信号集合(位图)
3、oldset:毎当block集合要友生改变的吋候,都会将原block集合中的数据拷贝到oldset中返 回给用户, 便于后期还原

0、定义几个信号的处理方式
signal(SIGINT, sigcb); signal(SIGQUIT, sigcb);
1、定义一个集合—用户向其中添加信号,即将要阻塞这些信号
sigset tset; <-- SIGINT SIGQUIT
2、阻塞上边定义的信号集合中的信号
sigprocmask(SIG_BLOCK, &set, NULL);
3、向进程发送这些信号,看看是否能够收到这些信号,是否处理了这些信号
getchar()—等待用户一个回车,然后流程オ会继续向下
4.解除阻塞,然后双察解除阻塞后信号会不会被处理
sigprocmask(SIG_UNBLOCK, &set, NULL);

二、函数的可重入于不可重入

  • 原子操作: 一件事情要么没干,要么就一次性完成,中间不会被打断
  • 竞态条件:多个执行流同时竞争执行
  • 在竞态条件下,一个函数的运行如果不会出现数据二义/逻辑混乱,则这个函数是一个可重入函数
  • 在竞态条件下,一个函数的运行有哦可能会出现数据二义/逻辑, 则这个函数是一个 不可重入函数 (一旦重入就会出错)
    函数的重入,在多个执行流中,同时进入一个函数执行功能
  • 函数的可重入与不可重入关键点:这个函数重是否对全局数据进行了不受保护的操作(非原子操作)

三、SIGCHLD信号

  • 子进程先于父进程退出,操作系统会通知父进程,但是父进程若没有关注子进程的退出状态,则子进程成为僵尸进程
  • 通知:操作系统就是通过SIGCHLD信号通知父进程的,但是: SIGCHLD信号, 默认的处理动作就是一个忽略处理,导致父进程无法及时的或者,因此只能使用进程等待一直阻塞等待才可以若父进程知道什么时候信号到来了,然后再去调用 wait/waitpid接口,则可以不需要过多等待
  • 因此修改SIGCHLD信号的处理方式,在回调函数中调用waitpid接口; —当子进程退出时, 会向父进程发送SIGCHLD信号,收到信号,操作进程就会自动的去回调信号处理函数,调用 其中的wait/waitpid接口实现回收资源
  • 1~31号信号都是非可靠信号。

多个子进程同时退出,因为SIGCHLD信号是一个非可靠信号,因此有可能会造成信号丢失 (非可靠信号在已经注册的情况下,就不再注册了) .
三个同时退出,只注册了一次信号,表示只有一次事件,也就指挥处理一次(指挥调用一次回 调函数) ;只能处理一个子进程,剩下的两个就会成为僵尸进程

因此好就在一次回调函数中,能够将所有的僵尸进程都处理掉----循环调用waitpid接 口,直到没有子进程才退出信号回调函数

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值