【问题标题】:Signal handling slower when sleeping than spinning?睡眠时信号处理比旋转慢?
【发布时间】:2020-04-09 20:23:51
【问题描述】:

我试图理解为什么在主进程旋转时(while(1))比睡眠时处理信号的速度更快。

我使用以下代码创建一个 500us 的一次性计时器 (基于How to implement highly accurate timers in Linux Userspace?):

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

#define NSEC_PER_SEC 1000000000L

#define timerdiff(a,b) (((a)->tv_sec - (b)->tv_sec) * NSEC_PER_SEC + \
(((a)->tv_nsec - (b)->tv_nsec)))

struct timespec prev;

void handler( int signo )
{
    struct timespec now;
    unsigned long diff;

    clock_gettime(CLOCK_MONOTONIC, &now);
    diff = timerdiff(&now, &prev);

    printf("%lu\n", diff);

    exit(0);
}

int main(int argc, char *argv[])
{
    int i = 0;
    timer_t t_id;

    struct itimerspec tim_spec = {.it_interval= {.tv_sec=0,.tv_nsec=0},
                    .it_value = {.tv_sec=0,.tv_nsec=500000}};

    struct sigaction act;
    sigset_t set;

    sigemptyset( &set );
    sigaddset( &set, SIGALRM );

    act.sa_flags = 0;
    act.sa_mask = set;
    act.sa_handler = &handler;

    sigaction( SIGALRM, &act, NULL );

    if (timer_create(CLOCK_MONOTONIC, NULL, &t_id))
        perror("timer_create");

    clock_gettime(CLOCK_MONOTONIC, &prev);

    if (timer_settime(t_id, 0, &tim_spec, NULL))
        perror("timer_settime");

#ifdef SLEEP
    while(1)
        sleep(1);
#else
    while(1);
#endif

    return 0;
}

如果代码在定义了 SLEEP 的情况下执行,我会执行 10 次:

596940
549098
535758
606020
556990
528634
592051
545047
531079
541067
552520

如果未定义 SLEEP,代码将旋转,我会得到这些时间:

512641
510337
509778
510406
510057
507193
511245
511245
511384
509638
510127

这样更好。

谁能解释一下?脱离睡眠比中断旋转循环慢?

它在 Intel 平台(8 核)上的 Linux 4.9 PREEMPT_RT 修补内核上尝试了此代码,系统处于空闲状态。

谢谢!

奥雷利安

【问题讨论】:

  • 请注意,您不能从信号处理程序中安全地调用printf()exit()。每7.1.4 Use of library functions, p4:“标准库中的函数不能保证是可重入的,并且可能会修改具有静态或线程存储持续时间的对象。” Footnote 188 补充道:“因此,信号处理程序通常不能调用标准库函数。” POSIX 允许调用异步信号安全函数,但 printf()exit() 都不是异步信号安全函数。
  • (续)见2.4 Signal Concepts
  • @AndrewHenle 但是如果你可以排除,另一个使用 stdio 的函数被信号中断,它应该是相对安全的(至少对于调试/测试来说足够安全)。
  • @AndrewHenle 谢谢,这只是一个快速的'n'dirty示例代码:)

标签: c linux signals real-time scheduling


【解决方案1】:

当自旋时,内核不需要准备进程运行,因为当信号到达时进程已经在运行。它只需要更改指令指针即可进入信号处理程序。

如果进程处于休眠状态,则必须将其放入任务队列,必须恢复上下文(寄存器、内存映射)以及许多其他准备步骤,直到进程真正运行。

这有一点不同,但如果你看一下总数,总共只有大约 10 - 100 µs。

【讨论】:

  • 有可能该进程正在运行但当前尚未执行,对吧?在那种情况下,无论如何都不需要上下文切换吗?
  • @SaucyGoat 好吧,如果系统大部分时间处于空闲状态并且您有多个内核,那么当您使用旋转方法时,几乎可以肯定该进程正在运行。如果不是,则需要上下文切换,是的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-29
  • 1970-01-01
  • 2011-05-18
  • 1970-01-01
  • 2020-06-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多