Linux 上以相反顺序接收的实时信号

Posted

技术标签:

【中文标题】Linux 上以相反顺序接收的实时信号【英文标题】:Real-time signals received in reversed order on Linux 【发布时间】:2014-05-03 11:36:22 【问题描述】:

这个程序向自己发送实时信号并处理它们。一旦它们被处理,它就会按照接收到的顺序输出它接收到的信号。

$ cat realtime.c
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int received_signals[10];
int received_signals_value[10];
int received_signals_count = 0;

void real_time_handler(int sig_number, siginfo_t * info,
                       void * arg __attribute__ ((unused)))

    received_signals[received_signals_count] = sig_number - SIGRTMIN;
    received_signals_value[received_signals_count] = info->si_value.sival_int;
    ++received_signals_count;


void send_real_time_signal(int sig_number, int value)

    union sigval sig_value;

    printf("Sending signal SIRTMIN+%d, value %d\n", sig_number, value);
    sig_value.sival_int = value;
    if (sigqueue(getpid(), sig_number + SIGRTMIN, sig_value) < 0) 
        perror("sigqueue");
        exit(EXIT_FAILURE);
    


int main()

    struct sigaction action;
    sigset_t set;
    int i;

    // Handler setup
    action.sa_sigaction = real_time_handler;
    sigemptyset(&action.sa_mask);
    action.sa_flags = SA_SIGINFO;
    if ((sigaction(SIGRTMIN + 1, & action, NULL) < 0)
     || (sigaction(SIGRTMIN + 2, & action, NULL) < 0)
     || (sigaction(SIGRTMIN + 3, & action, NULL) < 0)) 
        perror("sigaction");
        exit(EXIT_FAILURE);
    

    // Block all signals
    sigfillset(&set);
    sigprocmask(SIG_BLOCK, &set, NULL);

    send_real_time_signal(1, 0);
    send_real_time_signal(2, 1);
    send_real_time_signal(3, 2);
    send_real_time_signal(1, 3);
    send_real_time_signal(2, 4);
    send_real_time_signal(3, 5);
    send_real_time_signal(3, 6);
    send_real_time_signal(2, 7);
    send_real_time_signal(1, 8);
    send_real_time_signal(3, 9);

    // Unblock all signals
    sigfillset(&set);
    sigprocmask(SIG_UNBLOCK, &set, NULL);

    // To make sure we're handling all signals before resuming
    sleep(1);

    // Display results
    for (i = 0; i < received_signals_count; ++i) 
        printf("Received signal SIGRTMIN+%d, value %d\n",
               received_signals[i], received_signals_value[i]);
    

    return EXIT_SUCCESS;

我一直得到以下输出:

$ gcc -Wall -Wextra -O2 -o realtime realtime.c
$ ./realtime
Sending signal SIRTMIN+1, value 0
Sending signal SIRTMIN+2, value 1
Sending signal SIRTMIN+3, value 2
Sending signal SIRTMIN+1, value 3
Sending signal SIRTMIN+2, value 4
Sending signal SIRTMIN+3, value 5
Sending signal SIRTMIN+3, value 6
Sending signal SIRTMIN+2, value 7
Sending signal SIRTMIN+1, value 8
Sending signal SIRTMIN+3, value 9
Received signal SIGRTMIN+3, value 2
Received signal SIGRTMIN+3, value 5
Received signal SIGRTMIN+3, value 6
Received signal SIGRTMIN+3, value 9
Received signal SIGRTMIN+2, value 1
Received signal SIGRTMIN+2, value 4
Received signal SIGRTMIN+2, value 7
Received signal SIGRTMIN+1, value 0
Received signal SIGRTMIN+1, value 3
Received signal SIGRTMIN+1, value 8

这与 signal(7) (http://man7.org/linux/man-pages/man7/signal.7.html) 中的内容相矛盾:

实时信号以有保证的顺序传递。多个相同类型的实时信号按照它们发送的顺序传递。如果不同的实时信号被发送到一个进程,它们会从编号最小的信号开始传递。 (即,编号低的信号具有最高优先级。)相比之下,如果多个标准信号正在等待一个进程,则它们的传递顺序是未指定的。

据我所知,我应该得到:

$ ./realtime
Sending signal SIRTMIN+1, value 0
Sending signal SIRTMIN+2, value 1
Sending signal SIRTMIN+3, value 2
Sending signal SIRTMIN+1, value 3
Sending signal SIRTMIN+2, value 4
Sending signal SIRTMIN+3, value 5
Sending signal SIRTMIN+3, value 6
Sending signal SIRTMIN+2, value 7
Sending signal SIRTMIN+1, value 8
Sending signal SIRTMIN+3, value 9
Received signal SIGRTMIN+1, value 0
Received signal SIGRTMIN+1, value 3
Received signal SIGRTMIN+1, value 8
Received signal SIGRTMIN+2, value 1
Received signal SIGRTMIN+2, value 4
Received signal SIGRTMIN+2, value 7
Received signal SIGRTMIN+3, value 2
Received signal SIGRTMIN+3, value 5
Received signal SIGRTMIN+3, value 6
Received signal SIGRTMIN+3, value 9

我快速浏览了 Linux 内核源代码,但(还)找不到处理实时信号的位置。

我做错了什么/遗漏了什么还是手册页不准确?

【问题讨论】:

您确定您没有意外依赖未定义的行为吗?在信号处理程序中添加互斥锁/锁会发生什么? 【参考方案1】:

您的问题是您允许其他信号中断正在运行的信号处理程序:

sigemptyset(&action.sa_mask);

sa_mask 字段指定不允许中断此处理程序执行的信号集。

这意味着你得到了SIGRTMIN + 1,在你可以对它做任何事情之前你得到另一个较低优先级的信号,然后你开始处理它。然后该信号又被另一个信号中断,依此类推。

要解决此问题,请禁止任何其他信号中断您的处理程序:

sigfillset(&action.sa_mask);

【讨论】:

【参考方案2】:

我认为你的问题是这一行sigemptyset(&amp;action.sa_mask);

信号以正确的顺序 RT1 -> RT3 传递,但优先级较高的信号会中断较低优先级的信号,因为它们稍后发送。

在每个优先级中,信号按发送顺序传递,并且默认情况下,正在传递的信号在传递过程中被阻止,它们按发送顺序正确处理,这会导致您的排序混乱。

正如 cnicutar 所说,切换到使用sigfillset,一切都会好起来的。

【讨论】:

默认情况下,正在传递的信号在传递过程中被阻止,它们按照发送的顺序正确处理:谢谢,这是第一个答案中缺少的部分。【参考方案3】:

正如人们在上面提到的那样,问题出在sigemptyset(&amp;action.sa_mask); 行。 所以另一种解决方案是只阻止发送信号:

::sigaddset(&action.sa_mask, SIGRTMIN + 1);
::sigaddset(&action.sa_mask, SIGRTMIN + 2);
::sigaddset(&action.sa_mask, SIGRTMIN + 3);

P.S : 在调用处理程序时自行阻塞信号被认为是一种很好的做法。

【讨论】:

以上是关于Linux 上以相反顺序接收的实时信号的主要内容,如果未能解决你的问题,请参考以下文章

Linux流量控制(TC)之表面

在 Linux 中控制信号量队列中的出队顺序

实时信号

有没有与信号量相反的东西

Linux signal 那些事儿信号的deliver顺序

在 PostgreSQL 上以正确的顺序对数组元素进行分组