ToB企服应用市场:ToB评测及商务社交产业平台

标题: 【Linux探索学习】第二十八弹——信号(下):信号在内核中的处理及信号捕 [打印本页]

作者: 祗疼妳一个    时间: 2025-2-12 09:17
标题: 【Linux探索学习】第二十八弹——信号(下):信号在内核中的处理及信号捕
Linux学习条记:
https://blog.csdn.net/2301_80220607/category_12805278.html?spm=1001.2014.3001.5482
媒介:
   在前面我们已经学习了有关信号的一些根本的知识点,包括:信号的概念、信号产生和信号处理等,今天我们重点来讲解一下信号在内核中的处理以及信号捕捉的相关知识点
  
  在这篇文章中,我们将深入探讨 Linux 信号在内核中的处理流程,详细讲解信号递达、信号阻塞、未决信号、信号集操纵、信号捕捉等内容,并通过大量的代码示例和现实场景来展示信号如安在 Linux 中运作。
  与信号有关的还有一个很紧张的知识点是有关用户态、内核态和状态切换的知识,本篇没有进行讲解,需要自己再去了解一下

目次
1. 信号在内核中的处理流程
1.1 信号在内核中的表示
1.2 信号的递达机制
信号递达的条件
信号递达过程
示例代码:信号递达
1.3 信号未决状态
信号未决队列的管理
示例代码:查察未决信号
1.4 信号集与 sigset_t
信号集的操纵
示例代码:操纵信号集
​编辑
1.5 信号屏蔽与 sigprocmask
示例代码:利用 sigprocmask() 阻塞和解除阻塞信号
1.6 获取未决信号:sigpending()
示例代码:利用 sigpending() 查察未决信号
2. 信号捕捉与处理
2.1 利用 signal() 捕捉信号
示例代码:利用 signal() 捕捉信号
2.2 利用 sigaction() 捕捉信号
示例代码:利用 sigaction() 捕捉信号
3. 总结


1. 信号在内核中的处理流程

信号是由内核或其他进程通过系统调用发送给目标进程的。当进程正在执行时,信号可以或许在不干扰进程当前操纵的环境下打断它的执行,触发某种特定的举动。信号的处理流程在 Linux 内核中被设计得非常灵活,既支持异步信号处理,又能通过进程的信号屏蔽机制来控制信号的递达。
1.1 信号在内核中的表示

信号在内核中的表示表示图:

   
  1.2 信号的递达机制

信号递达是信号机制中的焦点概念,它是信号从信号源发送到目标进程的过程。信号递达的实现依靠于内核的进程调度机制。在进程执行过程中,内核需要判断该进程是否有需要处理的未决信号,信号的递达会在进程的上下文切换时被触发。
信号递达的条件

信号的递达取决于以下几个因素:
     信号递达过程

信号的递达过程通常包括以下几个步骤:
     示例代码:信号递达

  1. #include <stdio.h>
  2. #include <signal.h>
  3. #include <unistd.h>
  4. void signal_handler(int sig) {
  5.     printf("Received signal: %d\n", sig);
  6. }
  7. int main() {
  8.     signal(SIGINT, signal_handler);  // 捕捉 SIGINT 信号
  9.     printf("Waiting for SIGINT...\n");
  10.     while(1) {
  11.         sleep(1);  // 进入等待状态,直到接收到信号
  12.     }
  13.     return 0;
  14. }
复制代码
在上面的代码中,进程会不停运行并等候 SIGINT 信号(通常由按下 Ctrl+C 触发)。一旦进程吸取到 SIGINT 信号,内核会将其递送到进程,并触发信号处理函数 signal_handler

1.3 信号未决状态

当信号发送给进程时,如果该信号被进程的信号屏蔽字阻塞,那么该信号就会进入未决状态。未决信号是那些已经被发送但尚未被递达的信号。内核维护了每个进程的未决信号队列,并会在进程解除对该信号的阻塞时按次序递送这些信号。
信号未决队列的管理

在 Linux 内核中,每个进程都有一个 task_struct 结构体,其中包含了当进步程的未决信号聚集。每当一个信号发送给一个进程时,如果该信号被阻塞,内核不会立刻递送它,而是将其存放在进程的未决信号队列中,直到进程解除对该信号的阻塞。
未决信号通常在进程解除信号屏蔽字后,由内核递送。递送次序通常与信号发送次序一致,且会按照优先级递送实时信号和标准信号。
示例代码:查察未决信号

  1. #include <stdio.h>
  2. #include <signal.h>
  3. #include <unistd.h>
  4. int main() {
  5.     sigset_t pending;
  6.     sigpending(&pending);  // 获取当前进程的未决信号
  7.    
  8.     if (sigismember(&pending, SIGINT)) {
  9.         printf("SIGINT is pending.\n");
  10.     } else {
  11.         printf("No SIGINT pending.\n");
  12.     }
  13.     sleep(10);  // 稍作停顿,方便查看信号状态
  14.     return 0;
  15. }
复制代码
通过利用 sigpending() 函数,我们可以查察当进步程的未决信号集。如果进程没有处理 SIGINT 信号,且信号被阻塞,则该信号会处于未决状态。

1.4 信号集与 sigset_t

信号集(sigset_t)是一个用于表示信号聚集的数据结构,它通过位掩码的方式表示进程当前可以接受的信号聚集。sigset_t 通常是一个整数或更大的数据范例,每一位对应一个信号。
信号集的操纵

在 Linux 中,常用的信号集操纵函数包括:
   
  示例代码:操纵信号集

  1. #include <signal.h>
  2. #include <stdio.h>
  3. int main() {
  4.     sigset_t set;
  5.     sigemptyset(&set);         // 初始化为空集
  6.     sigaddset(&set, SIGINT);   // 将 SIGINT 添加到信号集中
  7.     sigaddset(&set, SIGTERM);  // 将 SIGTERM 添加到信号集中
  8.     if (sigismember(&set, SIGINT)) {
  9.         printf("SIGINT is in the set.\n");
  10.     }
  11.     sigdelset(&set, SIGINT);   // 从信号集中删除 SIGINT
  12.     if (!sigismember(&set, SIGINT)) {
  13.         printf("SIGINT is no longer in the set.\n");
  14.     }
  15.     return 0;
  16. }
复制代码


1.5 信号屏蔽与 sigprocmask

sigprocmask() 是一个用于修改进程信号屏蔽字的系统调用,它可以用来阻塞、解除阻塞或查询进程的信号屏蔽字。信号屏蔽字界说了哪些信号是被阻塞的,从而影响信号递达的时机。
sigprocmask() 具有以下操纵模式:
   
  示例代码:利用 sigprocmask() 阻塞和解除阻塞信号

  1. #include <signal.h>
  2. #include <stdio.h>
  3. #include<unistd.h>
  4. int main() {
  5.     sigset_t new_mask, old_mask;
  6.     sigemptyset(&new_mask);
  7.     sigaddset(&new_mask, SIGINT);  // 阻塞 SIGINT
  8.     sigprocmask(SIG_BLOCK, &new_mask, &old_mask);  // 阻塞 SIGINT
  9.     // 信号屏蔽后,可以进行一些操作
  10.     printf("SIGINT is blocked. Press Ctrl+C to send SIGINT.\n");
  11.     sleep(10);  // 暂停,等待 Ctrl+C 输入
  12.     // 恢复信号屏蔽字
  13.     sigprocmask(SIG_SETMASK, &old_mask, NULL);  // 恢复原信号屏蔽字
  14.     printf("SIGINT is unblocked.\n");
  15.     sleep(10);  // 等待信号递达
  16.     return 0;
  17. }
复制代码
在这段代码中,SIGINT 信号在前 10 秒内被阻塞,用户按下 Ctrl+C 时信号不会立刻递达。10 秒后,信号屏蔽被解除,SIGINT 信号会被递送并触发相应的处理。

1.6 获取未决信号:sigpending()

sigpending() 函数用于获取当进步程的未决信号,它返回一个信号集,表示该进程尚未处理的信号聚集。sigpending() 的实现依靠于进程的信号队列,它可以用于调试和监控进程的信号处理状态。
示例代码:利用 sigpending() 查察未决信号

  1. #include <signal.h>
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. int main() {
  5.     sigset_t pending;
  6.     sigemptyset(&pending);
  7.    
  8.     // 阻塞 SIGINT 信号
  9.     sigset_t mask;
  10.     sigemptyset(&mask);
  11.     sigaddset(&mask, SIGINT);
  12.     sigprocmask(SIG_BLOCK, &mask, NULL);
  13.     // 发送 SIGINT 信号,但它会被阻塞
  14.     kill(getpid(), SIGINT);
  15.     // 获取未决信号
  16.     sigpending(&pending);
  17.     if (sigismember(&pending, SIGINT)) {
  18.         printf("SIGINT is pending.\n");
  19.     } else {
  20.         printf("No SIGINT pending.\n");
  21.     }
  22.     sleep(5);  // 稍作等待,防止信号丢失
  23.     return 0;
  24. }
复制代码
此步伐模拟了阻塞 SIGINT 信号并通过 sigpending() 查察进程的未决信号状态。如果信号被阻塞,它将在信号屏蔽字解除后递达。

2. 信号捕捉与处理

信号捕捉是指进程通过自界说信号处理函数来响应特定的信号。Linux 提供了 signal() 和 sigaction() 两种方式来捕捉信号。signal() 是一种简单的接口,而 sigaction() 提供了更为复杂的配置选项,使得开发者可以或许在处理信号时得到更多的控制权。
2.1 利用 signal() 捕捉信号

signal() 是最基础的信号捕捉方式,它答应开发者指定一个信号处理函数来响应特定信号。signal() 的利用非常简单,但它并不支持所有高级功能,如信号的重入处理或复杂的信号控制。
示例代码:利用 signal() 捕捉信号

  1. #include <signal.h>
  2. #include <stdio.h>
  3. #include<unistd.h>
  4. void signal_handler(int sig) {
  5.     printf("Received signal: %d\n", sig);
  6. }
  7. int main() {
  8.     signal(SIGINT, signal_handler);  // 捕捉 SIGINT 信号
  9.     printf("Waiting for SIGINT...\n");
  10.     while (1) {
  11.         sleep(1);  // 程序将一直运行,直到接收到信号
  12.     }
  13.     return 0;
  14. }
复制代码
在这个示例中,signal_handler 函数会在吸取到 SIGINT 信号时被调用。

2.2 利用 sigaction() 捕捉信号

sigaction() 提供了比 signal() 更灵活的方式来处理信号。它答应开发者在捕捉信号时设定更多的参数,比如怎样处理重入信号、是否需要恢复默认举动等。
sigaction() 的结构体界说如下:
  1. struct sigaction {
  2.     void (*sa_handler)(int);
  3.     sigset_t sa_mask;
  4.     int sa_flags;
  5.     void (*sa_restorer)(void);
  6. };
复制代码

示例代码:利用 sigaction() 捕捉信号

  1. #include <signal.h>
  2. #include <stdio.h>
  3. #include<unistd.h>
  4. void signal_handler(int sig) {
  5.     printf("Received signal: %d\n", sig);
  6. }
  7. int main() {
  8.     struct sigaction sa;
  9.     sa.sa_handler = signal_handler;  // 指定信号处理函数
  10.     sa.sa_flags = 0;
  11.     sigemptyset(&sa.sa_mask);  // 初始化信号集
  12.     sigaction(SIGINT, &sa, NULL);  // 捕捉 SIGINT 信号
  13.     printf("Waiting for SIGINT...\n");
  14.    
  15.     while (1) {
  16.         sleep(1);  // 程序将一直运行,直到接收到信号
  17.     }
  18.     return 0;
  19. }
复制代码
通过 sigaction(),步伐可以或许灵活地处理信号,并控制信号捕捉的举动,甚至答应在处理信号时阻塞其他信号。

3. 总结

   本文我们讲解了信号的处理机制,并且对信号捕捉进行了更详细的增补,结合上篇内容,根本上将信号部门的内容进行了大概的讲解,认真看一下相信会对你有所帮助
  

   感谢各位大佬观看,创作不易,还望各位大佬点赞支持!!!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4