netlib.narod.ru | < Назад | Оглавление | Далее > |
Функция schedule (строка 26686) является функцией планировщика ядра и была рассмотрена очень подробно в главе 7. Версия schedule для симметричной мультипроцессорной системы имеет два основных отличия от версии для однопроцессорной системы:
26784: Получение текущего времени в виде числа циклов, которые истекли с момента включения компьютера. Это во многом аналогично проверке переменной jiffies, но со степенью детализации, измеряемой в циклах процессора, а не в импульсах сигнала времени таймера, очевидно, что это намного точнее.
26785: Вычисление величины промежутка времени, который истек с того момента, как функция schedule была запланирована на выполнение на этом процессоре, и регистрация текущего числа циклов на следующий раз, когда это снова произойдет. (Переменная schedule_data — это часть предназначенного для каждого процессора массива aligned_data, который определен в строке 26628.)
26790: Член avg_slice процесса (строка 16342) следит за тем, как долго процесс в среднем владел процессором в течение срока своего существования. Но это не простое среднее, а взвешенное среднее, в котором недавнее поведение процесса влияет на результаты намного сильнее, чем его поведение в отдаленном прошлом. (Поскольку настоящие компьютеры обладают конечной точностью, часть, относящаяся к «отдаленному прошлому», в конечном итоге приближается к нулю, когда прошлое становится достаточно отдаленным.) Этот член используется в функции reschedule_idle (строка 26221, которая рассматривается ниже) в качестве одного из оснований для принятия решения о том, направлять ли процесс на другой процессор. Поэтому он не нужен и не вычисляется для однопроцессорного случая.
26797: Регистрирует, какой процессор должен вступить в работу в следующую очередь (она выполняется на текущем процессоре), и поднимает его флажок has_cpu.
26803: При переключении контекста функция schedule регистрирует процесс, который теряет доступ к данному процессору; это нужно для работы функции __schedule_tail, которая рассматривается ниже.
26654: Если задача, которая только что потеряла доступ к процессору, изменила свое состояние (это описано в предыдущем комментарии), она отмечается для дальнейшего перепланирования.
26664: Поскольку ядро отключилось от этого процесса, он больше не имеет доступа к данному процессору и этот факт регистрируется.
26221: Функция reschedule_idle вызывается из функции wake_up_process, когда активизируемый процесс (идентификатор которого передается функции reschedule_idle в качестве параметра р) уже не находится в очереди выполнения. Функция пытается запланировать вновь активизированный процесс на другой процессор (холостой).
26225: Первая часть этой функции применяется и к симметричной мультипроцессорной системе, и к однопроцессорной системе. Она помогает высокоприоритетным процессам получить интервал времени процессора и, как правило, выполняет то же для процессов, переведенных на голодный паек. Если процесс является процессом в реальном масштабе времени или его динамический приоритет на определенную (произвольно выбранную) величину выше по сравнению с динамическим приоритетом процесса, который владеет процессором, этот процесс отмечается для перепланировки, чтобы он получил дополнительные шансы в конкуренции за процессор.
26263: Теперь мы переходим к части, касающейся симметричной мультипроцессорной системы, которая применяется только к процессам, не сумевшим пройти описанное выше испытание, однако это должно происходить довольно часто. Функция reschedule_idle должна определить, стоит ли попытаться выполнить этот процесс на другом процессоре.
Как уже упоминалось при описании функции schedule, член avg_slice процесса представляет собой взвешенную среднюю оценку потребления им процессорного времени, поэтому эта величина позволяет узнать, существует ли вероятность того, что определенный процесс будет непрерывно использовать процессор в течение относительно длительного времени, если он продолжит работу. Если нет, то этот процесс, вероятно, связан со вводом/выводом, во всяком случае он, по-видимому, не связан с интенсивным использованием процессора. Поскольку цель состоит в повышении общей пропускной способности за счет максимального распараллеливания, предоставление этому процессу, не связанному с использованием большого количества процессорного времени, другого процессора для его работы, вероятно, не имеет смысла.
26264: Во втором выражении этого условия if применяется макрокоманда related (расположенная сразу перед этой функцией в строке 26218), которая проверяет, владеют ли оба процесса или хотят ли владеть блокировкой ядра. Если да, то они, вероятно, не смогут выполняться одновременно, независимо от того, на какой процессор они запланированы, поэтому отправка данного процесса на другой процессор не повысит общую производительность параллельной обработки. Следовательно, если истинно это или предыдущее выражение, функция просто выполняет возврат, не рассматривая возможность перевода данного процесса на другой процессор.
26267: В остальных случаях вызывается функция reschedule_idle_slow (которая рассматривается ниже) для определения того, должен ли быть процесс уничтожен.
26157: Функция reschedule_idle_slow, как сказано в этих комментариях, пытается найти холостой процессор для размещения на нем процесса с идентификатором р. Алгоритм основан на том наблюдении, что первые n входов массива заданий представляют собой холостые системные процессы, по одному на каждый из n процессоров компьютера. Холостые процессы работают тогда (и только тогда), когда ни один другой процесс на данном процессоре не хочет получить к нему доступ. Холостой процесс обычно переводит процессор в режим ожидания с малым потреблением энергии с помощью команды hlt, если это возможно. Следовательно, просмотр в цикле первых n процессов в массиве задач — это все, что требуется для поиска холостого процессора, если он существует. Функция reschedule_idle_slow просто опрашивает каждый холостой процесс, выполняется ли он в настоящее время; если да, то процессор, на котором он работает, должен быть холостым и поэтому должен быть хорошим кандидатом для размещения на нем процесса р.
Безусловно, всегда возможно, что выбранный процессор, который с виду является холостым, является таковым только временно и вскоре должен быть загружен сверх меры десятками высокоприоритетных процессов, нуждающихся в большом количестве процессорного времени, которые активизируются через несколько наносекунд. Поэтому такой алгоритм не является идеальным, но он вполне приемлем с точки зрения статистических данных о загрузке процессора, и помните, что такой алгоритм выбран также потому, что он полностью соответствует принятому в планировщике принципу «забегаловки» — не заглядывать слишком далеко вперед и стараться все выполнить побыстрее.
26180: Устанавливает локальные переменные. Здесь best_cpu представляет собой процессор, на котором сейчас работает процесс р; это — «наилучший» процессор, поскольку, если процесс р останется здесь, то не придется промывать кэш или нести другие издержки, a this_cpu обозначает процессор, на котором работает функция reschedule_idle_slow.
26182: Переменные idle и tsk проходят по массиву задач, a target_tsk представляет собой последний найденный работающий холостой процесс (или NULL, если такового нет).
26183: Переменная i инициализируется функцией smp_num_cpus (которая была вызвана за n итераций перед этим) и ведет обратный отсчет при каждой итерации.
26189: Если установлен флажок has_cpu этой холостой задачи, она активно работает на своем процессоре (мы будем называть его «целевым процессором»). Если нет, то целевой процессор занят какой-то другой задачей, поэтому он не является холостым и функция reschedule_idle_slow не будет пытаться запланировать процесс р на него. Здесь проявляется оборотная сторона проблемы, упомянутой перед этим: тот факт, что процессор сейчас не является холостым, не означает, что вскоре на нем не закончат свою работу все его процессы и не сделают его холостым. Но функция reschedule_idle_slow не имеет способа узнать об этом, поэтому она вполне может предполагать, что целевой процессор на какое-то время будет занят. Во всяком случае, вполне вероятно, что так оно и будет, и даже если случится иное, то достаточно скоро на этот процессор, который только что стал холостым, будут запланированы какие-то другие процессы.
26190: Однако, если целевой процессор является текущим процессором, он будет пропущен. Такое решение кажется неожиданным, но так или иначе это — «невероятный» случай: счетчик холостого процесса является отрицательным, поэтому проверка в строке 26226 должна была заранее предотвратить возможность достижения настоящей функцией данной точки.
26192: Был найден подходящий холостой процессор; связанный с ним холостой процесс сохраняется с использованием переменной target_tsk. Почему бы теперь просто не прервать цикл, коль скоро найден подходящий целевой процессор? Дело в том, что продолжение цикла может выявить, что процессор, на котором находится процесс р, также холост, и лучше оставить процесс на его текущем процессоре, чем послать на другой, если оба процессора являются холостыми.
26193: Здесь функция reschedule_idle_slow проверяет, является ли холостым процессор, на котором работает процесс р. Если только что найденный холостой процессор является именно тем процессором, на котором уже размещен процесс р, функция переходит вперед к метке send (строка 26203), чтобы запланировать процесс р на этот процессор.
26199: Функция рассмотрела возможность применения другого процессора; она выполняет обратный отсчет.
26204: Если в этом цикле обнаружены какие-либо холостые процессоры, холостая задача процессора отмечается для перепланирования и функция smp_send_reschedule (строка 5019) посылает на этот процессор прерывание IPI, чтобы он перепланировал свои процессы.
Можно видеть, что функция reschedule_idle_slow — это прекрасный пример работы по координации взаимодействия между процессорами, которая просто не нужна в однопроцессорной системе. В однопроцессорном компьютере задача определения процессора, который должен быть получен процессом, просто эквивалентна определению того, должен ли процесс получить единственный процессор системы или не должен получить ничего. Поэтому в компьютере с симметричной мультипроцессорной архитектурой нужно также приложить некоторые усилия для определения того, какой из процессоров системы в наибольшей степени подходит для данного процесса. Безусловно, полученное взамен невероятное повышение скорости вполне оправдывает это дополнительное усилие.
22951: Часть функции release, не связанная с симметричной мультипроцессорной системой, рассматривалась в главе 7, а здесь процесс-зомби отправляется в могилу и освобождается его объект struct task_struct.
22960: Проверка того, владеет ли процесс процессором. (Процессор, которым владеет этот процесс, мог еще не успеть очистить этот флажок; это вскоре будет сделано.) Если нет, функция release выходит из цикла и приступает, как обычно, к освобождению объекта struct task_struct.
22966: Иначе функция release ждет очистки флажка has_cpu процесса. Когда это произойдет, функция release снова повторит свою попытку. Это внешне странная ситуация (процесс уничтожен, но владеет процессором) встречается действительно редко, но не является невероятной. Процесс мог быть уничтожен на одном процессоре, который еще не нашел времени, чтобы очистить флажок has_cpu, a родитель данного процесса проверяет значение этого флажка с другого процессора.
netlib.narod.ru | < Назад | Оглавление | Далее > |