Linux实时信号不会丢失

文章通过对比Linux中的实时信号与非实时信号在处理中的差异,解释了实时信号不会丢失的原因,即实时信号使用队列管理,能记录重复发送的信号。通过两个程序示例展示了如何阻塞和发送实时信号,证明了在阻塞期间实时信号不会丢失,而是会在解除阻塞后按发送次数响应。

Linux中的 实时信号 (又称为 可靠信号 )在被阻塞的时候,不会像 非实时信号 不可靠信号 )那样丢失。

因为 非实时信号的等待信号集是以集合的数据结构进行管理的 ,所谓集合有一个重要的性质就是元素不能够重复,而 实时信号是以队列的形式来管理等待信号的,所以不会存在信号丢失的问题

对于 非实时信号 ,当进程的信号掩码中存在某个非实时信号,那么当这个非实时信号发送给进程的时候,会被内核阻塞,内核会将其放在一个叫做等待信号集的数据结构里面,只有此信号从进程的信号掩码中移除之后,内核才会把等待信号集中的该信号发送给进程,如果在信号阻塞期间,又发送来了相同的信号,那么由于集合的元素不可重复,所以这个重复发送的信号将会被内核丢弃,称为信号丢失,当解除阻塞之后,仅仅会响应一次被阻塞的信号处理函数

对于实时信号而言,由于等待信号(即被内核阻塞期间又发送同样的信号)采用队列管理,所以不存在信号丢失的情况发生,如果某个信号在阻塞期间又多次发送,那么内核会记录信号发送的次数。也就是说:实时信号是可以在阻塞期间重复发送,而不会丢失的,当解除阻塞之后,会多次响应实时信号的信号处理函数!

怎么验证实时信号不可以被阻塞呢?

当然是实践出真知喽:下面通过代码来实践一下:

程序1:阻塞接收来此其他进程发送的实时信号

/**
  ****************************************************************************************
  * @file    09_sigaction.c
  * @author  GuiStar-李什么恩
  * @version V1.1.0
  * @date    2023-4-14
  * @brief   挂起进程,阻塞参数1指定的实时信号,当接收到SIGINT信号之后,进程会被唤醒
  *          并取消对参数1指定的实时信号的阻塞(即从信号掩码中移除),此程序用来验证实
  *          时信号不会丢失
  ****************************************************************************************
  */
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>

static void sig_handler1(int sig)
{
    printf("接收到SIGINT,即将唤醒程序!\n");
}

static void sig_handler2(int sig, siginfo_t *info, void *context)
{
    sigval_t sig_val = info->si_value;
    printf("接收到实时信号: %d\n", sig);
    printf("伴随数据为: %d\n", sig_val.sival_int);
    printf("发送方的PID是:%d\n",info->si_pid);
}

int main(int argc, char *argv[])
{
    struct sigaction sig = {0};
    sigset_t sig_set;
    int num;

    /* 判断传参个数 */
    if (argc<2)
    {
        printf("参数过少\n");
        exit(-1);
    }
    /* 获取用户传递的参数 */
    num = atoi(argv[1]);

    sigemptyset(&sig_set);
    sigaddset(&sig_set, num);

    /* 为实时信号绑定处理函数 */
    sig.sa_handler = sig_handler1;
    sig.sa_flags = 0;
    if (sigaction(SIGINT, &sig, NULL)==-1)
    {
        perror("sigaction error");
        exit(-1);
    }
    sig.sa_sigaction = sig_handler2;
    sig.sa_flags = SA_SIGINFO;
    if (sigaction(num, &sig, NULL)==-1)
    {
        perror("sigaction error");
        exit(-1);
    }
    
    /*挂起此进程,等待SIGINT信号来唤醒,同时阻塞用户给此进程参数指定的实时信号*/
    printf("开始挂起进程,同时阻塞%d信号\n",num);
    if (sigsuspend(&sig_set)!=-1)
    exit(-1);

    printf("程序唤醒成功!\n");


    exit(0);
}



程序2:向某个进程发送实时信号:

/**
  ****************************************************************************************
  * @file    09_sigqueue.c
  * @author  GuiStar-李什么恩
  * @version V1.1.0
  * @date    2023-4-14
  * @brief   向某个进程发送实时信号,参数1指定接受进程的pid号,参数2指定要发送的实时信号的编号
  ****************************************************************************************
  */
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

int main(int argc, char *argv[])
{
    union sigval sig_val;
    int pid;
    int sig;
    /* 判断传参个数 */
    if (argc<3)
    {
        puts("参数过少,参数一:pid,参数二:实时信号");
        exit(-1);
    }

    /* 获取用户传递的参数 */
    pid = atoi(argv[1]);
    sig = atoi(argv[2]);
    printf("pid: %d\nsignal: %d\n", pid, sig);

    /* 发送信号 */
    sig_val.sival_int = 10; //伴随数据
    if (sigqueue(pid, sig, sig_val)==-1) 
    {
        perror("sigqueue error");
        exit(-1);
    }
    puts("信号发送成功!");
    exit(0);
}

运行结果:

  • 首先运行程序1,程序首先会挂起,会阻塞指定的实时信号34,如果按下ctrl+c,发送SIGINT信号,则程序会被唤醒,同时解除对实时信号34的阻塞
    在这里插入图片描述

  • 然后,在查询到程序1的pid之后,多次运行程序2,向程序1的进程多次发送实时信号34。此时进程1是被挂起的,并且实时信号34被内核阻塞,所以在进程1的终端看不到响应信息。只有取消阻塞,进程1才会响应这些实时信号34

在这里插入图片描述


  • 按下ctrl+c,发送SIGINT信号,进程1被唤醒从下图中可以看出,进程1确实多次响应了在阻塞期间发送的实时信号34

在这里插入图片描述


至此实时信号不会被丢弃得到验证!

<think>嗯,用户问的是Linux实时信号会打断哪些系统调用。这个问题我需要仔细回忆一下之前学过的知识,可能还要查证一些资料确保准确。首先,我记得Linux信号分为标准信号实时信号实时信号是从SIGRTMIN到SIGRTMAX的那些。实时信号相比标准信号有什么不同呢?比如排队机制,多个实时信号会按顺序传递,不会被合并。而标准信号如果多次发送可能会被合并成一个。 然后关于系统调用被打断的情况。系统调用被信号打断通常是因为信号处理函数被设置,当进程正在执行一个系统调用时,如果收到信号,系统调用可能会被中断,返回EINTR错误。不过,这取决于系统调用是否被信号处理函数设置为自动重启,或者是否使用了SA_RESTART标志。但是实时信号的行为是否有所不同呢? 我记得实时信号在某些情况下可能会影响不同的系统调用。比如,一些慢速系统调用(如read、write、wait等)在被信号中断后会返回EINTR,而实时信号可能会更倾向于打断这些调用。但需要具体确定哪些系统调用会被实时信号打断。另外,可能有些系统调用即使被信号中断也不会返回EINTR,比如某些阻塞在不可中断状态的操作,比如等待磁盘I/O。 另外,用户可能还关心实时信号和标准信号在打断系统调用方面有什么不同。比如,是否实时信号更有可能导致系统调用中断,或者是否某些系统调用对实时信号特别敏感。此外,可能需要提到SA_RESTART标志的作用,当使用sigaction设置信号处理时,如果指定了SA_RESTART,那么被中断的系统调用会自动重启,而不是返回EINTR。但实时信号是否默认带有这个标志,或者是否行为不同? 还有,可能需要举例说明哪些常见的系统调用会被实时信号打断,比如accept、read、write、sleep类的函数,或者等待子进程的wait系列函数。同时,像poll、select、epoll这样的I/O多路复用函数是否也会被实时信号中断? 此外,用户可能想知道如何处理被中断的系统调用,比如检查errno是否为EINTR,然后决定是否重新调用。这在实际编程中是一个常见的问题,需要正确处理以避免意外错误。 可能还需要提到,某些系统调用即使被信号打断,也会自动恢复,特别是当使用SA_RESTART标志时。但对于实时信号,是否默认行为不同?比如,标准信号可能默认有SA_RESTART,而实时信号可能没有?或者是否相反? 总结来说,回答需要包括以下几点: 1. 实时信号与标准信号的不同之处,特别是排队机制。 2. 系统调用被信号中断的一般情况,即返回EINTR。 3. 哪些系统调用会被实时信号打断,列举常见的例子。 4. SA_RESTART标志的作用及其对实时信号的影响。 5. 如何处理被中断的系统调用,比如重启它们。 需要确认这些信息的准确性,可能需要查阅相关文档或资料。比如,APUE(Advanced Programming in the UNIX Environment)中的相关章节,或者Linux的man page,特别是signal(7)、sigaction(2)等部分。</think>Linux实时信号(Real-Time Signals,范围 `SIGRTMIN` 到 `SIGRTMAX`)在打断系统调用时的行为与标准信号类似,但因其支持排队机制(多个信号按顺序传递,不会丢失),可能在某些场景下对系统调用的影响更显著。以下是关键点说明: --- ### **1. 实时信号与标准信号的差异** - **标准信号**(如 `SIGINT`, `SIGTERM`): - 不支持排队,多次发送可能被合并为一次。 - 默认行为可能自动重启被中断的系统调用(依赖 `SA_RESTART` 标志)。 - **实时信号**(如 `SIGRTMIN+1`): - 支持排队,确保每个信号被独立处理。 - 默认不自动重启被中断的系统调用(除非显式设置 `SA_RESTART`)。 --- ### **2. 系统调用被信号打断的通用规则** 当进程执行 **阻塞式系统调用**(如 `read`, `write`, `accept`)时,若被信号中断: - 系统调用立即返回,并设置 `errno` 为 `EINTR`。 - 进程需检查 `EINTR` 并决定是否重试。 **例外**:某些系统调用(如 `sleep`)即使被中断,也可能返回剩余未休眠的时间,而非 `EINTR`。 --- ### **3. 实时信号可能打断的系统调用(常见示例)** 以下类型的系统调用可能被实时信号中断(返回 `EINTR`): - **I/O 操作**:`read`, `write`, `recv`, `send`(在阻塞模式下)。 - **进程控制**:`wait`, `waitpid`, `waitid`。 - **网络操作**:`accept`, `connect`, `sendto`, `recvfrom`。 - **同步原语**:`sem_wait`, `pthread_mutex_lock`(某些实现)。 - **定时与休眠**:`nanosleep`, `poll`, `select`, `epoll_wait`。 --- ### **4. `SA_RESTART` 标志的影响** - **若设置 `SA_RESTART`**(通过 `sigaction`): 被中断的系统调用会自动重启,无需手动处理 `EINTR`。 - **实时信号的默认行为**: 默认不启用 `SA_RESTART`,因此更可能返回 `EINTR`。需显式设置该标志以改变行为。 --- ### **5. 处理被中断的系统调用** 代码示例(以 `read` 为例): ```c while (1) { ssize_t n = read(fd, buf, size); if (n >= 0) { // 成功读取数据 break; } else if (errno == EINTR) { // 被信号中断,重新调用 read continue; } else { // 处理其他错误 perror("read"); break; } } ``` --- ### **6. 不可中断的系统调用** 某些系统调用处于 **不可中断状态**(如磁盘 I/O、部分内核操作),即使收到信号不会返回 `EINTR`。例如: - `write` 在等待磁盘写入时可能无法被中断。 - `uninterruptible sleep` 状态(如某些内核操作)不会被任何信号打断。 --- ### **总结** - **实时信号**更倾向于打断系统调用并返回 `EINTR`(默认无 `SA_RESTART`)。 - **关键处理原则**:对可能返回 `EINTR` 的系统调用,需在代码中检查并决定是否重试。 - **扩展阅读**:可通过 `man 7 signal` 和 `man 2 sigaction` 查阅更详细的 Linux 文档。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GuiStar_李什么恩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值