2014-11-13 2 views
1

Я работаю с переносом одной из встроенных библиотек микроконтроллеров в linux и написанием обертки python вокруг нее.SIGALRM, интервальные таймеры и проблемы со сном()

Один из моих модулей низкого уровня зависит от обратного вызова, который вызывается каждые 10 мс. Этот обратный вызов подсчитывает таймеры программного обеспечения, каждый из которых имеет свои собственные обратные вызовы. Эти таймеры используются по всей нашей библиотеке, и, хотя они не должны быть на 100% точными, ожидается, что они продолжат тикать и, надеюсь, достигнут точности +/- 10%. Этот модуль имитирует стандартное прерывание таймера в микроконтроллере.

У меня есть существующая реализация с использованием обработчиков setitimer и SIGALRM, которые прекрасно работают. Вот некоторые из моего кода:

void halCounterInit(void) 
{ 
    struct sigaction alrm_action; 
    alrm_action.sa_handler = timerInterrupt; 
    alrm_action.sa_flags = SA_RESTART; 
    sigaction(SIGALRM, &alrm_action, NULL); 
} 

void halCounterEnable(void) 
{ 
    mytime.it_interval.tv_sec = 0; 
    mytime.it_interval.tv_usec = 10000; //10ms 
    mytime.it_value.tv_sec = 0; 
    mytime.it_value.tv_usec = 10000; //10ms 
    setitimer(ITIMER_REAL, &mytime, NULL); 
} 

void halCounterDisable(void) 
{ 
    mytime.it_interval.tv_sec = 0; 
    mytime.it_interval.tv_usec = 0; 
    mytime.it_value.tv_sec = 0; 
    mytime.it_value.tv_usec = 0; 
    setitimer(ITIMER_REAL, &mytime, NULL); 
} 

void halCounterRegisterInterruptHandler(InterruptHandlerCallback cb, void *params) 
{ 
    myInterruptHandler = cb; 
    myParams = params; 
} 

void timerInterrupt(int signum) 
{ 
    if (myInterruptHandler != NULL) 
    { 
     myInterruptHandler(myParams); 
    } 
} 

Во время тестирования некоторые из этого кода в сочетании с другим кодом, я заметил, что звонки спать() выгонять рано, когда генерируется сигнал SIGALRM. Я взаимодействую с gevent (на основе libevent) в python, используя ctypes, чтобы создать новый слой над моей библиотекой C. Я обеспокоен тем, что эта проблема SIGALRM затруднит поиск ошибок в python.

Я попытался переписать мой простой счетчик, используя вызов pthread и blocking select(), но кажется, что вызов select() выходит из системы раньше, а мои таймеры становятся ужасно неточными.

  1. Есть ли способ заставить setitimer генерировать другой сигнал, который я могу уловить, вместо сигальма?
  2. Есть ли другой способ сделать это без использования сигналов? Я пробовал наноселекцию, выбор и регулярный сон в другом потоке, но это работает не очень хорошо.

Любая помощь приветствуется. В идеале это будет работать как для OSX, так и для Linux, но Linux является единственным жестким требованием.

Edit:

Мой отдельный код нить с помощью выбора() или nanosleep(). Оба являются неточными. Выбирается реализация select. MyTime правильно настроен в функции init. Это функция резьбы:

while (TRUE) 
{ 
    pthread_mutex_lock(&Lock); 
    //we are paused 
    while(!Running) 
    { 
     //we are waiting for a signal to tell us when to start again 
     pthread_cond_wait(&Cond, &Lock); 
    } 
    pthread_mutex_unlock(&Lock); 
    RemTime.tv_nsec = 0; 
    do 
    { 
     nanosleep(&MyTime, &RemTime); 
    } 
    while(RemTime.tv_nsec != 0); 
    //select(0, NULL, NULL, NULL, &MyTime); 
    if (myInterruptHandler != NULL) 
    { 
     myInterruptHandler(myParams); 
    } 
} 

Спасибо.

+0

Re # 1, да, но это сложно сделать (tm), как предлагает @abligh ниже, и особенно сложно в коде _library_ (который не может утверждать, что сигналы со свободным перерывом). Re # 2, да, но мы не можем помочь вам отлаживать код, который мы не видим. (Кстати, SIGALRM или SIGVTALRM? Ваше повествование и ваш код говорят разные вещи.) – pilcrow

+0

@pilcrow, SIGALRM, я просто понял, что скопировал код отладки, с которым я играл. Будет редактировать. –

ответ

2

Всякий раз, когда возникает сигнал без маскировки, он прерывает любой выполняемый системный вызов. Существует опция SA_RESTART (см. man -s7 signal), но это только перезапустит некоторые системные вызовы (а не, например, sleep). Независимо от того, какой сигнал вы используете, если системный вызов прерван, тогда при условии SA_RESTART proviso выше поведение будет изменено. Поэтому вам нужно будет пройти через все ваше приложение, гарантируя правильное обращение кода возврата EINTR. Конечно, это сложно, если вы используете sleep и ожидаете надежности.

Я предлагаю вам лучше всего отходить от использования сигналов, особенно если вы используете нитки; они не играют хорошо. Вы говорите, что хотите, чтобы он был переносимым: еще одна причина избежать сигналов, подобных чуме.

Что бы я сделал, это настроить другой поток для выполнения ваших таймеров и защитить значения таймера с помощью соответствующего мьютекса. Этот поток может спать до тех пор, пока ему не понадобится выполнить следующий, либо используя nanosleep, либо pthread_cond_timedwait (если вы хотите, чтобы из него вышло условие).В любом случае вам нужно будет обернуть вызов, чтобы избежать сигналов, вызывающих ложный выход. Прочитайте текущее значение часов перед вызовом, заверните вызов в цикле do/while() и проверьте, что время действительно превысило время, которое вы хотели в условиях теста. Обычно для этого используется gettimeofday, но это не всегда монотонно, поэтому вам может понадобиться clock_gettime с CLOCK_MONOTONIC в зависимости от того, как вы хотите реагировать на изменение системного времени.

+0

Спасибо за ваш ответ. Я попытался использовать отдельный поток наряду с select() и nanosleep, но не мог заставить правильное поведение работать. Полагаю, я могу попытаться адекватно обернуть код для учета поступающих сигналов и прервать его. Я получаю вашу точку в EINTR, но это не обязательно мой код, о котором я беспокоюсь, но о любой из различных библиотек python, с которыми я мог бы взаимодействовать. –

Смежные вопросы