在 C 中等待中断的有效方法

Efficient way to wait for an interrupt in C

我在 raspberry pi 上使用 WiringPi。我用它分配了一个稍后调用的中断函数。在等待中断被调用时,我不确定该怎么做。

示例使用(自旋锁?)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(;;) 会占用资源。我想知道该做什么或关于该做什么的指示(线程等)。

我不确定 WiringPi 特别是什么,但是使用 pthread_cond_t 在完成任务后等待 signal 由您的中断处理程序引导怎么样?

This为参考。

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 标志编译和 link 您的代码。

我最近不得不这样做。我的理论是 KISSsleep 被定义为使用最少的资源 - 使用它意味着我不必关心线程。

简单、可读且对原版没有可衡量的性能影响 Raspberry Pi B:

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

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

请注意使用 UINT_MAX 来最小化 for 循环调用的数量 - 这假设一个无符号的 32 位定时器延迟,这就是 WiringPi 使用的。

这取决于您是否需要在用户级别通知。有一些情况的话:

1) 'I need no notification at all since I do everythng in interrupt state'。好的,在 main() 中等待使用长 Sleep() 循环,或 Sleep(INFINITE)(如果可用),或等待某个从未发出信号的同步对象,或循环一些 'set low power state' 或 'HALT' 指令.这消除了从用户状态执行的需要,只让您的处理器等待中断。

2)“我需要用户状态下的通知,但我不关心延迟”。好的,从中断状态设置一些原子 int 或布尔值,并从需要注意中断可能发生的任何 main() 或线程轮询它。这样的轮询可能会很浪费并且响应缓慢,但是如果您不需要关心您的应用程序,那很好:)

3) 'I need notification in user state as fast as is praticable'。 'classic' 实现这种信号的方式是让处理程序线程在信号量上等待,从 interrupt-handler 和 'instruct' 发出信号量,OS 它必须执行重新调度interrupt-handler 一结束。所以使等待线程 ready/running。其他信号机制,例如。 events/condvars,从 interrupt-state 发信号也可能是安全的,但你应该检查你的 OS 文档。

我不能做什么?您不能,和/或不得在中断状态下调用任何可能试图阻止的东西。那是灾难性的,你的 OS 可能会摔倒:(

您也可以使用 semaphore. sem_post() must be async-signal-safe:

#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() 的潜在虚假唤醒。

我会(和 do,在编码嵌入时)宁愿使用自旋锁 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    [=10=], %eax
        call    sleep
        movl    [=10=], %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 到达之前识别此模式。


话虽如此,"keep the program running, using minimal resources" 的正确方法是查看处理器提供的 the sleep modes (page 2-14 or 36)