Advertisement

Linux (八)进程信号(信号产生,阻塞信号,捕捉信号)

阅读量:

信号的基本概念

为了解释信号这一现象,请考虑我们日常生活中常见的场景。
当用户发出指令时,在shell环境中开启了一个处于前台运行的任务。
当按下Ctrl+C键时,在键盘层面触发了一个硬件中断事件。
一旦CPU负责运行当前任务时(即处于忙碌态),则该任务在切换回中断处理前需暂停其用户的态操作,并立即切换至内核态以处理中断请求。
在接收到来自控制台的中断请求后(即收到SIGINT信号),终端驱动程序将此请求解释为 SIGINT 信号,并将其状态信息存储于相应的PCB中;随后,在系统准备从内核态返回到该进程的状态时(即完成中断处理),会先检查PCB中的记录是否有待处理的中断事件;若发现有一个SIGINT信号待处理,则默认其处理动作是终止该进程;因此系统会直接终止该进程中运行的所有子过程而不返回其用户的态代码进行进一步的操作。

其中Ctrl-C产生的信号仅能将该信号分配给前切进程
该系统允多后台进程同时与一个前切进程共存
在控制流程中呈异步特征
通过使用kill命令可快速浏览预定义的信号集合

产生信号的三种方式
1:通过终端操作指令触发信号
默认情况下,SIGINT处理会终止相关进程;而SIGQUIT处理不仅会导致进程终止,并且还会执行Core Dump操作;通过ulimit命令可以调整shell进程的资源限制设置。
当发生Core Dump时(即某些特殊情况下),我们可以利用core文件来进一步分析问题。
使用gdb命令文件名参数的方式运行程序后,在核心文件中即可定位到错误发生的具体位置。

通过调用 kill 函数来完成杀掉某个进程的操作;该 kill 函数将特定信号发送至指定进程;raise 也可以向当前程序发送特定类型的信息

复制代码
    #include<signal.h>
    int kill(pid_t pid,int signo);
    int raise(int signo);
    这两个函数都是成功返回0,错误返回-1
    #include<stdlib.h>
    void abort(void);
    就像exit函数一样,abort函数总是会成功,所以没有返回值

第3点基于软件前提的触发机制 SIGPIPE 该触发机制是由软件前提所引发的一种信号 下一步将介绍alarm函数

复制代码
    #include<unistd.h>
    unsigned int alarm(unsigned int seconds);

通过调用alarm函数设置一个定时闹钟,并向内核指定seconds秒后发送SIGALRM信号;该信号默认处理方式为终止当前进程。

本节介绍常见的信号处理方式。
不响应此信号。
按照预设程序自动完成该操作。
配置一个基于硬件的触发机制,在响应特定事件时将系统切换至用户态以完成特定功能,并将其定义为捕获事件。

阻塞信号

与本系统相关的其他常见概念中包含以下关键要素:
当系统接收并处理一个请求时所经历的动作被称为'到达'(Delivery)。
在到达之前的状态定义为'未解决'(Pending)状态。
进程可以通过选择'阻塞'(block)某个请求来管理资源分配的问题。
一旦一个请求被阻塞,在其到达之前始终处于'未解决'(Pending)状态。
需要注意的是:其中一种情况是当进程遇到需要等待响应时会选择'阻塞'(block)某个请求;另一种情况则是当该请求已经到达后系统会根据当前的任务优先级进行处理。

信号在内核中表示示意图

这里写图片描述

每个事件都有两个标志字段分别标识阻塞与未决状态以及一个处理函数指针用于指定处理动作。每当一个事件发生时系统会在相应的进程中设置该事件的未决标记只有当该事件到达时相关机制才会被激活从图中可以看到并未发生过SIGHUP信号的情况当它到达时默认处理函数会被调用SIGINT 事件曾经被阻塞过在当前状态下仍处于无法到达的状态SIGUIT 事件从未发生一旦出现此类型事件系统会将其置入阻塞状态并由自定义函数Sighandler来处理相应的操作

信号集概念

sigprocmask

复制代码
    #include<signal.h>
    int sigprocmask(int how,const sigset_t *set,sigset_ *oset);
    返回值:成功返回0;失败返回-1

若oset为非空指针,则获取进程当前设置的信号屏蔽位,并通过oset参数返回;若set为非空指针,则用于修改进程的信号屏蔽位设置。其中how仅用于指定修改方式。若两者皆不为空,则先将原信号屏蔽位移至oset中;接着根据set与how参数更新该位设置。

信号的捕捉

1。内核如何实现信号捕捉

这里写图片描述

从图中可以看出来程序共在内核与用户之间切了四次

2。sigaction

复制代码
    #include<signal.h>
    int sigaction(int signo,const struct sigaction *act,struct sigaction *oact);
    sigaction 函数可以读取和修改与指定信号相关联的处理动作。调用成功返回0;失败返回-1

signo是用于标识特定信号的编号字段。当act指针存在时,会根据其值更新该信号的操作指令;而如果oact指针不为空,则会利用它传递出该信号原先的操作指令。需要注意的是,在本系统中(act, oact)两个变量均指向sigaction类型的结构体

每当某个特定类型的事件或触发条件触发时,在操作系统内核中会触发相关事件处理器的执行过程。具体来说,在事件处理器被激活并执行完毕后系统会恢复到之前的事件管理状态。这种机制能够有效防止同一类型事件在同一处理器窗口期间多次执行从而确保系统的稳定性与安全性。此外通过使用sammask字段可以实现对多个事件类型的同步禁用从而进一步提升了系统的灵活性与容错能力

3。pause

复制代码
    #include<unistd.h>
    int pause(void);

该函数将执行暂停操作直至接收特定信号。若选择立即终止进程,则过程立即终止,在此情况下该函数不会进行任何响应。若选择忽略该信号,则系统保持暂停状态,并在此情况下该函数不会进行任何响应。在捕获此类事件后会触发相应的事件响应机制,在这种情况下会通过设置指定错误码来指示发生错误的情况。因此,在正常情况下该函数不会有任何输出结果。

全部评论 (0)

还没有任何评论哟~