【探索Linux】—— 强大的命令行工具 P.17(进程信号 —— 信号保存 | 阻塞信号 | sigprocmask() | sigpending() )

这篇具有很好参考价值的文章主要介绍了【探索Linux】—— 强大的命令行工具 P.17(进程信号 —— 信号保存 | 阻塞信号 | sigprocmask() | sigpending() )。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【探索Linux】—— 强大的命令行工具 P.17(进程信号 —— 信号保存 | 阻塞信号 | sigprocmask() | sigpending() ),Linux,linux,运维,服务器,c++

引言

在计算机科学领域,信号是一种重要的通信机制,用于处理各种系统事件和进程间的通信。Linux作为一个开源操作系统,以其稳定性和高度可定制性而闻名。在Linux下,信号的处理是实现进程间通信和事件处理的关键机制之一。

本文将继续探讨Linux下信号的相关主题,着重介绍信号的保存、阻塞以及sigprocmask函数的用法。通过深入了解这些概念和技术,我们能够更好地掌握Linux信号处理的原理和方法,提高系统的可靠性和稳定性。

通过本文的学习,读者将对Linux下信号的保存、阻塞和sigprocmask函数有更深入的了解。这些知识将帮助读者在开发和维护Linux应用程序时更好地处理信号,提高系统的稳定性和性能。无论是初学者还是有经验的开发人员,都将受益于本文所涵盖的内容。让我们一起深入研究Linux信号处理的精髓吧!

一、阻塞信号

1. 信号相关常见概念

(1)信号递达

信号递达是指当信号被发送和接收后,信号的处理过程。在Linux中,进程可以通过系统调用kill()向其他进程发送信号,同时也可以接收来自其他进程的信号。当信号被发送到一个进程时,需要经过多个步骤才能被接收并处理:

  1. 发送信号:进程A使用kill()系统调用向进程B发送信号。

  2. 信号递送:信号从进程A发送到进程B,进程B接收到信号。

  3. 信号处理:进程B根据信号的类型和处理方式来进行相应的处理。

在Linux中,每个信号都有一个默认的处理方式。例如,SIGKILL信号会强制终止进程,而SIGINT信号会让进程中断并退出。然而,进程也可以通过signal()系统调用或sigaction()系统调用来改变信号的处理方式,以便实现更灵活的信号处理行为。

🚨注意在信号递送和信号处理的过程中,可能会发生信号丢失或者信号被阻塞的情况。当一个进程处于阻塞状态时,它将无法接收到任何信号,直到解除了阻塞状态。如果多个信号同时到达进程时,可能会出现信号排队的情况,此时进程需要按照一定的规则来处理这些信号。

(2)信号未决

在Linux中,信号未决(Pending Signal)指的是一个进程接收到但尚未处理的信号。当一个信号被发送给一个进程时,如果该进程当前正在执行某个信号处理函数或者该信号已经处于未决状态,则该信号会被放入进程的信号未决位集(Pending Signal Mask)中,等待进程从信号处理函数返回后进行处理。

在信号未决位集中,每个位代表一个信号,如果该位为1,则表示该位对应的信号处于未决状态。一个进程可以通过sigpending()系统调用来查询自己的信号未决位集

如果一个进程接收到多个同类型的信号并且信号处理函数尚未返回,则这些信号将被合并成一个信号,并只记录一次信号未决。进程可以使用sigprocmask()系统调用来设置或修改信号未决掩码,以控制哪些信号可以被接收和处理。

🚨注意:当进程解除信号阻塞状态后,它必须处理所有未决的信号,否则这些信号将继续被保留在信号未决位集中,可能会导致信号丢失或者其他问题。因此,在处理信号的过程中,要注意及时处理所有未决信号,避免信号积压导致系统异常。

(3)阻塞信号

阻塞信号是指进程可以选择暂时延迟处理某些特定信号的传递和处理。在Linux中,进程可以通过设置信号阻塞掩码(Signal Mask)来达到这一目的。

当一个信号被发送给一个进程时,内核会首先检查该信号是否在进程的信号阻塞掩码中。如果信号在阻塞掩码中,则该信号将被暂时挂起,直到该信号从阻塞状态解除后才能被处理。进程可以使用sigprocmask()系统调用来修改信号阻塞掩码。

通过设置信号阻塞掩码,进程可以灵活地控制哪些信号可以被接收和处理,以及在何种情况下可以延迟处理某些信号。这种机制在多线程编程和信号处理复杂的应用中尤为重要。

🚨注意当进程解除对某个信号的阻塞时,如果有多个该类型的信号在阻塞期间到达,那么这些信号将按照某种规则进行排队,等待进程逐个处理。另外,即使信号被阻塞,但仍然会记录在信号未决位集中,等待进程解除阻塞后处理。

(4)忽略信号

忽略信号是指进程可以选择不对某些特定信号进行处理,即忽略该信号的传递和默认处理行为。在Linux中,进程可以通过设置信号处理函数为SIG_IGN来达到这一目的。

当一个信号被发送给一个进程时,内核会首先检查该信号的处理方式。如果进程将该信号的处理函数设置为SIG_IGN(忽略信号),则内核将不对该信号进行任何处理,直接丢弃该信号。

通过忽略信号,进程可以屏蔽一些不需要处理的信号,从而避免其产生默认的处理行为。对于某些特定的信号,可能会存在一些默认的处理行为,比如终止进程、终止进程并生成core文件等。通过忽略信号,进程可以防止这些默认的处理行为发生。

🚨注意并非所有的信号都可以被忽略。一些重要的信号,如SIGKILL和SIGSTOP,默认情况下是不能被忽略的,它们具有固定的处理行为。此外,一些特殊的信号,如SIGCHLD,可以被设置为忽略,但是会导致一些系统资源无法正确释放,因此需要谨慎使用。

2. 信号在内核中的表示

⭕信号在内核中的表示示意图

【探索Linux】—— 强大的命令行工具 P.17(进程信号 —— 信号保存 | 阻塞信号 | sigprocmask() | sigpending() ),Linux,linux,运维,服务器,c++

  • 每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。在上图的例子中,SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作。
  • SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。
  • SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。如果在进程解除对某信号的阻塞之前这种信号产生过多次,将如何处理?POSIX.1允许系统递送该信号一次或多次。Linux是这样实现的:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。本章不讨论实时信号。

3. sigset_t (数据类型)

sigset_t 是一个数据类型,用于表示信号集。它是一个由位图组成的数据结构,用于跟踪和管理多个信号的状态。在 C 语言中,sigset_t 常常以无符号整数或者数组的形式实现

在 Linux 中,sigset_t 是通过使用位操作来表示信号集的。每个信号对应 sigset_t 中的一个位(bit),如果某个位被设置为 1,则表示相应的信号在该信号集中被包含;如果某个位被设置为 0,则表示相应的信号在该信号集中不被包含。

sigset_t 通常用于以下操作:

  • 设置信号集中的某个位:可以使用宏函数 sigaddset()sigemptyset() 来设置信号集中的位。sigaddset() 可以将指定的信号添加到信号集中,而 sigemptyset() 可以清空信号集。
  • 清除信号集中的某个位:可以使用宏函数 sigdelset() 来清除信号集中的位,从而从信号集中删除指定的信号。
  • 检查信号集中的某个位是否被设置:可以使用宏函数 sigismember() 来检查信号集中的位是否被设置,从而判断指定的信号是否在信号集中。

sigset_t 的使用可以帮助进程或线程控制和管理信号的行为,如阻塞或解除阻塞某些信号,判断信号是否被阻塞等。在信号处理函数中,可以通过调用相关的系统调用来获取和修改当前进程或线程的 sigset_t,以实现对信号的处理和控制。

4. 信号集操作函数

在C语言中,可以使用以下函数来进行信号集(sigset_t)的操作:

函数 功能
sigemptyset(sigset_t *set) 清空信号集,将所有信号从集合中移除。
sigfillset(sigset_t *set) 将所有信号添加到信号集中,使其包含所有信号。
sigaddset(sigset_t *set, int signum) 将指定的信号添加到信号集中。
sigdelset(sigset_t *set, int signum) 从信号集中删除指定的信号。
sigismember(const sigset_t *set, int signum) 检查指定的信号是否在信号集中,如果在返回1,否则返回0。

这些函数都返回一个整数值来表示操作的成功与否。如果函数执行成功,返回值为0;如果函数执行失败,返回值为-1,并设置相应的错误号(errno)。

这些函数通常用于与信号处理相关的操作,例如设置阻塞信号集、检查信号是否被阻塞、解除阻塞等。通过操作信号集,可以对进程接收和处理的信号进行控制。

需要包含 <signal.h> 头文件才能使用上述函数。此外,还有其他一些与信号处理相关的函数,如 sigprocmask()sigpending() 等,它们也可用于信号集的操作和管理。

二、sigprocmask() 函数

sigprocmask() 函数用于更改或检索进程的信号屏蔽字(signal mask)。信号屏蔽字决定了进程当前阻塞的信号集,即哪些信号在被阻塞的情况下不能被进程接收到。

#include <signal.h>

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

sigprocmask() 函数接受三个参数:

  • how:用于指定对信号屏蔽字的操作。
操作类型 描述
SIG_BLOCK set 指向的信号集中的信号添加到当前的信号屏蔽字中。
SIG_UNBLOCK 从当前的信号屏蔽字中移除 set 指向的信号集中的信号。
SIG_SETMASK 将当前的信号屏蔽字替换为 set 指向的信号集。
  • set:一个指向 sigset_t 类型的指针,指向要设置的新的信号屏蔽字。
  • oldset:可选参数,如果不为 NULL,则旧的信号屏蔽字将被存储在 oldset 指向的位置。

sigprocmask() 函数返回值表示操作的成功与否。如果函数执行成功,返回值为0;如果函数执行失败,返回值为-1,并设置相应的错误号(errno)。

以下示例演示了如何使用 sigprocmask() 函数:

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

int main() {
    sigset_t newset, oldset;

    // 设置要阻塞的信号集
    sigemptyset(&newset);
    sigaddset(&newset, SIGINT);

    // 阻塞 SIGINT 信号
    if (sigprocmask(SIG_BLOCK, &newset, &oldset) == -1) {
        perror("sigprocmask");
        return 1;
    }

    printf("SIGINT is blocked. Press Ctrl+C to send the signal.\n");

    // 挂起进程,等待信号到达
    pause();

    // 恢复原来的信号屏蔽字
    if (sigprocmask(SIG_SETMASK, &oldset, NULL) == -1) {
        perror("sigprocmask");
        return 1;
    }

    printf("SIGINT is unblocked. Signal handling resumed.\n");

    return 0;
}

上述示例将 SIGINT 信号添加到新的信号屏蔽字中,然后使用 sigprocmask() 函数将其阻塞。当程序运行时,按下 Ctrl+C 将发送 SIGINT 信号,但由于该信号被阻塞,进程挂起直到信号解除阻塞后才继续执行。在恢复原来的信号屏蔽字后,程序可以正常处理 SIGINT 信号。

【探索Linux】—— 强大的命令行工具 P.17(进程信号 —— 信号保存 | 阻塞信号 | sigprocmask() | sigpending() ),Linux,linux,运维,服务器,c++

三、sigpending() 函数

sigpending() 函数用于获取当前进程挂起(pending)的信号集,即已经产生但尚未被进程处理的信号

#include <signal.h>

int sigpending(sigset_t *set);

sigpending() 函数接受一个指向 sigset_t 类型的指针作为参数,用于存储当前挂起的信号集。该函数将会将当前进程挂起的信号填充到 set 指向的信号集中。

sigpending() 函数返回值表示操作的成功与否。如果函数执行成功,返回值为0;如果函数执行失败,返回值为-1,并设置相应的错误号(errno)。

以下示例演示了如何使用 sigpending() 函数:

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

void handler(int signum) {
    printf("Received signal: %d\n", signum);
}

int main() {
    sigset_t pending_set;

    // 设置信号处理函数
    signal(SIGINT, handler);

    // 发送 SIGINT 信号
    raise(SIGINT);

    // 获取挂起的信号集
    if (sigpending(&pending_set) == -1) {
        perror("sigpending");
        return 1;
    }

    // 检查 SIGINT 是否在挂起的信号集中
    if (sigismember(&pending_set, SIGINT)) {
        printf("SIGINT is pending.\n");
    } else {
        printf("SIGINT is not pending.\n");
    }

    return 0;
}

上述示例中,首先定义了一个信号处理函数 handler,当接收到 SIGINT 信号时,该函数将被调用。然后使用 signal() 函数将 SIGINT 信号与该处理函数关联。

接下来,通过调用 raise(SIGINT) 发送 SIGINT 信号给当前进程。

然后,使用 sigpending() 函数获取当前挂起的信号集,并将结果存储在 pending_set 中。

最后,使用 sigismember() 函数检查 SIGINT 是否在挂起的信号集中,根据结果输出相应的信息。

请注意,由于信号的处理是异步的,在获取挂起的信号集之前,可能已经有其他信号被处理掉了。因此,sigpending() 只能提供当前未被处理的挂起信号的部分信息。

【探索Linux】—— 强大的命令行工具 P.17(进程信号 —— 信号保存 | 阻塞信号 | sigprocmask() | sigpending() ),Linux,linux,运维,服务器,c++

温馨提示

感谢您对博主文章的关注与支持!如果您喜欢这篇文章,可以点赞、评论和分享给您的同学,这将对我提供巨大的鼓励和支持。另外,我计划在未来的更新中持续探讨与本文相关的内容。我会为您带来更多关于Linux以及C++编程技术问题的深入解析、应用案例和趣味玩法等。如果感兴趣的话可以关注博主的更新,不要错过任何精彩内容!

再次感谢您的支持和关注。我们期待与您建立更紧密的互动,共同探索Linux、C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!
【探索Linux】—— 强大的命令行工具 P.17(进程信号 —— 信号保存 | 阻塞信号 | sigprocmask() | sigpending() ),Linux,linux,运维,服务器,c++文章来源地址https://www.toymoban.com/news/detail-752444.html

到了这里,关于【探索Linux】—— 强大的命令行工具 P.17(进程信号 —— 信号保存 | 阻塞信号 | sigprocmask() | sigpending() )的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【探索Linux】—— 强大的命令行工具 P.22(POSIX信号量)

    在上一篇文章中,我们深入探讨了多线程编程的核心概念,包括线程同步、条件变量以及线程安全等关键技术,为读者揭示了并发编程的复杂性及其解决方案。这些概念和技术是实现高效、稳定并发应用程序的基础。继续在并发编程的旅途上前进,本篇文章将引导我们走进

    2024年02月20日
    浏览(44)
  • 【探索Linux】—— 强大的命令行工具 P.8(进程优先级、环境变量)

    前面我们讲了C语言的基础知识,也了解了一些数据结构,并且讲了有关C++的一些知识,也学习了一些Linux的基本操作,也了解并学习了有关Linux开发工具vim 、gcc/g++ 使用、yum工具以及git 命令行提交代码也相信大家都掌握的不错,上一篇文章我们了解了关于进程的基本概念,今

    2024年02月09日
    浏览(56)
  • 【探索Linux】—— 强大的命令行工具 P.10(进程的控制——创建、终止、等待、程序替换)

    前面我们讲了C语言的基础知识,也了解了一些数据结构,并且讲了有关C++的一些知识,也学习了一些Linux的基本操作,也了解并学习了有关Linux开发工具vim 、gcc/g++ 使用、yum工具以及git 命令行提交代码也相信大家都掌握的不错,上一篇文章我们了解了关于进程的地址空间,今

    2024年02月08日
    浏览(54)
  • 【探索Linux】—— 强大的命令行工具 P.15(进程间通信 —— system V共享内存)

    在多进程编程中,进程间通信(IPC)是一项关键技术,它使得不同的进程能够相互交换数据和协调行为。而在众多的IPC机制中,System V共享内存是一种高效且强大的通信方式。通过使用共享内存,我们可以将数据从一个进程快速地传递给另一个进程,避免了复制数据的开销,

    2024年02月05日
    浏览(62)
  • 【探索Linux】—— 强大的命令行工具 P.14(进程间通信 | 匿名管道 | |进程池 | pipe() 函数 | mkfifo() 函数)

    当今计算机系统中,进程间通信扮演着至关重要的角色。随着计算机系统的发展和复杂性的增加,多个进程之间的协作变得更加必要和常见。进程间通信使得不同进程能够共享资源、协调工作、传输数据,并实现更加复杂和强大的功能。本文将深入探讨进程间的通信,以及管

    2024年02月05日
    浏览(72)
  • 【探索Linux】—— 学习强大的命令行工具 P.1(Linux简介)

    目录 前言 一、Linux简介 二、linux的不同发行版本 三、Linux的开源性质 四、Linux的特点  五、Linux代码演示(仅供参考)  总结         前面我们讲了C语言的基础知识,也了解了一些数据结构,并且讲了有关C++的一些知识,也相信大家都掌握的不错,今天博主将会新开一个L

    2024年02月11日
    浏览(74)
  • 【探索Linux】—— 强大的命令行工具 P.3(Linux开发工具 vim)

    前面我们讲了C语言的基础知识,也了解了一些数据结构,并且讲了有关C++的一些知识,也学习了一些Linux的基本操作,也相信大家都掌握的不错,今天博主带大家了解一下Linux开发工具—— vim ,下面话不多说坐稳扶好咱们要开车了!!!😍 Vim(Vi IMproved)是一款在Unix和类

    2024年02月12日
    浏览(42)
  • 【探索Linux】—— 步步学习强大的命令行工具 P.1(Linux简介)

    目录 前言 一、Linux简介 二、linux的不同发行版本 三、Linux的开源性质 四、Linux的特点  五、Linux代码演示(仅供参考)  总结         前面我们讲了C语言的基础知识,也了解了一些数据结构,并且讲了有关C++的一些知识,也相信大家都掌握的不错,今天博主将会新开一个L

    2024年02月14日
    浏览(47)
  • 【探索Linux】—— 强大的命令行工具 P.2(Linux下基本指令)

            前面我们讲了C语言的基础知识,也了解了一些数据结构,并且讲了有关C++的一些知识,也相信大家都掌握的不错,今天博主将会新开一个Linux专题,带领大家继续学习有关Linux的内容。今天第一篇文章博主首先带领大家了解一下什么是Linux,以及Linux的几个常用命令符

    2024年02月14日
    浏览(51)
  • 【linux】探索Linux命令行中强大的网络工具:netstat

    在Linux命令行中,有许多实用的工具可帮助我们管理和监控网络连接。其中一个最重要的工具就是netstat,它提供了丰富的网络连接和统计信息,帮助我们诊断网络问题并了解系统的网络状态。 在日常的网络管理和故障排除过程中,了解系统的网络连接情况是至关重要的。而在

    2024年02月09日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包