Почему код ядра/поток, выполняемый в контексте прерывания, не может спать?

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

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

который говорит

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

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

Ответ 1

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

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


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

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

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

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

Ответ 2

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

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

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

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

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

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

В этот момент у вас есть тупик. Процесс1 не может возобновить выполнение до тех пор, пока ISR не будет выполнен с его стеком. Но ISR заблокирован, ожидая, пока Process1 освободит LockA.

Ответ 3

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

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

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

EDIT2: Удалено использование слова "shutdown", так как это подразумевает читателей. Для этого требуется O/S. Добавлены ссылки на более авторитетные ссылки, чтобы оправдать мой ответ.

Ответ 4

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

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

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

Ответ 5

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

Ответ 6

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

Ответ 7

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

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

Ответ 8

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

Ответ 9

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

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

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

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

Ответ 10

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

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

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

Ответ 11

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

Но в ARM это невозможно, так как "ток" не имеет отношения к процессу в режиме прерывания. См. Код ниже:

#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 указывает на стек пользовательского пространства, тогда как режим svc sp r13_svc указывает на стек ядра, где пользовательский процесс task_structure был обновлен при предыдущем переключении задачи. Когда происходит системный вызов, процесс снова вводит пространство ядра, когда sp (sp_svc) все еще не изменяется, эти 2 sp связаны друг с другом, в этом смысле они " re 'same'), поэтому в режиме SVC код ядра может получить действительный "текущий". Но в других привилегированных режимах, скажем, режим прерывания, sp является "другим ", указывает на выделенный адрес, определенный в cpu_init()." Текущий", рассчитанный в этом режиме, не будет иметь отношения к прерванному процессу, доступ к нему приведет к неожиданному поведению. Вот почему он всегда говорил, что системный вызов может спать, но обработчик прерываний не может, системный вызов работает в контексте процесса, но прерывает его.