0

I'm developing a program that runs on a Raspberry-Pi (linux), that gets data from GPS modules and processes it.

Right now, the program is centered around a while(1) loop that takes the GPS data and does stuff with it, (data is streamed at the frequency of the GPS module).

However, I know while(1) is not a very power efficient solution, (This RPi is going to sit on a drone later).

I'd like to set up a timer, and only when the timer ends, will the program take GPS data. Ideally, the program would be completely stopped until the timer ends, so that the CPU is not wasting time/energy on it.

How can I do the above mentioned, if SIGSTOP signal is not allowed in sigaction() calls ?

void timer_handler(int signum){
    /* what can I do to make a program stop and coninue,
       so that no CPU cycles are devoted to this program until
       the timer is elapsed ? (SIGSTOP is not allowed) */
}

int main(int argc, char** argv){

    struct sigaction sigAct;
    struct itimerval timer;
    sa.sa_handler = &timer_handler;

    // SIGALRM is only a place holder here.. 
    sigaction(SIGALRM, &sigAct, NULL);

    /* Here would be some timer.it_value, timer.it_interval stuff,
    setting it to some arbitrary time */

    setitimer(ITIMER_REAL, &timer, NULL);

    /* Main program loop here */
    while(1){
        // process GPS data.
        // instead of while(1), I'd like this loop to run only when
        // the timer ends.
    }

}
  • can't you just call `sleep`? – kaylum Apr 30 '16 at 09:35
  • I need a larger resolution than just seconds.. I'm aware of `nanosleep()`, but I'm not sure if by calling this function the system abandons execution of this program completely.. No precise info on that as far as I could find.. – Daniel_Bogorad Apr 30 '16 at 09:51
  • 1
    yes it does suspend the process completly. To check for yourself just call nanosleep and check the state of the process. – kaylum Apr 30 '16 at 09:59
  • OT: Be aware that `sleep()` as well as `nanosleep()` might return ***before*** the expected time had passed. You saying, as talking about planes, drones ... wtf flying above my head. – alk Apr 30 '16 at 10:32
  • @Daniel_Bogorad What do you mean by "abandons execution of this program completely"? – David Schwartz May 01 '16 at 00:50

1 Answers1

2

In your loop, just call pause(). It'll block and only return when a signal is received, so then you can run your code, loop again, and repeat. You need a signal handler to stop SIGALRM from terminating your program, but it doesn't have to do anything, you can just leave the function body empty.

For instance:

#define _POSIX_C_SOURCE 200809L

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

void timer_handler(int signum)
{
   (void) signum;       /*  Avoids warning for unused argument  */
}

int main(void)
{
    struct sigaction sa;
    sa.sa_handler = timer_handler;
    sa.sa_mask = 0;
    sa.sa_flags = 0;
    sigaction(SIGALRM, &sa, NULL);

    struct itimerval timer;
    timer.it_value.tv_sec = 1;
    timer.it_value.tv_usec = 0;
    timer.it_interval.tv_sec = 1;
    timer.it_interval.tv_usec = 0;
    setitimer(ITIMER_REAL, &timer, NULL);

    while ( 1 ) {
        pause();
        printf("Timer expired - get GPS data.\n");
    }

    return 0;
}

yields output:

paul@horus:~/src/sandbox$ ./alarm
Timer expired - get GPS data.
Timer expired - get GPS data.
Timer expired - get GPS data.
Timer expired - get GPS data.
Timer expired - get GPS data.
Timer expired - get GPS data.
Timer expired - get GPS data.
Timer expired - get GPS data.
^C
paul@horus:~/src/sandbox$

It's a fairly crude solution. If there's any possibility that the code may sometimes take longer to run than the timer interval, then things become unreliable and you may sometimes skip a signal. You may or may not care about this. For a more sophisticated approach, you can block receipt of SIGALRM and then call sigsuspend() with a signal mask that unblocks it, safe in the knowledge that the unblocking and the waiting for the signal will be an atomic operation. Here's an example of that approach:

#define _POSIX_C_SOURCE 200809L

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

void timer_handler(int signum)
{
    static const char msg[] = "Handler was called.\n";
    write(STDIN_FILENO, msg, sizeof(msg) - 1);
    (void) signum;
}

int main(void)
{
    struct sigaction sa;
    sa.sa_handler = timer_handler;
    sa.sa_mask = 0;
    sa.sa_flags = 0;
    sigaction(SIGALRM, &sa, NULL);

    struct itimerval timer;
    timer.it_value.tv_sec = 1;
    timer.it_value.tv_usec = 0;
    timer.it_interval.tv_sec = 1;
    timer.it_interval.tv_usec = 0;
    setitimer(ITIMER_REAL, &timer, NULL);

    sigset_t old_mask, block_mask;
    sigemptyset(&block_mask);
    sigaddset(&block_mask, SIGALRM);
    sigprocmask(SIG_BLOCK, &block_mask, &old_mask);

    sleep(3);   /*  To demonstrate signal handler won't be
                    called until sigsuspend() is called, timer
                    is firing every second while we're sleeping  */

    while ( 1 ) {
        sigsuspend(&old_mask);
        printf("Timer expired - get GPS data.\n");
    }

    return 0;
}

with output:

paul@horus:~/src/sandbox$ ./alarm2
Handler was called.
Timer expired - get GPS data.
Handler was called.
Timer expired - get GPS data.
Handler was called.
Timer expired - get GPS data.
Handler was called.
Timer expired - get GPS data.
Handler was called.
Timer expired - get GPS data.
Handler was called.
Timer expired - get GPS data.
^C
paul@horus:~/src/sandbox$

Error checking has been omitted for brevity in the above examples, but your code should of course include it for every system call.

Crowman
  • 25,242
  • 5
  • 48
  • 56