Путаница вокруг spin_lock_irqsave: в какой вложенной ситуации сохраняется состояние прерывания?

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

Мой первый вопрос: (Q1) в ситуации SMP schedule() запускается на каждом процессоре одновременно (я знаю, что планирование начинается с прерывания таймера jiffies)? Я предполагаю, что да в моем вопросе ниже. Я был бы признателен, если бы кто-нибудь вкратце объяснил мне, как процессы перемещаются между ядрами процессора во время планирования.

Пытаюсь понять, как, почему, когда используется spin_lock/unlock_irqsave. Вот мой вопрос.

Предположим, есть код, который вызывает spin_lock_irqsave, и состояние прерывания (разрешено) было «отключено» во время вызова spin_lock_irqsave(). Может ли этот код работать в контексте прерывания? Вероятно, нет, потому что ISR не должна была сработать в первую очередь, если прерывание было отключено в соответствующем локальном процессоре. Следовательно, код, вызывающий spin_lock_irqsave, должен находиться в контексте процесса. Хорошо, прерывание ранее было отключено, но процесс пытается заблокироваться с помощью spin_lock_irqsave.

В каком случае прерывание можно было отключить? Думаю, есть два случая.

Случай 1: предыдущая процедура прерывания была вытеснена этим процессом (который вызывает этот spin_lock_irqsave). Это странно, потому что прерывание ISR невозможно. (Q2) Между прочим, может ли процесс прерывания прерывания прерывания прерывания прерываться процессом? (Q3) Я полагаю, поскольку preempt_count() это #defined как (current_thread_info()->preempt_count), preempt_disable работает только для процесса, а не для прерывания. Есть ли в прерываниях информация о текущем потоке?

Случай 2: предыдущий нормальный процесс получил блокировку с spin_lock_irq (или irqsave). Но это также странно, потому что перед блокировкой spin_lock_irq (или irqsave) отключает вытеснение и прерывание для задачи, сообщая планировщику не переключаться на другую задачу после прерывания таймера планировщика. Так что это не может быть правдой.

Я знаю, что мне нужно продолжить изучение планирования процессов для SMP и приоритетного прерывания работы ядра, и, возможно, я что-то неправильно понимаю. Может ли кто-нибудь прояснить мой вопрос? Большое спасибо за чтение.


person Chan Kim    schedule 26.03.2020    source источник


Ответы (1)


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

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

Я настоятельно рекомендую вам прочитать Глава 5: Условия параллелизма и гонки < / em> книги Драйверы устройств Linux, который находится в свободном доступе в Интернете. В частности, раздел «Спин-блокировки» в главе 5 очень полезен для понимания того, как спин-блокировки могут быть полезны в различных ситуациях.

(Q1) в ситуации SMP, schedule() выполняется ли одновременно на всех процессорах? [...] Я был бы признателен, если бы кто-нибудь вкратце объяснил мне, как процессы также перемещают ядра процессора во время планирования.

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

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

Сходство ЦП задачи - это то, что определяет, на каких ЦП может выполняться задача. «Нормальное» соответствие по умолчанию позволяет запускать задачу на любом ЦП (за исключением особых конфигураций). В зависимости от их принадлежности задачи могут быть перемещены из одной очереди выполнения в другую либо с помощью планировщика, либо, если они того требуют, с помощью системного вызова sched_setaffinity (здесь a соответствующий ответ, в котором объясняется, как).

Предлагаемое чтение: Полное руководство по планированию процессов Linux < / em>.

Предположим, есть код, который вызывает spin_lock_irqsave, и состояние прерывания (разрешено) было «отключено» во время вызова spin_lock_irqsave(). Может ли этот код работать в контексте прерывания? Возможно нет.

Почему нет? Это возможно. Код может выполняться в контексте прерывания, но не вызывается другим прерыванием. См. Нижнюю часть моего ответа.

Случай 1: предыдущая процедура прерывания была вытеснена этим процессом (который вызывает этот spin_lock_irqsave). Это странно, потому что прерывание ISR невозможно.

Вы правы, это странно. Более чем странно, это невозможно. В Linux в любое время прерывания могут быть включены или отключены (между ними нет). На самом деле нет «приоритета» для прерываний (как для задач), но мы можем разделить их на два ранга:

  • Непрерываемые прерывания, которые обязательно должны выполняться от начала до конца с полным контролем ЦП. Эти прерывания переводят систему в состояние «отключенные прерывания», и никакие другие прерывания невозможны.
  • Вытесняемые прерывания, которые повторяются и допускают возникновение других прерываний. В случае, если во время обслуживания этого прерывания происходит другое прерывание, вы входите во вложенный сценарий прерывания, который аналогичен сценарию вложенных обработчиков сигналов для задач.

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

(Q2) Между прочим, может ли процесс прерывания прерывания прерывания прерывания прерываться процессом?

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

(Q3) [...] У прерываний также есть информация о текущем потоке?

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

Случай 2: предыдущий нормальный процесс получил блокировку с spin_lock_irq (или irqsave). Но это также странно, потому что перед блокировкой spin_lock_irq (или irqsave) отключает вытеснение и прерывание для задачи, говоря планировщику не переключаться на другую задачу после прерывания таймера планировщика. Так что это не может быть правдой.

Да, ты прав. Это невозможно.


Функция spin_lock_irqsave() существует для использования в обстоятельствах, в которых вы не можете знать, были ли прерывания уже отключены или нет, и поэтому вы не можете использовать spin_lock_irq(), за которым следует spin_unlock_irq(), потому что эта вторая функция принудительно повторно включает прерывания. Кстати, это также объясняется в главе 5 «Драйверы устройств Linux», на которую я ссылался выше.

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

Возможны следующие сценарии:

  1. Первоначальное отключение прерывания было вызвано обработчиком прерывания, и теперь вы выполняете другой фрагмент кода как часть того же обработчика прерывания (т. Е. Текущая функция была вызвана прямо или косвенно самим обработчиком прерывания ). Вы вполне можете вызвать spin_lock_irqsave() в функции, которая вызывается обработчиком прерывания. Или даже просто вызов local_irqsave() (например, функция kfree() делает это, и ее, безусловно, можно вызвать из контекста прерывания).

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

person Marco Bonelli    schedule 26.03.2020
comment
Большое спасибо! Это прояснило мне несколько вещей. Я читал «Драйвер устройства Linux от Корбета», но некоторые вещи не были любезно объяснены, например, когда неясно, отключено ли прерывание ... теперь я понимаю это намного лучше. Только один вопрос, я думаю, что в некоторых архитектурах процедура обработки прерывания с более высоким приоритетом может начать вытеснять процедуру обработки прерывания с более низким приоритетом, не так ли? возможно, это может быть еще один случай, когда мы не уверены, что прерывание включено или не видно из кода ISR с более высоким приоритетом. и я видел в архитектуре sparc, spin_lock_irqsave хранит 4-битный уровень прерывания. - person Chan Kim; 26.03.2020
comment
И хотя процесс одновременно запускается только на одном процессоре, переходя от ядра к ядру ... как вы сказали, я понимаю, что может быть несколько процессов, выполняющих один и тот же код, или даже много экземпляров одной и той же программы (например, когда мы запускаем многие независимые браузеры), это может быть еще один случай, когда код не знает, отключено прерывание или нет. - person Chan Kim; 26.03.2020
comment
@ChanKim Я думаю, что процедура обработки прерывания с более высоким приоритетом может запустить программу обслуживания прерывания с более низким приоритетом - Да, моя проблема, поскольку вы говорили, что прерывания отключены, я предполагал только прерывания с высоким приоритетом. Я обновил свой ответ в приведенном выше случае 1, чтобы он стал яснее. - person Marco Bonelli; 26.03.2020
comment
Я снова прочитал ваш обновленный ответ (потому что снова запутался, читая книгу :)). Вы сказали, что процессы на самом деле ничего не вытесняют, они вытесняются ядром, которое берет на себя управление., Но я понимаю, что `` вытеснение '' не означает вмешательство планировщика (прерыванием таймера) во время обычного пользовательского процесса или процесса ядра , но переключение на другой процесс по результату планирования. Думаю, это важный момент для многих. Что вы думаете о моем понимании? - person Chan Kim; 08.04.2020
comment
@ChanKim Вытеснение не означает переключение на другой процесс. Вытеснение означает принудительное управление процессором для запуска планировщика. Переключение на другой процесс может произойти, а может и не произойти (например, планировщик может решить оставить текущую задачу запущенной). Переключение на другой процесс также может происходить естественным образом, без вытеснения: например, если процесс завершает выполнение через системный вызов выхода, следующая задача для запуска выбирается автоматически без необходимости вытеснять текущую задачу. - person Marco Bonelli; 08.04.2020
comment
@ChanKim Насколько мне известно, обработчик прерывания с низким приоритетом не может быть вытеснен планировщиком, он может быть прерван только другим обработчиком прерывания. И это то, что я пытался сказать в ответе на ваш вопрос 2. - person Marco Bonelli; 08.04.2020
comment
Бонели Я вижу, я помню, как читал, что, когда ядро ​​является «невытесняемым», любой код ядра не прерывается планировщиком до тех пор, пока он не откажется от ЦП. (как до ядра 2.4?) Спасибо, очень помогло. - person Chan Kim; 08.04.2020
comment
@Boneli а .. Я снова читаю это. ты сказал, а почему бы и нет? Это возможно. Код может выполняться в контексте прерывания, но не вызываться другим прерыванием. Разве это не должно быть ... но теперь вызвано другим прерыванием? Это была опечатка? - person Chan Kim; 10.08.2020
comment
@ChanKim Это не опечатка. См. Нижнюю часть моего ответа, пункт 1. Если вы находитесь в сценарии, в котором прерывания отключены, и вы обнаруживаете, что вызываете spin_lock_irqsave(), тогда код, который это сделал, должен исходить из того же прерывания, которое отключило прерывания в первую очередь. - person Marco Bonelli; 10.08.2020