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

这里写目录标题

  • 引言
  • 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是这样实现的:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。不讨论实时信号。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

智慧水务系统解决了哪些问题

随着城市化进程的加快&#xff0c;水资源的日益紧缺和水环境问题的日益突出&#xff0c;传统的水务管理方式已经无法满足现代城市的需求。为了解决这些问题&#xff0c;智慧水务系统应运而生。 一、智慧水务系统概述 智慧水务系统是一种基于物联网、大数据、云计算等先进技术…

回归预测 | MATLAB实现NGO-SCN北方苍鹰算法优化随机配置网络的数据回归预测 (多指标,多图)

回归预测 | MATLAB实现NGO-SCN北方苍鹰算法优化随机配置网络的数据回归预测 &#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现NGO-SCN北方苍鹰算法优化随机配置网络的数据回归预测 &#xff08;多指标&#xff0c;多图&#xff09;效果一览基本介绍…

前端检测字符串中是否含有特殊字符,并返回该特殊字符

一、判断字符串中是否含有特殊字符 const hasSpecicalCharacter (str) > {var regex /[!#$%^&*(),.?":{}|<>]/return regex.test(str) } //含有特殊字符返回true, 没有特殊字符返回false 二、判断字符串中是否含有特殊字符&#xff0c;并返回该特殊字符…

作业--day32

机械臂 #include <myhead.h>#define PORT 8888 #define IP "192.168.125.59"int main(int argc, const char *argv[]) {int sfd socket(AF_INET, SOCK_STREAM, 0);if(sfd -1){perror("socket error");return -1;}int reuse -1;if(setsockopt(sfd…

C# 调用腾讯混元大模型

写在前面 今天用C#调用了一下腾讯混元大模型&#xff0c;调用代码贴一下&#xff0c;具体的效果等深入使用后再来评价。 GitHub - TencentCloud/tencentcloud-sdk-dotnet: Tencent Cloud API 3.0 SDK for .NET 腾讯混元大模型简介_腾讯混元大模型购买指南_腾讯混元大模型操作…

等保测评主要保护哪些方面的安全?

等保测评是经公安部认证的具有资质的测评机构&#xff0c;依据国家信息安全等级保护规范规定&#xff0c;受有关单位委托&#xff0c;按照有关管理规范和技术标准&#xff0c;对信息系统安全等级保护状况进行检测评估的活动。那么企业做等保“保”的是什么呢&#xff1f; 等保主…

直播江湖:东方甄选与董宇辉的权力游戏

出品| 大力财经 文 | 魏力 近期&#xff0c;围绕东方甄选的小作文事件引起了广泛关注&#xff0c;有人将其解读为一场巧妙策划的事件营销&#xff0c;然而&#xff0c;舆情的不可控性使得事态逐渐演变为一场复杂的利益博弈。 东方甄选与董宇辉的“蜜月期”可以说是双方互相成就…

直流电、交流电和发电机、接地、变压器

直流电 此节内容主要摘录自&#xff1a;图文详解直流电与直流电路基本知识 直流电是指电流方向不随时间作周期性变化&#xff0c;由正极流向负极&#xff0c;但电流的大小可能会变化的电流。直流电可以分为稳定&#xff08;恒定&#xff09;直流和脉动直流两种&#xff0c;如下…

Re解析(正则表达式解析)

正则表达式基础 元字符 B站教学视频&#xff1a; 正则表达式元字符基本使用 量词 贪婪匹配和惰性匹配 惰性匹配如下两张图&#xff0c;而 .* 就表示贪婪匹配&#xff0c;即尽可能多的匹配到符合的字符串&#xff0c;如果使用贪婪匹配&#xff0c;那么结果就是图中的情况三 p…

ZKP Commitment (1)

MIT IAP 2023 Modern Zero Knowledge Cryptography课程笔记 Lecture 5: Commitment 1 (Ying Tong Lai) Overview: Modern SNARK IOP: Interactive Oracle ProofCommitment SchemeIOP “compiled by” the commitment scheme to get a non-interactive proofAn IOP is “inform…

【重点】【前缀树|字典树】208.实现Trie(前缀树)

题目 前缀树介绍&#xff1a;https://blog.csdn.net/DeveloperFire/article/details/128861092 什么是前缀树 在计算机科学中&#xff0c;trie&#xff0c;又称前缀树或字典树&#xff0c;是一种有序树&#xff0c;用于保存关联数组&#xff0c;其中的键通常是字符串。与二叉查…

windows10-tdengine的安装及使用

win10-tdengine的安装及使用 一、下载及安装配置1.1 下载安装1.2 配置 二、启动及关闭服务2.1 启动tdengine服务2.2 关闭tdengine服务2.2 开机自启动配置 四、可视化工具&#xff08;GUI&#xff09;4.1 下载4.2 安装 五、TDengine 命令行&#xff08;CLI&#xff09;5.1 进入命…

st.pp.normalize_total(data) # NOTE: no log1p

这段代码在使用 stlearn 包中的 st.pp.normalize_total 函数对数据进行总体计数标准化。标准化后&#xff0c;每个细胞的总计数都将等于 median(total_counts)。 NOTE: no log1p 这行注释表示在标准化后&#xff0c;数据不会进行 log1p 转换。log1p 转换将每个计数值增加 1&a…

【每日一题】1901. 寻找峰值 II-2023.12.19

题目&#xff1a; 1901. 寻找峰值 II 一个 2D 网格中的 峰值 是指那些 严格大于 其相邻格子(上、下、左、右)的元素。 给你一个 从 0 开始编号 的 m x n 矩阵 mat &#xff0c;其中任意两个相邻格子的值都 不相同 。找出 任意一个 峰值 mat[i][j] 并 返回其位置 [i,j] 。 你…

Java对象结构

Java 对象(Object 实例)结构包括三部分:对象头、对象体、对齐字节。 Object的三个部分 对象头包括三个字段&#xff0c;第一个字段叫做 Mark Word(标记字)&#xff0c;用于存储自身运行时的数据 例如 GC 标志位、哈希码、锁状态等信息。 第二个字段叫做 Class Pointer(类对象…

CEC2013(python):五种算法(HHO、WOA、GWO、DBO、PSO)求解CEC2013(python代码)

一、五种算法简介 1、哈里斯鹰优化算法HHO 2、鲸鱼优化算法WOA 3、灰狼优化算法GWO 4、蜣螂优化算法DBO 5、粒子群优化算法PSO 二、5种算法求解CEC2013 &#xff08;1&#xff09;CEC2013简介 参考文献&#xff1a; [1] Liang J J , Qu B Y , Suganthan P N , et al. P…

设计模式(三)-结构型模式(5)-外观模式

一、为何需要外观模式&#xff08;Facade&#xff09;? 要实现一个大功能&#xff0c;我们需要将它拆分成多个子系统。然后每个子系统所实现的功能&#xff0c;就由一个称为外观的高层功能模块来调用。这种设计方式就称为外观模式。该模式在开发时常常被使用过&#xff0c;所…

翻译: LLMs大语言模型影响到高工资的的白领知识工作者 加速各行各业的自动化潜力 Automation potential across sectors

我们已经探讨了生成人工智能可能对您的工作有用&#xff0c;也讨论了分析其对企业的影响。现在&#xff0c;让我们拉远镜头&#xff0c;看看它对不同公司的工作角色以及对不同行业部门的影响。这个视频的结果对特定企业可能不那么直接可行&#xff0c;但也许这会帮助您思考并尝…

文件包含的提升刷题

上一篇文章&#xff1a;一篇文章带你入门文件包含-CSDN博客 已经开始入门了文件包含&#xff0c;那现在开始拔高提升刷题&#xff01; 1. 拿到题目后啥也没有&#xff0c;所以也不知道要读取啥文件&#xff0c;那就查看源代码。 直接看if的条件就可以知道一定要设置cookie&a…

【C++】模板--函数模板.类模板

目录 一 函数模板 1 概念 2 函数模板格式 3 函数模板的原理 4 模板参数的匹配原则 1 多个模板参数 2 非模板函数和同名的函数模板 5 函数模板实例化 1. 隐式实例化 2. 显式实例化 二 类模板 1 类模板定义格式 2 类模板实例化 3 细节 一 函数模板 1 概念 函数模…