Демон ksoftirqd

Демон ksoftirqd

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

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

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

Второе решение — это вообще не обрабатывать реактивизированные отложенные прерывания. После возврата из очередного обработчика прерывания ядро просто просматривает список всех ожидающих на выполнение отложенных прерываний и выполняет их как обычно. Если какое-то отложенное прерывание реактивизирует себя, то оно не будет выполняться до того времени, пока ядро в следующий раз снова не приступит к обработке отложенных прерываний. Однако такое, скорее всего, произойдет, только когда поступит следующее аппаратное прерывание, что может быть равносильно ожиданию в течение длительного промежутка времени, пока новое (или вновь активизированное) отложенное прерывание будет выполнено. В таком решении плохо то, что на не загруженной системе выгодно обрабатывать отложенные прерывания сразу же. К сожалению, описанный подход не учитывает то, какие процессы могут выполняться, а какие нет. Следовательно, данный метод хотя и предотвращает нехватку процессорного времени для задач пространства пользователя, но создает нехватку ресурсов для отложенных прерываний, и к тому же такой подход не выгоден для систем, работающих при малых нагрузках.

Необходим какой-нибудь компромисс. Решение, которое реализовано в ядре, — не обрабатывать немедленно вновь активизированные отложенные прерывания. Вместо этого, если сильно возрастает количество отложенных прерываний, ядро возвращает к выполнению (wake up) семейство потоков пространства ядра, чтобы они справились с нагрузкой. Данные потоки ядра работают с самым минимально возможным приоритетом (значение параметра nice равно 19). Это гарантирует, что они не будут выполняться вместо чего-то более важного. Но они в конце концов тоже когда-нибудь обязательно выполняются. Это предотвращает ситуацию нехватки процессорных ресурсов для пользовательских программ. С другой стороны, это также гарантирует, что даже в случае большого количества отложенных прерываний они все в конце концов будут выполнены. И наконец, такое решение гарантирует, что в случае незагруженной системы отложенные прерывания также обрабатываются достаточно быстро (потому что соответствующие потоки пространства ядра будут запланированы на выполнение немедленно).

Для каждого процессора существует свой поток. Каждый поток имеет имя в виде ksoftirqd/n, где n — номер процессора. Так в двухпроцессорной системе будут запущены два потока с именами ksoftiqd/0 и ksoftirqd/1. To, что на каждом процессоре выполняется свой поток, гарантирует, что если в системе есть свободный процессор, то он всегда будет в состоянии выполнять отложенные прерывания. После того как потоки запущены, они выполняют замкнутый цикл, похожий на следующий.

for (;;) {

 set_task_state(current, TASK_INTERRUPTIBLE);

 add_wait_queue(&cwq->more_work, &wait);

 if (list_empty(&cwq->worklist))

  schedule();

 else

  set_task_state(current, TASK_RUNNING);

 remove_wait_queue(&cwq->more_work, &wait);

 if (!list_empty(&cwq->worklist))

  run_workqueue(cwq);

}

Если есть отложенные прерывания, ожидающие на обработку (что определяет вызов функции softirq_pending()), то поток ядра ksoftirqd вызывает функцию do_softirq(), которая эти прерывания обрабатывает. Заметим, что это делается периодически, чтобы обработать также вновь активизированные отложенные прерывания. После каждой итерации при необходимости вызывается функция schedule(), чтобы дать возможность выполняться более важным процессам. После того как вся обработка выполнена, поток ядра устанавливает свое состояние в значение TASK_INTERRUPTIBLE и активизирует планировщик для выбора нового готового к выполнению процесса.

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