Эффективный способ ожидания прерывания в 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
во главе с вашим обработчиком прерываний, когда закончите с задачей?
Это ссылка.