Операционная система UNIX - Андрей Робачевский
Шрифт:
Интервал:
Закладка:
□ Копирует сохраненные аргументы и переменные окружения в новый стек процесса.
□ Устанавливает обработку всех сигналов на умалчиваемые значения, поскольку процесс теперь не имеет требуемых обработчиков. Установки для игнорируемых и заблокированных сигналов не изменяются.
□ Инициализирует аппаратный контекст процесса. В частности, после этого указатель инструкций адресует точку входа новой программы.
В случае, когда программа использует динамические библиотеки, соответствующий раздел исполняемого файла (для файла формата ELF данный раздел имеет тип INTERP) содержит имя редактора связей динамической библиотеки. В этом случае редактор связей должен быть запущен до начала выполнения основной программы для связывания с программами требуемых динамических библиотек. Таким образом точка входа в программу устанавливается на точку входа в редактор связей. После завершения своей работы редактор связей, в свою очередь, запускает программу самостоятельно, анализируя заголовок исполняемого файла. Стадии запуска новой программы проиллюстрированы на рис. 3.16.
Рис. 3.16. Запуск новой программы: а) Адресное пространство процесса до вызова exec(2); б) Уничтожение старого адресного пространства; в) Новое адресное пространство процесса; г) Новое адресное пространство процесса при использовании динамических библиотек
Выполнение в режиме ядра
Существуют всего три события, при которых выполнение процесса переходит в режим ядра — аппаратные прерывания, особые ситуации и системные вызовы. Во всех случаях ядро UNIX получает управление и вызывает соответствующую системную процедуру для обработки события. Перед вызовом ядро сохраняет состояние прерванного процесса в системном стеке. После завершения обработки, состояние процесса восстанавливается и процесс возвращается в исходный режим выполнения. Чаще всего это режим задачи, но если, например, прерывание возникло, когда процесс уже находился в режиме ядра, после обработки события он останется в этом режиме.
Отметим существенную разницу между прерываниями и особыми ситуациями. Аппаратные прерывания генерируются периферийными устройствами при наступлении определенных событий (например, завершение дисковой операции ввода/вывода или поступление данных на последовательный порт) и имеют асинхронный характер, поскольку невозможно точно сказать, в какой момент наступит то или иное прерывание. Более того, эти прерывания, как правило, не связаны с текущим процессом, а вызваны внешними событиями. Именно поэтому, обработка прерываний происходит в системном контексте, при этом недопустим доступ к адресному пространству процесса, например, к его u-area. По этой же причине, обработка прерываний не должна блокироваться, поскольку это вызовет блокирование выполнения независимого процесса.
Напротив, особые ситуации вызваны самим процессом, и связаны с выполнением тех или иных инструкций, например, деление на ноль или обращение к несуществующей странице памяти. Таким образом, обработка особых ситуаций производится в контексте процесса, при этом может использоваться его адресное пространство, а сам процесс — при необходимости блокироваться (перемещаться в состояние сна).
Системные вызовы позволяют процессам воспользоваться базовыми услугами ядра. Интерфейс системных вызовов определяет ограниченный набор точек входа в ядро системы, обращение к которым изменяет режим выполнения процесса и позволяет выполнять привилегированные инструкции ядра. Стандартная библиотека С, позволяющая использовать системные функции как обычные процедуры, на самом деле содержит заглушки, обеспечивающие фактическую реализацию вызова соответствующей точки входа ядра. Эта реализация существенным образом зависит от аппаратной архитектуры системы. Например, для систем на базе процессоров Intel используются шлюзы (gate). Имеются два типа шлюзов: шлюзы ловушек (trap gate) и шлюзы вызовов (call gate). Для осуществления вызова через шлюз ловушки процесс выполняет команду прерывания, а при работе шлюз вызова — команду межсегментного вызова.
Выполнение системного вызова происходит в режиме ядра, но в контексте процесса, сделавшего системный вызов. Таким образом, открыт доступ к адресному пространству процесса и используется стек ядра процесса.
Сон и пробуждение
Процесс обычно переводится в состояние сна при обработке системной функции. Если для завершения обработки запроса требуется недоступный ресурс, процесс снимается с процессора и переводится в состояние сна. Недоступность ресурса может быть связана с запуском операции ввода/вывода с диска, ожиданием выделения (освобождения) буфера, ожиданием ввода или вывода на терминал или ожиданием завершения дочернего процесса. К недоступным ресурсам можно также отнести отсутствующую в памяти страницу, к виртуальному адресу которой обратился процесс. В любом случае процесс переходит в состояние сна до наступления события, делающего ресурс доступным. Во время сна процесс не потребляет вычислительные ресурсы системы. При этом выполняется переключение контекста на другой, высокоприоритетный процесс для выполнения. Таким образом, процесс, ожидающий ввода с клавиатуры, не занимает процессор, циклически опрашивая терминальную линию, а процесс, считывающий данные с диска, не блокирует выполнение других задач.
Состояние сна — это логическое состояние процесса, при этом он не перемещается физически в памяти. Переход в состояние сна в первую очередь определяется занесением в системную таблицу процессов соответствующего флага состояния и события, пробуждающего процесс.
События возвещают о доступности того или иного ресурса. Как правило события связаны с работой периферийных устройств, таких как диск, терминал и принтер, поэтому об их наступлении сигнализируют соответствующие аппаратные прерывания. Наступления одного и того же события может ожидать несколько процессов. Поскольку переход из состояния в состояние акт скорее логический, то и пробуждаются все эти процессы одновременно. Однако это не означает, что какой-либо один из них сразу начнет выполняться. Это лишь приводит к тому, что их состояние меняется от "сна" к "готов к выполнению", и они помещаются в очередь на запуск. Задачу выбора процесса для запуска затем решает планировщик процессов.
События, в ожидании которых "засыпают" процессы, не являются равноценными.
Во-первых, они различаются по вероятности наступления. Например событие, связанное с завершением операции ввода с диска или освобождением буфера, имеет высокую вероятность. Как правило, подобные операции имеют конечное время выполнения, в противном случае система оказалась бы заблокированной. С другой стороны, вероятность наступления события, связанного с вводом с терминала, может быть весьма низкой. Пользователь может надолго оставить терминал, не завершив сеанса работы с системой. В длительном ожидании события нет ничего опасного — процесс не занимает ресурсы процессора, однако без специальных мер выключение терминала приведет к блокировке этого устройства. Для того чтобы избежать подобной ситуации, должна существовать возможность вывести процесс из состояния сна, несмотря на отсутствие ожидаемого события. В этом случае используется стандартное решение — отправление процессу сигнала. В противоположность этому, отправление сигнала процессу, ожидающему операции ввода с диска, может привести к ухудшению производительности системы. Поэтому все события и связанные с ними ресурсы разделяются на две категории по вероятности их наступления: на допускающие прерывание сигналом и на не допускающие таковых.
Во-вторых, процессы, разбуженные событием, должны иметь различную вероятность запуска. Это, в первую очередь, связано с тем, что несколько ресурсов могут отображаться на одно событие. Например, процесс А, ожидающий завершения операции ввода с диска, и процесс В, ожидающий освобождения буфера ввода, будут связаны с одним и тем же событием. Они оба окажутся "разбуженными" и затем "готовыми к запуску" после завершения этой операции. Если процесс В будет запущен первым, он все равно не сможет выполняться, так как буфер не освобожден процессом А. Даже в случае, когда спящие процессы связаны с различными событиями, необходимо отдавать предпочтение процессу с более ценным ресурсом. Например, освобождение буфера ввода безусловно предпочтительнее завершения ввода с терминала.
Поскольку планировщик принимает решение о запуске процесса, основываясь на приоритетах, единственным способом установить "справедливый" порядок запуска процессов является присвоение определенного приоритета каждому событию. Приоритет процесса и его влияние на планирование достаточно подробно обсуждались в разделе "Контекст процесса".