Search code examples
clinuxsignalsreal-timescheduling

Signal handling slower when sleeping than spinning?


I'm trying to understand why a signal is handled faster when the main process is spinning (while(1)) than sleeping.

I use the following code which creates a one-shot timer of 500us (based on 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;
}

If the code is executed with SLEEP defined, 10 executions give me:

596940
549098
535758
606020
556990
528634
592051
545047
531079
541067
552520

If SLEEP in not defined, the code will spin and I get those timings :

512641
510337
509778
510406
510057
507193
511245
511245
511384
509638
510127

That's really better.

Can someone explain me? Getting out of sleep is slower than interrupting a spinning loop?

It tried this code on a Linux 4.9 PREEMPT_RT patched kernel on an Intel platform (8 cores), system is idle.

Thanks!

Aurélien


Solution

  • When spinning, the kernel does not need to prepare the process to run, since the process is already running when the signal arrives. It just has to alter the instruction pointer to enter the signal handler.

    If the process sleeps, it has to be put on the task queue, the context has to be restored (registers, memory mappings) and a whole lot of other preparatory steps until the process really runs.

    This makes a small difference, but if you look at the total numbers, it is only about 10 - 100 µs in total.