Есть много вопросов и ответов о спин-блокировках, но меня это все еще сбивает с толку. Я думаю, это потому, что вопросы и ответы предполагают разные настройки или нечетко объясняют настройки о том, является ли это 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()
, а прерывания уже отключены чем-то другим. Это означает, что любая из родительских функций вызывающего абонента, которые в конечном итоге вызывали текущую функцию, должна каким-то образом уже отключить прерывания.
Возможны следующие сценарии:
Первоначальное отключение прерывания было вызвано обработчиком прерывания, и теперь вы выполняете другой фрагмент кода как часть того же обработчика прерывания (т. Е. Текущая функция была вызвана прямо или косвенно самим обработчиком прерывания ). Вы вполне можете вызвать spin_lock_irqsave()
в функции, которая вызывается обработчиком прерывания. Или даже просто вызов local_irqsave()
(например, функция kfree()
делает это, и ее, безусловно, можно вызвать из контекста прерывания).
Первоначальное отключение прерывания было вызвано нормальным кодом ядра, и теперь вы выполняете другой фрагмент кода как часть того же обычного кода ядра (т. Е. Текущая функция была вызвана прямо или косвенно другим ядром функция после отключения прерываний). Это вполне возможно, и именно поэтому существует irqsave
вариант.
person
Marco Bonelli
schedule
26.03.2020