问题
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 signal
led 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