Путаница вокруг spin_lock_irqsave: в какой вложенной ситуации сохраняется состояние прерывания? - PullRequest
0 голосов
/ 26 марта 2020

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

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

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

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

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

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

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

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

1 Ответ

1 голос
/ 26 марта 2020

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

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

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

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

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

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

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

Рекомендуется прочитать: Полное руководство по Linux Планирование процесса .

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

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

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

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

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

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

( Q2 ) Кстати, в вытесняющем ядре может ли процесс прерывать ISR процессом?

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

( 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(), и прерывания уже были отключены чем-то другим. , Это означает, что любая из родительских функций вызывающего абонента, которые в конечном итоге вызвали текущую функцию, должна как-то уже отключать прерывания.

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

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

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

...