Почему код / ​​поток ядра, выполняющийся в контексте прерывания, не может спать? - PullRequest
48 голосов
/ 28 июня 2009

Я читаю следующую статью Роберта Лава

http://www.linuxjournal.com/article/6916

что говорит

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

Я не понимаю. AFAIK, планировщик в ядре O (1), который реализуется через растровое изображение. Так что же мешает Scehduler перевести контекст прерывания в режим сна и запустить следующий запланированный процесс и передать ему управление?

Ответы [ 11 ]

41 голосов
/ 28 июня 2009

Так что же мешает сэчдулеру перевести контекст прерывания в спящий режим и запустить следующий запланированный процесс и передать ему управление?

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

Когда происходит прерывание, процессор сохраняет регистры в стек и переходит к началу процедуры обработки прерывания. Это означает, что когда обработчик прерываний работает, он выполняется в контексте процесса, который выполнялся, когда произошло прерывание. Прерывание выполняется в стеке этого процесса, и когда обработчик прерываний завершит работу, этот процесс возобновит выполнение.

Если вы попытаетесь заснуть или заблокировать внутри обработчика прерываний, вы получите не только остановку обработчика прерываний, но и процесс, который он прервал. Это может быть опасно, поскольку обработчик прерываний не может узнать, что делал прерванный процесс, или даже если этот процесс безопасен для приостановки этого процесса.

Простой сценарий, когда что-то может пойти не так, это тупик между обработчиком прерываний и процессом, который он прерывает.

  1. Процесс1 входит в режим ядра.
  2. Process1 приобретает LockA .
  3. Прерывание происходит.
  4. ISR начинает выполнение с использованием стека Process1 .
  5. ISR пытается получить LockA .
  6. ISR вызывает sleep, чтобы дождаться, пока LockA будет освобожден.

На данный момент у вас тупик. Process1 не может возобновить выполнение, пока ISR не завершит работу со своим стеком. Но ISR блокируется в ожидании Process1 для освобождения LockA .

29 голосов
/ 29 июня 2009

Я думаю, что это дизайнерская идея.

Конечно, вы можете спроектировать систему, в которой вы можете спать в режиме прерывания, но кроме как усложнить и усложнить систему (многие ситуации, которые вы должны принять во внимание), это ничего не поможет. Таким образом, с точки зрения дизайна, объявить обработчик прерываний, так как он не может спать, очень просто и легко реализовать.


От Роберта Лава (хакер ядра): http://permalink.gmane.org/gmane.linux.kernel.kernelnewbies/1791

Вы не можете спать в обработчике прерываний, потому что прерывания не имеют контекст процесса поддержки, и поэтому нет ничего, чтобы перенести обратно в. Другими словами, обработчики прерываний не связаны с задачей, так что нечего "усыплять" и (что более важно) "нечего проснись ". Они должны бежать атомарно.

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

Причина, по которой обработчик ошибок страницы может спать, заключается в том, что он вызывается только по коду, который выполняется в контексте процесса. Потому что собственное ядро память не поддерживается, только доступ к памяти в пользовательском пространстве может привести к ошибка страницы. Таким образом, только несколько определенных мест (например, звонки на copy_ {to, from} _user ()) может вызвать ошибку страницы в ядре. Те все места должны быть созданы кодом, который может спать (т. е. контекстом процесса, без замков и так далее).

6 голосов
/ 28 июня 2009

Поскольку инфраструктура переключения потоков в этот момент непригодна для использования. При обслуживании прерывания может выполняться только материал с более высоким приоритетом - см. Руководство разработчика программного обеспечения Intel по приоритету прерывания, задачи и процессора . Если вы разрешите выполнение другого потока (что вы подразумеваете в своем вопросе, что это будет легко сделать), вы не сможете позволить ему делать что-либо - если это вызвало ошибку страницы, вам придется использовать службы в ядре, которое невозможно использовать во время обслуживания прерывания (см. ниже, почему).

Как правило, ваша единственная цель в процедуре прерывания - заставить устройство перестать прерывать и ставить что-то в очередь на более низком уровне прерывания (в Unix это обычно уровень без прерываний, но для Windows это диспетчеризация, apc или пассив). уровень), чтобы сделать тяжелую работу, где у вас есть доступ к дополнительным функциям ядра / ОС. Смотрите - Реализация обработчика .

Это свойство того, как должны работать O / S, а не то, что присуще Linux. Процедура прерывания может выполняться в любой момент, поэтому состояние прерывания не соответствует. Если вы прервали выполнение кода планирования потока, его состояние не согласовано, поэтому вы не можете быть уверены, что можете «спать» и переключать потоки. Даже если вы защищаете код переключения потоков от прерывания, переключение потоков является функцией высокого уровня O / S, и если вы защитили все, на что оно опирается, прерывание становится скорее подсказкой, чем императивом, подразумеваемым его именем.

3 голосов
/ 28 июня 2009

Так что же мешает сэчдулеру перевести контекст прерывания в спящий режим и запустить следующий запланированный процесс и передать ему управление?

Планирование происходит при прерываниях по таймеру. Основное правило заключается в том, что одновременно может быть открыто только одно прерывание, поэтому, если вы засыпаете в прерывании «полученные данные с устройства X», прерывание по таймеру не может быть запущено для его планирования.

Прерывания также случаются много раз и перекрываются. Если вы поместите прерывание «получил данные» в спящий режим, а затем получите больше данных, что произойдет? Это сбивает с толку (и хрупко) достаточно того, что правило всеобъемлющего: не спать в прерываниях. Вы сделаете это неправильно.

2 голосов
/ 30 июля 2009

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

1 голос
/ 27 сентября 2013

По своей природе вопрос заключается в том, можете ли вы в обработчике прерываний получить действительный «текущий» (адрес текущей структуры задачи), если да, то можно соответствующим образом изменить содержимое, чтобы перевести его в состояние «сна», который может быть возвращен планировщиком позже, если состояние будет изменено каким-либо образом. Ответ может зависеть от аппаратного обеспечения.

Но в ARM это невозможно, поскольку «current» не имеет значения для обработки в режиме прерывания. Смотрите код ниже:

#linux/arch/arm/include/asm/thread_info.h 
94 static inline struct thread_info *current_thread_info(void)
95 {
96  register unsigned long sp asm ("sp");
97  return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
98 }

sp в режимах USER и SVC - это "одинаково" ("то же самое" здесь не означает, что они равны, вместо этого точка sp пользовательского режима указывает на стек пространства пользователя, в то время как sp r13_svc режима svc указывает на стек ядра, где структура задачи пользовательского процесса была обновлена ​​при предыдущем переключении задач. Когда происходит системный вызов, процесс снова входит в пространство ядра, когда sp (sp_svc) все еще не изменен, эти 2 sp связаны друг с другом, в этом смысле они ' re 'same'), поэтому в режиме SVC код ядра может получить действительный 'current'. Но в других привилегированных режимах, скажем, в режиме прерывания, sp «отличается», указывает на выделенный адрес, определенный в cpu_init (). «Текущий», рассчитанный в этих режимах, не будет иметь отношения к прерванному процессу, а доступ к нему приведет к неожиданному поведению. Вот почему всегда говорят, что системный вызов может спать, но обработчик прерываний не может, системный вызов работает в контексте процесса, но прерывания нет.

1 голос
/ 17 января 2011

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

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

Я также не думаю, что если обработчик прерываний был заблокирован, его нельзя разбудить. Когда мы говорим «блокировать», в основном это означает, что заблокированный процесс ожидает некоторого события / ресурса, поэтому он связывает себя с некоторой очередью ожидания для этого события / ресурса. Всякий раз, когда ресурс освобождается, процесс освобождения отвечает за пробуждение процесса (ов) ожидания.

Однако действительно раздражает то, что заблокированный процесс ничего не может сделать во время блокировки; он не сделал ничего плохого в этом наказании, что несправедливо. И никто точно не может предсказать время блокировки, поэтому невинный процесс должен ждать по непонятной причине и неограниченное время.

0 голосов
/ 31 января 2013

Это просто выбор дизайна / реализации в ОС Linux. Преимущество этого дизайна простое, но оно может не подходить для требований ОС в реальном времени.

Другие ОС имеют другие конструкции / реализации.

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

0 голосов
/ 03 августа 2010

В ядре Linux есть два способа выделения стека прерываний. Один находится в стеке ядра прерванного процесса, другой - выделенный стек прерываний на процессор. Если контекст прерывания сохраняется в выделенном стеке прерываний для каждого ЦП, то действительно, контекст прерывания совершенно не связан ни с каким процессом. «Текущий» макрос создаст недопустимый указатель на текущий запущенный процесс, поскольку «текущий» макрос с некоторой архитектурой вычисляется с помощью указателя стека. Указатель стека в контексте прерывания может указывать на выделенный стек прерывания, а не на стек ядра какого-либо процесса.

0 голосов
/ 30 июля 2009

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

Когда эта подпрограмма прерывания запускается, она поднимает уровень приоритета до уровня исходной подпрограммы прерывания и продолжает выполнение. Это имеет тот же эффект, что и сон.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...