Как приостановить основной цикл у демона, управляемого сигналом? - PullRequest
0 голосов
/ 10 февраля 2019

Я работаю над эмуляцией электронной телефонной линии для Raspberry Pi.Для этого мне нужна функция, которая вызывается каждые 20 мс (= 50 Гц, половина частоты напряжения звонка), проверяет электрический ток и обновляет рабочий цикл HW PWM.Чтобы туда попасть, я настроил обработчик сигнала на соответствующий таймер (как я бы повесил его в планировщик в чистой встроенной среде) и позволил ему выполнить свою работу.Обратите внимание, что приведенный ниже код сокращен только для того, чтобы решить проблему.

Это уже работает довольно хорошо.Я был поражен тем, насколько точно поддерживается время (джиттер меньше 10 мкс, измеренный осциллографом).

Однако до сих пор есть один недостаток: при такой работе основной цикл на самом деле нетребуется.Я не могу вернуться от этого, так как это убило бы процесс.Когда я просто позволяю этому быть пустым циклом, все работает нормально.Но я думаю о излишне потребляемой загрузке процессора.

Я пытался sleep , wait (оба из которыхя больше не буду использовать, я знаю), и sigsuspend .Но все это позволяет обработчику аварийных сигналов больше не вызываться.

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <signal.h>
#include <syslog.h>
#include <sys/time.h>
#include <bcm2835.h>

#define PIN_PWM RPI_V2_GPIO_P1_35

bool b_Alive;

/** Signal-Handlers: ******************************************************************/

void SIG_Alarm (int signum) {
    /** Just toggle a pin for now:                                                    */
    static bool bOn;
    if (bOn) {
        bOn = 0;
        bcm2835_gpio_write(PIN_PWM, LOW);
    }else{
        bOn = 1;
        bcm2835_gpio_write(PIN_PWM, HIGH);
    }
}

void SIG_Quit (int signum) {
    /** Shutdown the bcm-library:                                                     */
    bcm2835_close();
    /** Close the sys-log handler:                                                    */
    syslog(LOG_NOTICE + LOG_DAEMON, "Received SigInt and closed.");
    closelog();
    b_Alive = false;    
}

/** Main-Function: ********************************************************************/


int main(void) {
    /** Variables:                                                                    */
    struct sigaction sa;
    struct itimerval timer;
    /** Open syslog instead: */
    openlog( "PhoneLined", LOG_PID | LOG_CONS | LOG_NDELAY, LOG_LOCAL0 );
    /** Setup pins:                                                                   */
    if (!bcm2835_init()) return 1;
    bcm2835_gpio_fsel(PIN_PWM, BCM2835_GPIO_FSEL_OUTP);
    /** Setup signal handler for kill:                                                */
    memset(&sa, 0, sizeof (sa));
    sigemptyset(&sa.sa_mask);
    sa.sa_handler = &SIG_Quit;
    sigaction(SIGINT , &sa, NULL);
    sigaction(SIGQUIT, &sa, NULL);
    sigaction(SIGKILL, &sa, NULL);
    sigaction(SIGTERM, &sa, NULL);
    /** Setup signal handler for timer:                                               */
    memset(&sa, 0, sizeof (sa));
    sigemptyset(&sa.sa_mask);
    sa.sa_handler = &SIG_Alarm;
    sigaction(SIGVTALRM, &sa, NULL);
    /** Configure the timer of a start- and cycle-time of 20ms (=50Hz):               */
    timer.it_value.tv_sec = 0;
    timer.it_value.tv_usec = 20000;
    timer.it_interval.tv_sec = 0;
    timer.it_interval.tv_usec = 20000;
    setitimer(ITIMER_VIRTUAL, &timer, NULL);
    /** Prepare main-loop:                                                            */
    b_Alive = true;
    syslog(LOG_NOTICE + LOG_DAEMON, "Sucessfully initialized.");
    /** ... and do nothing, while the timer works:                                    */
    while (b_Alive) {
        //pause(), suspend, wait or anything?
    }
    exit(EXIT_SUCCESS);
}

Есть ли какие-либо подсказки, как поступить, когда в этом цикле фактически ничего не нужно делать?

1 Ответ

0 голосов
/ 10 февраля 2019

как минимум добавьте volatile к определению вашего флага, иначе оптимизатор удалит цикл (для -O2 или выше)

(проверьте с помощью cc -Wall -O4 -S signal.c)


#include <stdbool.h>
// volatile sig_atomic_t
volatile bool
        b_Alive=false;

main ():

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

// Prepare main-loop:            

b_Alive = true;
    syslog(LOG_NOTICE + LOG_DAEMON, "Sucessfully initialized.");
    /** ... and do nothing, while the timer works:                                    */
    while (b_Alive) {
        pause(); //, suspend, wait or anything?
    }
    exit(EXIT_SUCCESS);

И, типы таймеров (из тонкой инструкции):


   ITIMER_REAL    This timer counts down in real (i.e., wall clock) time.  At each expiration, a SIGALRM signal is generated.

   ITIMER_VIRTUAL This timer counts down against the user-mode CPU time consumed by the process.  (The measurement includes CPU time consumed by all threads in the process.)  At each
                  expiration, a SIGVTALRM signal is generated.

И, поскольку pause() не потребляет тактов процессора, таймер никогда не истечет при использовании ITIMER_VIRTUAL.(также: подается другой сигнал)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...