Efficient way to wait for an interrupt in C

生来就可爱ヽ(ⅴ<●) 提交于 2019-12-10 15:59:46

问题


I am using WiringPi on a raspberry pi. With it I assign an interrupt function which is called later. I'm not sure what to do when waiting for the interupt to be called.

Examples use a (spinlock?) for (;;) e.g.

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

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

And I noticed that sleep works too. The interrupt is called regardless of the sleep

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

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

What is the best practise to keep the program running, using minimal resources (say if this were for a background demon)?

Coming from other languages, I would have thought for(;;) would eat up resources. I would like to know what to do or pointers on what to do (threads etc).


回答1:


I recently had to do exactly this. My theory was KISS. sleep is written to use minimal resources by definition - and using it means I don't have to care about threads.

Simple, readable and no measurable performance hit on an original Raspberry Pi B:

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

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

Note the use of UINT_MAX to minimise number of for loop calls - this assumes an unsigned 32-bit timer delay, which is what WiringPi uses.




回答2:


WiringPi sets up a separate thread and calls your isr function from that thread.

You can then use a pthread condition variable to block another thread, main() in this case, and have the isr function wake it up when an interrupt occurs.

#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;
}

You need to compile and link your code with the -pthread flag.




回答3:


You can also use a 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;
}

Error checking and handling potential spurious wakeups from sem_wait() aren't included.




回答4:


It depends on whether you require notification at user level. There are some cases, then:

1) 'I need no notification at all since I do everythng in interrupt state'. Fine, wait in main() using a long Sleep() loop, or Sleep(INFINITE) if available, or wait on some synchro object that is never signaled, or loop round some 'set low power state' or 'HALT' instruction. This removes the neeed for execution from user state and just leaves your processor waiting for interrupts.

2) 'I need notification in user state, but I don't care about latency'. OK, set some atomic int or boolean from interrupt state, and poll it from whichever main() or thread required to notice that interrupts can happen. Such polling can be wasteful and responds slowly, but if you don't need to care in your app, fine:)

3) 'I need notification in user state as fast as is praticable'. The 'classic' manner to implement such signaling is to have a handler thread wait on a semaphore, signal the semaphore from the interrupt-handler and 'instruct' the OS that it must perform a reschedule as soon as the interrupt-handler is over. so making the waiting thread ready/running. Other signaling mechanisms, eg. events/condvars, may also be safe to signal from interrupt-state, but you should check with your OS documentation.

What can I not do? You cannot, and /or must not call anything in interrupt state that might try to block. That is disastrous and your OS will probably fall over hard:(




回答5:


I'm not sure about WiringPi in particular, but how about using a pthread_cond_t that waits to be signalled by your interrupt handler when finished with the task?

This is a reference.




回答6:


I would (and do, when coding embedded) rather use a spinlock while(1) ;. It is straightforward and expresses intent - never go past this point.

Not only does sleep have an expiration time, which might not be a problem right away, but become a problem after years. It also has to perform some computations to actually count the time.

Here is a comparison between sleep(0xFFFFFFFF); on a 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

and a while(1); approach:

        .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

So less work is done if time is not kept track of. I am not sure if the linux scheduler can recognise this pattern until an ISR arrives.


Having said this, the proper way to "keep the program running, using minimal resources" is to look into the sleep modes (page 2-14 or 36), that the processor provides.



来源:https://stackoverflow.com/questions/34945811/efficient-way-to-wait-for-an-interrupt-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!