Запуск потока из обработчика прерывания

Я пытаюсь запустить поток, как только происходит прерывание. Однако я понял, что не могу запустить поток из обработчика прерывания (или любой функции, которая прямо или косвенно вызывается обработчиком прерывания). Итак, я решил сделать так, чтобы обработчик установил флаг. Затем отдельный поток постоянно отслеживает этот флаг, и если он установлен, он, в свою очередь, создает (и запускает) поток. Вот псевдокод:

int interrupt_flag = 0;

interrupt_handler(void)
{
  interrupt_flag = 1
}

monitoring_thread(void) //this thread is started at the start of the program
{
  while(1)
  {
    if(interrupt_flag)
    {
      interrupt_flag = 0;
      //start the thread here
      sleep(/*some amount of time*/);
    }
  }
}

Я не очень доволен наличием выделенного цикла while, постоянно отслеживающего флаг. Проблема в том, что это значительно снижает скорость других потоков в моей программе. По этой причине я вызываю функцию сна, чтобы увеличить скорость других потоков в моей программе.

Вопрос: есть ли способ действительно запустить поток по прерыванию, не имея выделенного цикла while? Есть ли обходной путь для запуска потока из обработчика прерывания?

Если это имеет значение, я использую библиотеку POSIX.

Спасибо,

PS. Этот вопрос несколько связан с более ранним вопросом, размещенным здесь:

Обмен данными между главным потоком и подчиненным потоком в среде с прерываниями в C


person Arash Fotouhi    schedule 11.07.2013    source источник
comment
Если обработчик прерывания опережает текущую задачу, я ожидаю, что у него будет некоторый API для вызова, который запланирует выполнение какой-либо другой задачи (или обратного вызова) для выполнения реальной работы. Ничего подобного вы не можете сделать в своей системе? Затем эта задача может породить поток.   -  person jxh    schedule 11.07.2013
comment
Да, если это операционная система (что еще будет обрабатывать прерывание?), то у нее должен быть способ разбудить ожидающую задачу.   -  person Zan Lynx    schedule 11.07.2013
comment
Традиционный метод заключается в использовании семафора. Вызов любых методов, которые потенциально могут блокировать (например, мьютекс), в обработчике прерывания приведет к возможной катастрофе.   -  person Martin James    schedule 11.07.2013


Ответы (2)


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

sem_t interrupt_sem;

void interrupt_handler(void)
{
  sem_post(&interrupt_sem);
}

void monitoring_thread(void)
{
  while(1)
  {
    sem_wait(&interrupt_sem);
    //start the thread here
  }
}

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

Как указывалось в комментариях мной и другими, ваша операционная система должна предоставлять какой-то интерфейс для явного пробуждения ожидающей задачи. В приведенном выше коде мы предполагаем, что поток мониторинга всегда активен в фоновом режиме.

person jxh    schedule 11.07.2013
comment
Очень маловероятно, что вызов pthread_mutex_lock() в обработчике прерывания безопасен. Если прерывание срабатывает в потоке мониторинга в точке, где он удерживает блокировку, возникает взаимоблокировка. - person caf; 11.07.2013
comment
@caf: вы правы, если обработчик прерываний работает с повышенным приоритетом. Я предполагаю, что поток мониторинга находится в другом контексте, чем обработчик прерывания. - person jxh; 11.07.2013
comment
В каждой ОС, над которой я работал, не разрешается вызывать потенциально блокирующие вызовы из обработчика прерываний. Как говорит @caf, обработчик прерывания не имеет контекста потока и не должен блокироваться. Классическое решение опубликовано пользователем 2569075. - person Martin James; 11.07.2013
comment
@MartinJames: я думал, что многопоточное ядро ​​будет иметь специальный контекст для обработчиков прерываний. Я обновил ответ, чтобы отметить ограничения решения мьютекса и условной переменной, и предоставил более простое решение на основе семафора. - person jxh; 11.07.2013
comment
pthread_mutex_lock() не является безопасным для асинхронных сигналов, т. е. его нельзя вызвать из обработчика сигнала. Еще более удобным переносимым решением является трюк с self-pipe: обработчик сигнала записывает байт в конвейер. Другой поток ожидает чтения конца канала. - person Maxim Egorushkin; 11.07.2013
comment
@jxh ну, у него может быть отдельный стек прерываний, на который он переключается, но он не может легко иметь какой-либо контекст потока - он может и, вероятно, будет прерывать любой поток в любом процессе, принадлежащем любому пользователю, возможно, во время вызовов ядра ОС, которые уже находятся в процессе изменения состояния потока. Вот почему количество вызовов ОС, доступных аппаратным обработчикам прерываний самого низкого уровня, обычно ограничивается установкой флагов и размещением семафорных модулей. Обработчик прерывания не может блокироваться. Вызов чего-либо, что может заблокировать, приведет к сбою UB, возможно, к панике ядра, BSOD. - person Martin James; 11.07.2013
comment
@MartinJames: ответ скорректирован соответствующим образом. Спасибо. - person jxh; 11.07.2013

вы также можете использовать семафор POSIX

вы можете ждать семафора, начальное значение которого равно 0, потоком, который будет заблокирован ожиданием

и опубликуйте этот семафор в вашей функции дескриптора сигнала

затем поток выше будет разбужен и будет делать то, что вы хотите (создать поток)

person user2569075    schedule 11.07.2013
comment
Это традиционный метод обработки связи между обработчиком/драйвером прерывания и потоком. - person Martin James; 11.07.2013
comment
Обратите внимание, что если обработчик прерывания отправляет сообщение семафору, он должен предпринять какое-то специальное действие, чтобы быть эффективным, а не просто возвращать прерывание. Иногда необходимо установить «флаг» ОС, или, может быть, вызов функции «ExitInterruptSchedule» или фактический переход к точке входа в ОС. Это позволяет обработчику прерывания сделать поток готовым/запущенным «немедленно» после возврата из прерывания, возможно, вытесняя какой-либо другой поток, который выполнялся до прерывания. - person Martin James; 11.07.2013