Эффективный способ ожидания прерывания в C

Я использую WiringPi на Raspberry Pi. С его помощью я назначаю функцию прерывания, которая вызывается позже. Я не уверен, что делать при ожидании вызова прерывания.

Примеры использования (спинлок?) for (;;) например

int main()
{
    // register interrupt
    wiringPiISR( 18, INT_EDGE_BOTH, &myInterrupt );

    for (;;) {
        // really?
    }
    return 0;
}

И я заметил, что sleep тоже работает Прерывание вызывается независимо от сна

int main() 
{
    // register interrupt
    wiringPiISR( 18, INT_EDGE_BOTH, &myInterrupt );

    for (;;) {
        sleep(1000000);
    }
    return 0;
}

Как лучше всего поддерживать работу программы, используя минимальные ресурсы (скажем, для фонового демона)?

Исходя из других языков, я бы подумал for(;;) съел бы ресурсы. Я хотел бы знать, что делать или указатели на то, что делать (темы и т. Д.).

6 ответов

Решение

Я недавно должен был сделать именно это. Моя теория была KISS. sleep написано, чтобы использовать минимальные ресурсы по определению - и использование его означает, что мне не нужно заботиться о потоках.

Простая, удобочитаемая и не поддающаяся измерению производительность ударяет по оригинальной Raspberry Pi B:

int main() 
{
    // register interrupt
    wiringPiISR( 18, INT_EDGE_BOTH, &myInterrupt );

    for (;;) {
        sleep(UINT_MAX);
    }
    return 0;
}

Обратите внимание на использование UINT_MAX для минимизации числа вызовов цикла - это предполагает 32-битную задержку без знака, которую использует WiringPi.

Вы также можете использовать семафор. sem_post() должен быть безопасным для асинхронного сигнала:

#include <semaphore.h>

static sem_t staticSem;

void myInterupt( void )
{
    sem_post( &staticSem );
}

int main() 
{
    sem_init( &staticSem, 0, 0 );

    // register interrupt
    wiringPiISR( 18, INT_EDGE_BOTH, &myInterrupt );

    for (;;) {
        sem_wait( &staticSem );
        ...
    }
    return 0;
}

Проверка ошибок и обработка потенциальных ложных пробуждений от sem_wait() не включены

WiringPi устанавливает отдельный поток и вызывает функцию isr из этого потока.

Затем вы можете использовать переменную условия pthread, чтобы заблокировать другой поток, в этом случае main(), и заставить функцию isr вызывать ее при возникновении прерывания.

#include <pthread.h>

pthread_cond_t isr_cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t isr_mtx = PTHREAD_MUTEX_INITIALIZER;
unsigned int isr_count = 0;

void myInterrupt(void)
{
    pthread_mutex_lock(&isr_mtx);
    isr_count++;
    pthread_cond_signal(&isr_cond);    
    pthread_mutex_unlock(&isr_mtx);

}

int main() 
{
    // register interrupt
    wiringPiISR( 18, INT_EDGE_BOTH, &myInterrupt );

    for (;;) {
       pthread_mutex_lock(&isr_mtx);
       while (isr_count == 0) {
          pthread_cond_wait(&isr_cond, &isr_mtx);
       }
       //add logic here to handle the ISR, isr_count
       //tells you how many ISRs occured.
       //heavy work should be moved outside the mutex.

      isr_count = 0;
      pthread_mutex_unlock(&isr_mtx);
    }

    return 0;
}

Вам нужно скомпилировать и связать ваш код с -pthread флаг.

Это зависит от того, требуется ли вам уведомление на уровне пользователя. Тогда есть несколько случаев:

1) "Мне вообще не нужно уведомление, так как я делаю все в состоянии прерывания". Хорошо, подождите в main(), используя длинный цикл Sleep(), или Sleep(INFINITE), если он доступен, или дождитесь какого-нибудь синхронизирующего объекта, который никогда не сигнализируется, или выполните цикл вокруг какой-либо команды 'set low power state' или 'HALT'. Это удаляет необходимость выполнения из состояния пользователя и просто оставляет ваш процессор в ожидании прерываний.

2) "Мне нужно уведомление в состоянии пользователя, но я не беспокоюсь о задержке". Хорошо, установите некоторое атомарное int или логическое значение из состояния прерывания и опрашивайте его в зависимости от того, какой main() или поток требуется, чтобы заметить, что могут произойти прерывания. Такой опрос может быть расточительным и реагировать медленно, но если вам не нужно заботиться о своем приложении, хорошо:)

3) "Мне нужно уведомление в состоянии пользователя так быстро, как это похвально". "Классический" способ реализации такой сигнализации состоит в том, чтобы поток обработчика ожидал семафор, сигнализировал семафор от обработчика прерываний и "инструктировал" ОС, что он должен выполнить перепланирование, как только обработчик прерываний закончится. так что ждущий поток готов / работает. Другие сигнальные механизмы, например. события /condvars, также могут быть безопасными для сигнализации из состояния прерывания, но вы должны проверить с документацией вашей ОС.

Что я не могу сделать? Вы не можете и / или не должны вызывать что-либо в состоянии прерывания, которое может попытаться заблокировать. Это катастрофично, и ваша ОС, вероятно, сильно рухнет:(

Я бы (и сделал, когда кодирование встроено), скорее использовать спинлок while(1) ;, Это просто и выражает намерение - никогда не проходите мимо этого пункта.

Мало того, что у сна есть срок годности, который не может быть проблемой сразу, но становится проблемой спустя годы. Он также должен выполнить некоторые вычисления, чтобы фактически посчитать время.

Вот сравнение между sleep(0xFFFFFFFF); на Intel Core i3-4330TE:

        .file   "test.c"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    $-1, %edi
        movl    $0, %eax
        call    sleep
        movl    $0, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
        .section        .note.GNU-stack,"",@progbits

и while(1); подход:

        .file   "test.c"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
.L2:
        jmp     .L2
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
        .section        .note.GNU-stack,"",@progbits

Так что меньше времени делается, если время не отслеживается. Я не уверен, что планировщик Linux может распознать этот шаблон, пока не появится ISR.


Сказав это, правильный способ "держать программу работающей, используя минимальные ресурсы" - это заглянуть в спящие режимы (стр. 2-14 или 36), которые предоставляет процессор.

Я не уверен насчет WiringPi в частности, но как насчет использования pthread_cond_t что ждет, чтобы быть signalво главе с вашим обработчиком прерываний, когда закончите с задачей?

Это ссылка.

Другие вопросы по тегам