信号保存信号递达实际执行信号的处理动作信号未决信号从产生到递达之间的状态阻塞信号不会立刻处理会被缓存起来上面这些概念不是凭空产生的,在linux里面有真实对应的pending未决信号集已经到来、被阻塞卡住的信号位图每一位对应一个信号编号位 0该信号没有待处理位 1该信号存在未决实例等待递达block阻塞掩码哪些信号要暂时挡住block 与 pending 的联动逻辑内核发送信号给进程先查block位图对应位 0不阻塞直接信号递达执行 handler不会进入 pending对应位 1阻塞将pending对应位标记为 1信号暂存等待调用sigprocmask()修改 block把某信号位从 1 改为 0解除阻塞内核立刻扫描 pending若该信号 pending 位 1执行信号递达清空 pending 标记。handler:每个信号编号对应一条处理规则决定信号递达时程序该做什么。数组handler[32]长度 32对应 Linux 1~31 号标准信号 下标规则数组下标 信号编号 - 1例SIGINT (2 号信号) → 存储在handler[1]SIGHUP (1 号) →handler[0]。函数指针数组数组里每一格存的是地址可以是系统常量地址也可以是用户自定义函数的地址。三个问题问题一signal(2,myhandler)底层在做什么将myhandler的函数指针传入handler列表问题二什么叫做忽略,默认二者是内核用宏定义的特殊函数指针常量强制转换成sighandler_t信号处理函数指针类型存入handler[]函数指针数组。数值标识0代表默认处理1代表忽略处理和普通用户自定义函数地址区分开。问题三在信号还没有产生的时候,进程就能识别和处理信号???在内核中就内置了信号的管理和处理方法sigset_t叫做信号集本质是一个位图比特集合每一个 bit 对应一个信号bit 1该信号标记为「有效」bit 0该信号标记为「无效」 所有信号只用 1 个 bit 记录状态不计数同一个信号多次触发bit 只会置 1不会叠加次数。前文提到未决标志、阻塞标志都采用位图存储每个信号仅占用 1 个比特位状态只有 0/1 两种不会记录信号触发的次数。 正因二者存储逻辑完全一致Linux 统一使用sigset_t信号集类型承载这两类标记。sigset_t本质是比特集合用每一位标识单个信号的 “有效 / 无效” 状态在不同集合中含义不同阻塞信号集进程信号屏蔽字 Signal Mask比特位为 1代表该信号被阻塞 比特位为 0代表该信号允许递达。 ⚠️ 注意此处的 “屏蔽” 指暂时阻塞和直接忽略信号SIG_IGN是完全不同的逻辑。未决信号集pending比特位为 1代表该信号已产生、因阻塞暂未处理 比特位为 0代表无待处理的对应信号。阻塞信号集也被称作进程的信号屏蔽字属于进程独有属性。后续章节会详细讲解信号集的各类操作函数用于增删、清空、判断集合内的信号。系统当然也提供了对信号集修改的一些系统调用函数原型功能参数说明返回值int sigemptyset(sigset_t *set);清空信号集将集合内所有比特置 0不含任何信号set待操作的信号集指针成功返回 0失败返回 - 1int sigfillset(sigset_t *set);填满信号集将所有信号对应比特置 1包含全部信号set待操作的信号集指针成功返回 0失败返回 - 1int sigaddset(sigset_t *set, int signum);向信号集中添加单个信号对应 bit 置 1set信号集signum信号编号如 SIGINT2成功返回 0失败返回 - 1int sigdelset(sigset_t *set, int signum);从信号集中删除单个信号对应 bit 置 0set信号集signum信号编号成功返回 0失败返回 - 1int sigismember(const sigset_t *set, int signum);判断指定信号是否存在于集合中set只读信号集signum信号编号存在返回 1不存在返回 0出错返回 - 1sigprcmask参数详解int how操作模式3 种可选宏表格宏常量作用SIG_BLOCK屏蔽叠加将set中的信号追加到原有屏蔽字阻塞新增信号SIG_UNBLOCK解除屏蔽从原有屏蔽字中移除set里的信号SIG_SETMASK覆盖替换直接用set完整替换当前进程屏蔽字const sigset_t *set待操作的信号集传NULL仅读取当前屏蔽字不做任何修改非空根据how执行增 / 删 / 替换操作。sigset_t *oldset保存旧屏蔽字传NULL不保存原有屏蔽字非空函数执行完毕后将修改前的原始信号屏蔽字存入oldset方便后续恢复。返回值成功返回0失败返回-1并设置errno错误码sigpending读取进程未决信号集pending 信号集将当前所有已产生、但因被屏蔽暂时无法递达的信号存入参数set中。 未决信号信号已经发送给进程但该信号位于进程屏蔽字中被阻塞系统暂存该信号等待解除屏蔽后再处理。参数详解sigset_t *set输出型参数传入一个已初始化的信号集变量地址函数执行完成后set内部比特位会标记所有当前未决的信号。不可传入NULL否则无法获取未决信号信息。返回值成功返回0失败返回-1同步设置全局错误码errno那么这么修改pending--我们之前就学过了--kill接口使用我们自己设置要阻塞的信号void PrintPending(const sigset_t pending) { for(int sig31;sig0;sig--) { if(sigismember(pending,sig)) { cout1; } else cout0; } sleep(1); coutendl; } int main() { cout我的pid:getpid()endl; sigset_t block_set, old_set; sigemptyset(block_set); sigemptyset(old_set); sigaddset(block_set, 2); // 只是设置自己的block // 还要再进行系统调用 int n sigprocmask(SIG_SETMASK, block_set, old_set); if (n 0) perror(sigprocmask); while (1) { // 获取pending表 sigset_t pending; sigemptyset(pending); int n sigpending(pending); if (n 0) perror(sigpending); // 打印pending表 PrintPending(pending); sleep(1); } return 0; }我们将每一位都设置成阻塞,依然是9等信号无法被阻塞我们让进程进行15次循环之后将2号终止信号恢复发现进程立马就停止了一旦我们解除对某个信号的阻塞,他就会被立即递达但是我们不想要这个样子就要用到signal函数当阻塞结束的时候pending表相应的比特位会改变,那么是再handler之前变成0,还是再handler之后变成0的呢????我们可以再handler方法内部继续将pending打印出来就可以知道了根据观察我们不难得出----其实再调用handler函数之前,pending表对应的信号就已经被改成0了