深入了解Linux信号:作用、产生、捕捉和阻塞

news2024/11/29 12:52:40

这里写目录标题

  • 引言
  • 1. 信号的基本概念
    • 1.1 信号的分类和编号:
    • 1.2 查看信号默认处理动作
    • 1.3 信号的作用
    • 1.4 信号的产生
      • 1.4.1通过终端按键产生
      • 1.4.2通过系统函数向进程发信号
      • 1.4.3由软件条件产生信号
      • 1.4.4硬件异常产生信号
  • 2. 常见信号及其作用
    • `SIGINT (2) - 中断信号:`
    • ` SIGTERM (15) - 终止信号:`
    • ` SIGKILL (9) - 强制终止信号:`
    • `SIGSEGV (11) - 段错误信号:`
      • 示例
    • 核心转储
      • 什么是核心转储?核心转储文件有什么用?
      • 应该怎么用?
        • 开启核心转储功能
        • 怎么查看
  • 3. 信号捕捉和处理
    • 3.1 信号捕捉函数
    • 3.2 sigaction 函数
      • 示例
  • 问题总结
        • 1. 上面所说的所有信号产生,最终都要有OS来进行执行,为什么?`OS是进程的管理者`
        • 2. 信号的处理是否是立即处理的?在合适的时候
        • 3. 信号如果不是被立即处理,那么信号是否需要暂时被进程记录下来?记录在哪里最合适呢?
        • 4. 一个进程在没有收到信号的时候,能否能知道,自己应该对合法信号作何处理呢?
        • 5. 如何理解OS向进程发送信号?能否描述一下完整的发送处理过程?
  • 4. 信号阻塞
    • 阻塞信号

引言

Linux操作系统中,信号是一种重要的进程间通信机制,用于通知进程发生了某些事件。信号既可以是来自内核的通知,也可以是由其他进程发送的。在本篇博客中,我们将深入探讨Linux信号的作用、产生机制、捕捉方式以及信号阻塞的概念。

1. 信号的基本概念

1.1 信号的分类和编号:

Linux中的信号被分类为标准信号实时信号

标准信号是最基本的信号类型,由整数编号表示,编号范围是1到31。
实时信号是Linux中的扩展信号类型,由整数编号表示,编号范围是32到64。

在这里插入图片描述

为什么31-34中间缺少两个信号无法展示呢?

kill -l 命令用于列出系统支持的所有信号名称,但它并不显示编号为32和33的信号。这是因为Linux中的信号被分类为标准信号和实时信号,而编号为32和33的信号属于实时信号,它们在kill -l的输出中不会显示。

1.2 查看信号默认处理动作

man 7 signal

在这里插入图片描述

1.3 信号的作用

信号在Linux系统中有多种作用,包括通知进程某个事件的发生、中断进程的正常执行、以及在进程间传递简单的消息等。不同的信号有不同的含义和影响,了解这些信号是理解Linux系统行为的关键(常见信号及作用请看第2点)。

1.4 信号的产生

信号可以由多种来源产生,包括硬件故障、用户输入、操作系统事件等。例如,Ctrl+C组合键产生SIGINT信号,表示中断信号,通常用于终止正在运行的程序。此外,操作系统还可以向进程发送信号以通知发生的事件,如进程终止、停止等。

1.4.1通过终端按键产生

当用户在终端上按下某些特定的按键组合时,操作系统会向进程发送相应的信号。例如,当用户按下Ctrl+C时,操作系统会向前台进程发送SIGINT信号。

#include <stdio.h>  
#include <signal.h>  
#include <unistd.h>  
  
void signal_handler(int signal) {  
    printf("Interrupt signal received.\n");  
    // 在这里可以添加代码来处理中断信号  
}  
  
int main() {  
    // 注册信号和信号处理函数  
    signal(SIGINT, signal_handler);  
  
    // 进入无限循环,等待信号  
    while (1) {  
        printf("Waiting for signal...\n");  
        sleep(1);  
    }  
  
    return 0;  
}

在上述示例中,程序通过无限循环等待信号。当用户按下Ctrl+C时,操作系统会向前台进程发送SIGINT信号,并调用之前注册的信号处理函数signal_handler。此时想要终止只能ctrl+\
在这里插入图片描述

1.4.2通过系统函数向进程发信号

在程序中,可以使用系统函数如kill()或raise()来向指定的进程发送信号。例如,使用kill()函数向指定进程ID的进程发送SIGTERM信号来终止该进程。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>

int main()
{
    int n=0;
    while(n++<5)
    {
        printf("i am %d\n",n);
    }
	pid_t pid = getpid();
	int signo = 9;
	kill(pid, signo);
    while(n++<10)
    {
        printf("i am %d\n",n);
    }
	return 0;
}

在这里插入图片描述

在上述示例中,使用kill()函数向指定进程ID的进程发送SIGTERM信号。kill()函数的第一个参数是要发送信号的进程ID,第二个参数是要发送的信号类型。在这个例子中,我们使用了SIGTERM信号来请求进程终止。

1.4.3由软件条件产生信号

在某些情况下,软件本身可能会根据特定的条件触发信号。例如,当某个进程遇到未处理的异常或错误时,它可能会向自身发送SIGSEGV信号,表示发生了段错误。这种信号通常是由软件内部的错误检测机制触发的。

#include <stdio.h>  
#include <stdlib.h>  
#include <signal.h>  
#include <string.h>  
#include <assert.h>  
  
void signal_handler(int signal) {  
    printf("Segmentation fault occurred.\n");  
    // 在这里可以添加代码来处理段错误信号  
}  
  
int main() {  
    char *buffer = NULL; // 未初始化的指针,可能导致段错误  
    *buffer = 'A'; // 试图访问未初始化的指针,触发段错误并发送SIGSEGV信号  
    return 0;  
}

在这里插入图片描述

在上述示例中,程序试图访问一个未初始化的指针buffer,这会导致段错误并触发SIGSEGV信号。当发生段错误时,操作系统会向进程发送SIGSEGV信号,并调用之前注册的信号处理函数signal_handler。在处理函数中,可以添加代码来处理段错误。需要注意的是,上述代码只是一个演示示例,实际开发中应该避免出现未初始化的指针。

1.4.4硬件异常产生信号

硬件异常产生信号的示例可以是除零异常。在计算机中,除零异常是一种常见的硬件异常。当一个进程试图除以零时,CPU的运算单元会产生异常,并将这个异常解释为SIGFPE信号发送给进程。

以下是一个示例代码,演示了除零异常的产生和处理:

#include <stdio.h>  
#include <signal.h>  
#include <stdlib.h>  
#include <unistd.h>  
  
void signal_handler(int signal) {  
    printf("Division by zero error occurred.\n");  
    exit(1); // 退出程序  
}  
  
int main() {  
    // 注册信号和信号处理函数  
    signal(SIGFPE, signal_handler);  
  
    int a = 10;  
    int b = 0;  
    int result = a / b; // 试图除以零,触发除零异常  
  
    printf("Result: %d\n", result); // 这行代码将不会被执行,因为程序在除零异常后退出  
  
    return 0;  
}

在上述示例中,程序试图将变量a除以变量b,其中b被初始化为0。这将触发除零异常,导致CPU的运算单元产生异常,并将这个异常解释为SIGFPE信号发送给进程。当进程接收到SIGFPE信号时,它会调用之前注册的信号处理函数signal_handler。在处理函数中,程序打印错误信息并退出。由于除零异常的严重性,操作系统通常会终止进程以防止进一步错误。

2. 常见信号及其作用

SIGINT (2) - 中断信号:

  • 作用:用于通知进程中断正在执行的操作,通常由用户通过键入 Ctrl+C 生成。
  • 例子:在终端中运行一个长时间执行的程序,用户可以按下 Ctrl+C 来发送 SIGINT 信号,终止程序的执行。
    在这里插入图片描述

SIGTERM (15) - 终止信号:

  • 作用:请求进程正常终止,允许进程清理资源和保存状态。
  • 例子:当系统关闭时,操作系统向所有运行的进程发送 SIGTERM 信号,请求它们正常退出。
    在这里插入图片描述

SIGKILL (9) - 强制终止信号:

  • 作用:立即终止进程,不允许进程清理资源或保存状态。
  • 例子:在系统管理员需要立即停止一个无响应的进程时,可以使用 kill -9 命令发送SIGKILL信号。
    在这里插入图片描述

SIGSEGV (11) - 段错误信号:

  • 作用:表示进程尝试访问其无法访问的内存区域,通常是由于指针错误或内存越界引起。
  • 例子:如果一个程序尝试访问已经释放的内存块,操作系统将发送SIGSEGV信号给该进程,使其崩溃。
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

// SIGSEGV信号处理函数
void segv_handler(int signum) {
    printf("Segmentation fault (SIGSEGV) caught. Exiting...\n");
    exit(EXIT_FAILURE);
}

int main() {
    // 设置SIGSEGV信号处理函数
    signal(SIGSEGV, segv_handler);

    int *ptr = NULL;

    // 尝试解引用空指针,导致段错误
    *ptr = 10;

    // 这里的代码不会执行,因为上面的语句导致了段错误
    printf("This line will not be reached.\n");

    return 0;
}

我们通过signal函数将segv_handler函数与SIGSEGV信号关联起来。当运行程序并尝试解引用空指针时,会触发段错误,然后程序会捕获SIGSEGV信号并执行segv_handler函数。在这个处理函数中,我们输出一条消息并调用exit退出程序。

SIGUSR1 (10) 和 SIGUSR2 (12) - 用户定义信号:

  • 作用:这两个信号没有预定义的含义,可以由用户自定义其作用。
  • 例子:某个应用程序可以使用这两个信号来触发自定义的操作,比如重新加载配置文件或执行特定的功能。

示例

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

// SIGUSR1信号处理函数
void sigusr1_handler(int signum) {
    printf("Received SIGUSR1 signal.\n");
    // 在这里执行与SIGUSR1相关的操作
}
// SIGUSR2信号处理函数
void sigusr2_handler(int signum) {
    printf("Received SIGUSR2 signal.\n");
    // 在这里执行与SIGUSR2相关的操作
}

int main() {
    // 设置SIGUSR1和SIGUSR2信号处理函数
    signal(SIGUSR1, sigusr1_handler);
    signal(SIGUSR2, sigusr2_handler);

    printf("My process ID: %d\n", getpid());
    printf("I am process[%d], Waiting for SIGUSR1 or SIGUSR2 signals...\n",getpid());

    while (1) {
        // 进程持续运行
        sleep(1);
    }

    return 0;
}

在这里插入图片描述

这些信号的作用覆盖了从正常终止到异常情况的多个场景,使得进程能够及时响应各种事件。在编写程序时,了解这些信号及其作用是确保程序稳定性和可维护性的关键。

核心转储

大多数信号默认行为都是中断进程,那么这些信号又有什么不同呢?

以下述例子为例,我们中断一个进程可以用ctrl+c也可以用ctrl+\,但是这两者有什么不同呢?
在这里插入图片描述
在这里插入图片描述
可以看到两者的行为(Actrion并不相同),一个为Trem,另一个为Core

  • term信号是终止进程的信号,当进程接收到term信号时,它可以选择正常退出或执行一些清理操作后退出。

  • core信号则是核心转储信号,当进程接收到core信号时,它会被终止,并且操作系统会生成一个核心转储文件(core dump file)。这个核心转储文件包含了进程在内存中的数据和堆栈信息,对于调试程序和排查问题非常有用。

简单来说,term信号是用来终止进程的,而core信号则是用来生成核心转储文件的。

什么是核心转储?核心转储文件有什么用?

核心转储(coredump)是Unix和类Unix系统中的一种机制,用于在程序发生错误时,将程序当时的内存状况、寄存器状况、系统状态等,以转储文件(coredump文件)的形式保存下来。这个机制对于程序的错误调试、系统故障排查等,是非常有用的。

核心转储的生成通常是由操作系统在检测到程序异常终止(如:段错误)时自动进行的。当程序异常终止时,操作系统会首先尝试生成核心转储文件。如果系统支持核心转储,并且用户有相应的权限,就可以通过ulimit命令来设置生成核心转储文件的开关。

核心转储文件包含的内容通常包括:程序状态(例如程序计数器、堆栈指针等)、内存管理信息(例如内存区域的大小、位置等)、处理器和操作系统的标志和信息等。这些信息对于调试程序、理解程序运行状态以及排查系统故障非常有帮助。

对于核心转储文件的解释,需要使用调试器(例如gdb)或者其他专用工具(例如objdump)来进行。调试器可以查看转储进程的地址空间,包括查看变量的值、源代码等。如果系统中没有可用的符号表,调试器可能只能显示一些地址或者十六进制数值,但仍然可以帮助程序员进行故障排查。

总的来说,核心转储是一种重要的系统机制,可以帮助程序员和系统管理员更好地理解和解决程序和系统中的问题。

简要的说,核心转储就是一个文件,记录了进程在运行过程中发生错误时的内存状态,以及有助于调试的信息

应该怎么用?

开启核心转储功能
ulimit -a	//查看前资源限制的设定。

在这里插入图片描述
从图中可以看出默认大小为0,也就是默认关闭状态

ulimit -c size	//设置core文件的大小

ulimit -c size 命令用于设置核心转储文件的大小限制。其中,size 可以是一个具体的数值,也可以是一个关键字 unlimited。

  • 如果将 size 设置为一个具体的数值,例如 ulimit -c 10000,则核心转储文件的大小将被限制为 10000 字节。如果进程生成的核心转储文件超过了这个大小,那么只有部分数据会被写入到文件中
  • 如果将 size 设置为 unlimited,则核心转储文件的大小将不受限制,可以生成任意大小的核心转储文件。

在这里插入图片描述
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/779b780249c149ada1072960a66a30af.png
上述案例可以看出

  1. ulimit只对一个终端有效,其他终端想要改变必须重新设置
  2. 生成的核心转储文件以core.pid命名
怎么查看

我们得到的core.pid文件是一个二进制文件,那么我怎么查看呢?

这里就要搬出GDB来帮助我们进行使用了,在GDB中查看核心转储文件的内容需要使用一些特定的命令。以下是一些常用的命令:

命令名称命令作用
file:指定核心转储文件。例如,如果你知道核心转储文件名为core,你可以使用命令"file core"来指定要查看的文件。
info proc mappings:显示程序的内存映射情况。这可以帮助你理解程序是如何使用内存的,以及哪些内存区域是可访问的。
info proc stat:显示程序的状态信息,例如程序计数器、堆栈指针等。
info proc symbols:显示程序的符号表信息,例如函数名称、变量名称等。
print:打印变量的值。例如,如果你知道在发生段错误之前某个变量被设置了一个值,你可以使用"print"命令来查看这个变量的值。
backtrace:显示堆栈跟踪信息,这可以帮助你理解程序执行时的调用栈情况。
list:显示源代码。你可以使用"list"命令来查看特定函数或代码段的源代码。
step和next:单步执行程序。你可以使用"step"命令来逐行执行程序,或者使用"next"命令来执行下一行而不进入函数调用。
break:设置断点。你可以使用"break"命令来在特定位置设置断点,以便在程序执行到该位置时停止并查看相关信息。
#include <stdio.h>  
#include <stdlib.h>  
#include <signal.h>  
#include <unistd.h>  
  
int main()
{
    printf("i am process [%d]\n",getpid());
    sleep(2);
    int a=10/0;	//代码出现除0错误
    return 0;
}

2秒后崩溃,产生core.pid文件

在这里插入图片描述
gdb调试可知是由8号信号导致——8号信号是SIGFPE

SIGFPE它表示浮点数异常(Floating Point Exception)。当进程进行浮点数运算时,如果发生了异常,如除以零、溢出、下溢等,操作系统会向进程发送SIGFPE信号。

3. 信号捕捉和处理

Linux允许进程捕捉和处理信号,以执行自定义的操作。信号处理可以通过以下方式实现:

3.1 信号捕捉函数

在C语言中,可以使用 signal 函数来设置信号的处理函数。例如:

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

void handler(int signal)
{
	printf("i am process[%d],get a signal:%d\n",getpid(),signal);
	// 处理信号的代码
}
int main()
{
	int signo;
	// 设置 1~31信号 的处理函数为 sigint_handler
	for (signo = 1; signo <= 31; signo++){
		signal(signo, handler);
	}
	while (1){
		sleep(1);
		// 进程持续运行
	}
	return 0;
}

在这里插入图片描述

但是为什么不能捕捉 9 号信号呢?

  1. 9号信号(SIGKILL)是不能被捕捉的。这是因为SIGKILL信号是Linux系统中的一种特殊信号,它具有最高的优先级,并且无法被进程捕获、阻塞或忽略。当进程收到SIGKILL信号时,它会被立即终止,而无法执行任何处理逻辑。

  2. 这种设计是为了确保系统的稳定性和安全性。SIGKILL信号通常用于强制结束一些不响应或处于异常状态的系统进程,以防止它们对系统造成损害或影响其他进程的正常运行。因此,为了确保这种强制终止的执行,SIGKILL信号不能被捕获或修改其默认行为。

3.2 sigaction 函数

sigaction 函数是一种更为可靠和灵活的处理信号的方式,相较于 signal 函数,它提供了更多的控制选项。它允许指定处理函数、设置标志和提供可选的信号屏蔽。

   #include <signal.h>

   int sigaction(int signum, const struct sigaction *act,
                 struct sigaction *oldact);
    The sigaction structure is defined as something like:

    struct sigaction {
        void     (*sa_handler)(int);
        void     (*sa_sigaction)(int, siginfo_t *, void *);
        sigset_t   sa_mask;
        int        sa_flags;
        void     (*sa_restorer)(void);
    };

示例

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

// SIGUSR1 信号处理函数
void sigusr1_handler(int signum) {
    printf("Received SIGUSR1 signal.\n");
    // 在这里执行与 SIGUSR1 相关的操作
}

int main() {
    struct sigaction sa;

    // 设置 sa 结构体
    sa.sa_handler = sigusr1_handler;
    sa.sa_flags = 0;

    // 清空 sa_mask,即不阻塞任何其他信号
    sigemptyset(&sa.sa_mask);

    // 使用 sigaction 函数关联 SIGUSR1 信号和处理函数
    if (sigaction(SIGUSR1, &sa, NULL) == -1) {
        perror("Error setting up SIGUSR1 handler");
        exit(EXIT_FAILURE);
    }

    printf("My process ID: %d\n", getpid());
    printf("Waiting for SIGUSR1 signal...\n");

    while (1) {
        // 进程持续运行
        sleep(1);
    }

    return 0;
}

上述示例中我们使用了 sigaction 函数来设置对 SIGUSR1 信号的处理。struct sigaction 结构体用于指定信号处理函数及其他相关属性。在 main 函数中,我们设置了 sa_handler 为 sigusr1_handler,表示接收到 SIGUSR1 信号时执行这个处理函数。
上述示例中,我们还清空了 sa_mask,即不阻塞任何其他信号。如果你希望在信号处理期间阻塞某些其他信号,可以使用 sigaddset 等函数来设置 sa_mask。

此外,sa_flags 可以用来设置不同的标志,例如 SA_RESTART 用于指示系统调用在接收到信号后是否应该自动重启。

问题总结

1. 上面所说的所有信号产生,最终都要有OS来进行执行,为什么?OS是进程的管理者

操作系统负责管理系统资源和协调各个进程之间的交互。信号是一种用于进程间通信和处理异步事件的机制。当进程发生某些事件(例如错误、用户输入等)时,会生成相应的信号。操作系统作为进程的管理者,负责接收、传递和执行这些信号,以确保系统的正常运行和进程的协同工作。

2. 信号的处理是否是立即处理的?在合适的时候

信号的处理可以是立即的,也可以是在合适的时候执行。某些信号(如SIGKILL)会立即终止进程,而其他一些信号可以被捕获并延迟处理,直到进程执行适当的信号处理程序。

3. 信号如果不是被立即处理,那么信号是否需要暂时被进程记录下来?记录在哪里最合适呢?

是的,如果信号不是立即处理,通常会被记录下来。记录的方式可以是将信号挂起,或者将其加入到进程的信号队列中。这样,进程在适当的时候可以检查这些信号并执行相应的处理。

4. 一个进程在没有收到信号的时候,能否能知道,自己应该对合法信号作何处理呢?

进程通常可以事先定义信号处理函数(Signal Handler),这样当信号发生时,操作系统会调用相应的信号处理函数。通过注册信号处理函数,进程可以在没有收到信号时就定义好对合法信号的处理方式。

5. 如何理解OS向进程发送信号?能否描述一下完整的发送处理过程?

当发生与进程相关的事件时,操作系统会生成相应的信号并将其发送给目标进程。信号可以通过系统调用(例如kill)或其他机制发送。发送过程包括以下步骤:
操作系统生成信号: 根据事件的性质,操作系统生成相应的信号。
确定目标进程: 确定将信号发送给哪个进程。这可以通过进程标识符(PID)等方式来指定目标进程。
发送信号: 将信号发送给目标进程。这可以通过向进程发送软中断、修改进程的状态等方式实现。
触发信号处理: 如果进程注册了信号处理函数,操作系统会调用该函数来处理信号。如果没有注册处理函数,操作系统可能会采取默认的处理方式,例如终止进程或忽略信号。
总体而言,操作系统向进程发送信号是一种异步的通信方式,用于通知进程发生了某些事件或需要进行特定的处理

4. 信号阻塞

信号阻塞是指进程暂时屏蔽对某些信号的处理。在某些情况下,我们希望在处理一个信号时,暂时阻塞掉其他同类的信号,以确保处理的完整性。可以使用 sigprocmask 函数来设置信号阻塞。

#include <signal.h>

// 阻塞 SIGINT 信号
sigset_t new_mask, old_mask;
sigemptyset(&new_mask);
sigaddset(&new_mask, SIGINT);
sigprocmask(SIG_BLOCK, &new_mask, &old_mask);

// 执行一些需要阻塞 SIGINT 的操作

// 恢复原信号屏蔽状态
sigprocmask(SIG_SETMASK, &old_mask, NULL);

示例

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

// SIGUSR1 信号处理函数
void sigusr1_handler(int signum) {
    printf("Received SIGUSR1 signal.\n");
    // 在这里执行与 SIGUSR1 相关的操作
}

int main() {
    // 设置 SIGUSR1 信号处理函数
    signal(SIGUSR1, sigusr1_handler);

    printf("My process ID: %d\n", getpid());
    printf("Waiting for SIGUSR1 signal (blocked)...\n");

    // 创建一个集合来存储被阻塞的信号
    sigset_t block_mask;
    sigemptyset(&block_mask);  // 清空信号集

    // 将 SIGUSR1 加入到被阻塞的信号集中
    sigaddset(&block_mask, SIGUSR1);

    // 使用 sigprocmask 阻塞指定信号
    if (sigprocmask(SIG_BLOCK, &block_mask, NULL) == -1) {
        perror("Error blocking SIGUSR1");
        exit(EXIT_FAILURE);
    }

    // 在这里进行一些工作,期间 SIGUSR1 信号会被阻塞

    // 解除对 SIGUSR1 的阻塞
    if (sigprocmask(SIG_UNBLOCK, &block_mask, NULL) == -1) {
        perror("Error unblocking SIGUSR1");
        exit(EXIT_FAILURE);
    }

    printf("Waiting for SIGUSR1 signal (unblocked)...\n");

    while (1) {
        // 进程持续运行
        sleep(1);
    }

    return 0;
}

上述示例中,首先使用 sigemptyset 清空信号集,然后使用 sigaddset 将SIGUSR1
添加到被阻塞的信号集中。接着,使用 sigprocmask 函数将这个信号集应用到当前进程,从而阻塞了 SIGUSR1 信号。
在需要解除对信号的阻塞时,同样使用 sigprocmask 函数,但这次使用 SIG_UNBLOCK 标志。这样就解除了对
SIGUSR1 信号的阻塞。
这个过程演示了如何在程序运行过程中阻塞和解除阻塞指定的信号。在实际应用中,这种机制通常用于确保某些关键部分的原子性,防止信号中断影响程序的正常执行。

阻塞信号

信号其他相关常见概念

  • 实际执行信号的处理动作,称为信号递达(Delivery)。
  • 信号从产生到递达之间的状态,称为信号未决(pending)。
  • 进程可以选择阻塞(Block)某个信号。
  • 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。
  • 需要注意的是,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后的一种处理动作。

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

在这里插入图片描述

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1322112.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

你对葡萄酒中的亚硫酸盐是不是有误解呢?

亚硫酸盐不会让某些人对酒产生不良的反应首先&#xff0c;让我谈谈欧洲生产的葡萄酒不含亚硫酸盐的观点&#xff0c;这在很大程度上是一种误解。虽然我听说过某些生产商在酿造葡萄酒时不添加亚硫酸盐&#xff0c;但这些确实是例外&#xff0c;添加亚硫酸盐是世界公认的酿酒传统…

如何通过MT-Bench评估大模型

如何评估LLM在开放性问题的回答能力 前面三篇博客中介绍了如何评估大模型&#xff0c;内容包括评估大模型时常用的指标&#xff0c;每个指标背后的含义&#xff0c;如何通过编写代码实现指标的收集。对于passk指标&#xff0c;还进行了专门的说明。在前面的博客中&#xff0c;我…

【PC端查看PDA扫描结果】

1、生成条形码 https://www.gaitubao.com/barcode 2、打开Android Studio 进行下图这样配置&#xff0c;那么就能看到pda扫描的数据了 Decoding result&#xff1a;

Jenkins插件安装失败时这么做就搞定啦!

1.网络或墙的问题导致插件下载安装失败 这种错误提示很明显&#xff0c;就是无法连接到插件下载地址&#xff0c;导致插件下载失败。 解决方法 为Jenkins更换源 点击Jenkins主页面左侧列表中【系统管理】—— 下拉找到【管理插件】 选择【高级】选项卡 替换最下方【升级站点…

PTFE四氟托盘应用于化工、医药、食品行业

PTFE托盘是一种广泛应用于化工、医药、食品等行业的特种托盘&#xff0c;采用聚四氟乙烯&#xff08;PTFE&#xff09;材料制造而成。PTFE是一种具有出色耐腐蚀性、高温稳定性和优异物理特性的材料。 PTFE托盘在化工领域中被广泛应用&#xff0c;主要用于处理腐蚀性的化学物质。…

【极客公园 IF 2024】李彦宏:AI-native时代,需要怎样的产品和开发者

文章目录 01 卷 AI 原生应用才有价值&#xff0c;大模型进展 对 多数人不是机会02 大公司代表落后生产力&#xff0c;千万不要看大公司在干嘛03 搜索本身也在进化&#xff0c;有机会变成全新的产品04 用生成式AI改造现有业务&#xff0c;可以创造更大价值05 创业公司能做出三五…

LeetCode刷题--- 全排列 II

个人主页&#xff1a;元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客 个人专栏 力扣递归算法题 http://t.csdnimg.cn/yUl2I 【C】 http://t.csdnimg.cn/6AbpV 数据结构与算法 http://t.csdnimg.cn/hKh2l 前言&#xff1a;这个专栏主要讲述递归递归、搜…

【微服务架构】——hystrix基础知识

文章目录 一、Hystrix是什么&#xff1f;二、为什么要使用Hystrix&#xff1f;三、在哪用&#xff1f;什么时候用&#xff1f;四、Hystrix是如何实现自动服务降级和服务熔断的呢&#xff1f; 一、Hystrix是什么&#xff1f; Hystrix是一个用于处理分布式系统的延迟和容错的开源…

智慧安防视频监控可视化平台EasyCVR调用接口返回“Unauthorized”是什么原因?

智慧安防视频监控可视化平台EasyCVR采用了开放式的网络结构&#xff0c;平台能在局域网、公网、专网等复杂的网络环境中&#xff0c;将场景中分散的海量网络监控设备进行统一接入与汇聚管理&#xff0c;并能提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云…

在 Windows PC 上轻松下载并安装 FFmpeg

FFmpeg 是一种开源媒体工具&#xff0c;可用于将任何视频格式转换为您需要的格式。该工具只是命令行&#xff0c;因此它没有图形、可点击的界面。如果您习惯使用常规图形 Windows 程序&#xff0c;安装 FFmpeg 一开始可能看起来很复杂&#xff0c;但不用担心&#xff0c;它;很简…

xhr请求是什么?

1、什么是xhr请求&#xff1f; xhr&#xff0c;全称为XMLHttpRequest&#xff0c;用于与服务器交互数据&#xff0c;是ajax功能实现所依赖的对象&#xff0c;jquery中的ajax就是对 xhr的封装。 XHR&#xff08;XMLHttpRequest&#xff09;请求是一种在浏览器中发送HTTP请求的…

互式流程图|BPMN JointJS+ JavaScript 3.7.3 Crack

JointJS 是 JavaScript 图表库为卓越的 UI 提供支持 使用经过验证的库快速、自信地构建高级视觉和无代码/低代码应用程序。 赋能全球行业领导者 使用 JointJS 构建的图表 一个库&#xff0c;‍无限 UI 选项 直接在您的应用程序中享受交互式流程图、BPMN 和其他图表工作室。利用…

matplotlib科研绘图之折线图、柱状图、散点图、误差棒

matplotlib折线图例子1 # -*- coding: utf-8 -*- # Time : 2023/12/19 10:56 # Author : 长沙有肥鱼 # FileName: 21.py # Software: PyCharm # Blog : https://blog.csdn.net/weixin_53660567?spm1010.2135.3001.5343# 导入Matplotlib库 import matplotlib import ma…

【Vue】el-date-picker日期范围组件(本周、本月、上周)

系列文章 【Vue】vue增加导航标签 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/134965353 【Vue】Element开发笔记 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/133947977 【Vue】vue&#xff0c;在Windows IIS平台…

proxysql读写分离组件部署

一、前言 在mysql一主两从架构的前提下&#xff0c;引入读写分离组件&#xff0c;可以极大的提高mysql性能&#xff0c;proxysql可以在高可用mysql架构发生主从故障时&#xff0c;进行自动的主从读写节点切换&#xff0c;即当mysql其他从节点当选新的主节点时&#xff0c;proxy…

使用yolov8和moviepy自动截取视频中人出现的片段

前言 这么长时间没写博客&#xff0c;其实主要是忙于一个行为实时检测大型项目的开发&#xff0c;最近闲下来就写这篇当年末总结了。这篇文章的起因还是某个业务需求&#xff0c;要求分析视频中有人的部分&#xff0c;没人的部分需要去掉&#xff0c;同时行为检测的数据集如果…

沉浸式数字文旅黑科技!用AI数字人升级景区体验

这年头文旅界也太卷了&#xff01; 在国家文化数字化战略的深入实施下&#xff0c;各地方文旅纷纷打造新型消费场景&#xff0c;以数字文旅提升消费产品的互动性和社交性&#xff0c;增强用户沉浸式体验。 其中&#xff0c;数字人乘着AI大语言模型的东风&#xff0c;被文旅品牌…

SAP 特殊采购类80简介

前面我们已经测试特殊采购类40、70,我们今天测试一下特殊采购类80。 特殊采购类80:在替代工厂生产,在成品层维护特殊采购类,需求和收货在计划工厂完成,成品生产和组件采购在生产工厂完成。 80采购类也是我们在SAP系统中实现跨工厂需求传递的一种方式。它具有传递方式简单、…

破局:国内母婴市场“红利减退”,母婴店如何拓客引流裂变?

破局&#xff1a;国内母婴市场“红利减退”&#xff0c;母婴店如何拓客引流裂变&#xff1f; 背景&#xff1a;中国母婴市场近年来人口出生率一直在恒定范围值&#xff0c;国家也在鼓励优生、多生政策&#xff0c;并且随着互联网的高速发展&#xff0c;人均可支配收入也在增加&…

XC6617 双路输出电容式触摸感应加PWM调光芯片 适用于触摸台灯、触摸调光LED灯

XC6617是一款单路输入双路输出电容式触摸感应PWM调光IC XC6617可在有介质(如玻璃、亚克力、塑料、陶瓷等)隔离保护的情况下实现触摸功能&#xff0c;安全性高。XC6617内置稳压源、上电复位/低压复位及环境自适应算法等多种措施&#xff0c;抗干扰性能优秀。 应用范围:…