Добавлен: 29.10.2018
Просмотров: 48005
Скачиваний: 190
116
Глава 2. Процессы и потоки
памяти, единые строки описания конфигурации и одни и те же открытые файлы.
И больше ничего. Обычно после этого дочерний процесс изменяет образ памяти
и запускает новую программу, выполняя системный вызов execve или ему подобный.
Например, когда пользователь набирает в оболочке команду sort, оболочка создает от-
ветвляющийся дочерний процесс, в котором и выполняется команда sort. Смысл этого
двухступенчатого процесса заключается в том, чтобы позволить дочернему процессу
управлять его файловыми дескрипторами после разветвления, но перед выполнением
execve с целью выполнения перенаправления стандартного ввода, стандартного вывода
и стандартного вывода сообщений об ошибках.
В Windows все происходит иначе: одним вызовом функции Win32 CreateProcess
создается процесс, и в него загружается нужная программа. У этого вызова имеется
10 параметров, включая выполняемую программу, параметры командной строки
для этой программы, различные параметры безопасности, биты, управляющие на-
следованием открытых файлов, информацию о приоритетах, спецификацию окна,
создаваемого для процесса (если оно используется), и указатель на структуру, в кото-
рой вызывающей программе будет возвращена информация о только что созданном
процессе. В дополнение к функции CreateProcess в Win32 имеется около 100 других
функций для управления процессами и их синхронизации, а также выполнения всего,
что с этим связано.
В обеих системах, UNIX и Windows, после создания процесса родительский и дочер-
ний процессы обладают своими собственными, отдельными адресными пространства-
ми. Если какой-нибудь процесс изменяет слово в своем адресном пространстве, другим
процессам эти изменения не видны. В UNIX первоначальное состояние адресного про-
странства дочернего процесса является копией адресного пространства родительского
процесса, но это абсолютно разные адресные пространства — у них нет общей памяти,
доступной для записи данных. Некоторые реализации UNIX делят между процессами
текст программы без возможности его модификации. Кроме того, дочерний процесс
может совместно использовать всю память родительского процесса, но если память со-
вместно используется в режиме копирования при записи (copy on write), это означает,
что при каждой попытке любого из процессов модифицировать часть памяти эта часть
сначала явным образом копируется, чтобы гарантировать модификацию только в за-
крытой области памяти. Следует также заметить, что память, используемая в режиме
записи, совместному использованию не подлежит.
Тем не менее вновь созданный процесс может делить со своим создателем часть других
ресурсов, например открытые файлы. В Windows адресные пространства родительского
и дочернего процессов различаются с самого начала.
2.1.3. Завершение процесса
После создания процесс начинает работать и выполняет свою задачу. Но ничто не длит-
ся вечно, даже процессы. Рано или поздно новые процессы будут завершены, обычно
в силу следующих обстоятельств:
обычного выхода (добровольно);
выхода при возникновении ошибки (добровольно);
возникновения фатальной ошибки (принудительно);
уничтожения другим процессом (принудительно).
2.1. Процессы
117
Большинство процессов завершаются по окончании своей работы. Когда компилятор
откомпилирует заданную ему программу, он осуществляет системный вызов, сообща-
ющий операционной системе о завершении своей работы. Этим вызовом в UNIX яв-
ляется exit, а в Windows — ExitProcess. Программы, работающие с экраном, также
поддерживают добровольное завершение. Текстовые процессоры, интернет-браузеры
и аналогичные программы всегда содержат значок или пункт меню, на котором поль-
зователь может щелкнуть, чтобы приказать процессу удалить все временные файлы,
которые им были открыты, и завершить свою работу.
Вторая причина завершения — обнаружение процессом фатальной ошибки. Например,
если пользователь наберет команду
cc foo.c
с целью компиляции программы foo.c, а файла с таким именем не будет, то произойдет
простое объявление о наличии данного факта и выход из компилятора. Выхода из ин-
терактивных, использующих экран процессов при задании им неверных параметров
обычно не происходит. Вместо этого появляется диалоговое окно с просьбой о повтор-
ной попытке ввода параметров.
Третья причина завершения — ошибка, вызванная самим процессом, чаще всего
связанная с ошибкой в программе. В качестве примеров можно привести неверную
инструкцию, ссылку на несуществующий адрес памяти или деление на нуль. В не-
которых системах (например, UNIX) процесс может сообщить операционной системе
о своем намерении обработать конкретные ошибки самостоятельно, в таком случае,
когда встречается одна из таких ошибок, процесс получает сигнал (прерывается), а не
завершается.
Четвертая причина, из-за которой процесс может быть завершен, — это выполнение
процессом системного вызова, приказывающего операционной системе завершить
некоторые другие процессы. В UNIX этот вызов называется kill. Соответствующая
функция Win32 называется TerminateProcess. В обоих случаях у процесса, вызывающего
завершение, должны быть на это соответствующие полномочия. В некоторых системах
при добровольном или принудительном завершении процесса тут же завершаются и все
созданные им процессы. Но ни UNIX, ни Windows так не делают.
2.1.4. Иерархии процессов
В некоторых системах, когда процесс порождает другой процесс, родительский и до-
черний процессы продолжают оставаться определенным образом связанными друг
с другом. Дочерний процесс может и сам создать какие-нибудь процессы, формируя
иерархию процессов. Следует заметить, что, в отличие от растений и животных, ис-
пользующих половую репродукцию, у процесса есть только один родитель (но нуль,
один, два или более детей). Следовательно, процесс больше похож на гидру, чем, ска-
жем, на корову.
В UNIX процесс, все его дочерние процессы и более отдаленные потомки образуют
группу процессов. Когда пользователь отправляет сигнал с клавиатуры, тот достигает
всех участников этой группы процессов, связанных на тот момент времени с клавиату-
рой (обычно это все действующие процессы, которые были созданы в текущем окне).
Каждый процесс по отдельности может захватить сигнал, игнорировать его или совер-
шить действие по умолчанию, которое должно быть уничтожено сигналом.
118
Глава 2. Процессы и потоки
В качестве другого примера, поясняющего ту ключевую роль, которую играет иерар-
хия процессов, давайте рассмотрим, как UNIX инициализирует саму себя при запуске
сразу же после начальной загрузки компьютера. В загрузочном образе присутствует
специальный процесс, называемый init. В начале своей работы он считывает файл,
сообщающий о количестве терминалов. Затем он разветвляется, порождая по одному
процессу на каждый терминал. Эти процессы ждут, пока кто-нибудь не зарегистриру-
ется в системе. Если регистрация проходит успешно, процесс регистрации порождает
оболочку для приема команд. Эти команды могут породить другие процессы и т. д.
Таким образом, все процессы во всей системе принадлежат единому дереву, в корне
которого находится процесс init.
В отличие от этого в Windows не существует понятия иерархии процессов, и все про-
цессы являются равнозначными. Единственным намеком на иерархию процессов
можно считать присвоение родительскому процессу, создающему новый процесс, спе-
циального маркера (называемого дескриптором), который может им использоваться
для управления дочерним процессом. Но он может свободно передавать этот маркер
какому-нибудь другому процессу, нарушая тем самым иерархию. А в UNIX процессы
не могут лишаться наследственной связи со своими дочерними процессами.
2.1.5. Состояния процессов
Несмотря на самостоятельность каждого процесса, наличие собственного счетчика
команд и внутреннего состояния, процессам зачастую необходимо взаимодействовать
с другими процессами. Один процесс может генерировать выходную информацию, ис-
пользуемую другими процессами в качестве входной информации. В команде оболочки
cat chapter1 chapter2 chapter3 | grep tree
первый процесс, запускающий программу cat, объединяет и выдает на выходе содержи-
мое трех файлов. Второй процесс, запускающий программу grep, выбирает все строки,
в которых содержится слово «tree». В зависимости от относительной скорости этих
двух процессов (которая зависит от двух факторов: относительной сложности программ
и количества выделяемого каждому из них времени работы центрального процессора)
может получиться так, что программа grep готова к работе, но ожидающие ее входные
данные отсутствуют. Тогда она должна блокироваться до поступления входных данных.
Процесс блокируется из-за того, что, по логике, он не может продолжаться, как пра-
вило, потому что ожидает недоступных в настоящий момент входных данных. Может
случиться и так, что останавливается тот процесс, который в принципе готов к работе
и может быть запущен, а причина кроется в том, что операционная система решила на
некоторое время выделить центральный процессор другому процессу. Эти два условия
полностью отличаются друг от друга. В первом случае приостановка порождена какой-
нибудь проблемой (вы не можете обработать пользовательскую командную строку,
пока она не будет введена). Во втором случае на первый план выступает техническая
сторона вопроса (не хватает центральных процессоров, чтобы каждому процессу вы-
делить собственный процессор). На рис. 2.2 показана диаграмма, отображающая три
состояния, в которых может находиться процесс:
выполняемый (в данный момент использующий центральный процессор);
готовый (работоспособный, но временно приостановленный, чтобы дать возмож-
ность выполнения другому процессу);
2.1. Процессы
119
заблокированный (неспособный выполняться, пока не возникнет какое-нибудь
внешнее событие).
Логически первые два состояния похожи друг на друга. В обоих случаях процесс
желает выполняться, но во втором состоянии временно отсутствует доступный для
этого процессор. Третье состояние коренным образом отличается от первых двух тем,
что процесс не может выполняться, даже если процессору кроме него больше нечем
заняться.
Рис. 2.2. Процесс может быть в выполняемом, заблокированном или готовом состоянии.
Стрелками показаны переходы между этими состояниями
Как показано на рисунке, между этими тремя состояниями могут быть четыре пере-
хода. Переход 1 происходит в том случае, если операционная система определит, что
процесс в данный момент выполняться не может. В некоторых системах для перехода
в заблокированное состояние процесс может осуществить такой системный вызов,
как pause. В других системах, включая UNIX, когда процесс осуществляет чтение из
канала или специального файла (например, с терминала) и доступные входные данные
отсутствуют, процесс блокируется автоматически.
Переходы 2 и 3 вызываются планировщиком процессов, который является частью
операционной системы, без какого-либо оповещения самого процесса. Переход 2 проис-
ходит, когда планировщик решит, что выполняемый процесс продвинулся достаточно
далеко и настало время позволить другому процессу получить долю рабочего времени
центрального процессора. Переход 3 происходит, когда все другие процессы получили
причитающуюся им долю времени и настал момент предоставить центральный про-
цессор первому процессу для возобновления его выполнения. Вопрос планирования,
то есть решение, какой именно процесс, когда и сколько времени должен выполняться,
играет весьма важную роль и будет рассмотрен в этой главе чуть позже. В попытках
сбалансировать конкурирующие требования соблюдения эффективности системы
в целом и справедливого отношения к отдельному процессу было изобретено множе-
ство алгоритмов. Некоторые из них еще будут рассмотрены в этой главе.
Переход 4 осуществляется в том случае, если происходит внешнее событие, ожидав-
шееся процессом (к примеру, поступление входных данных). Если к этому моменту
нет других выполняемых процессов, будет вызван переход 3 и процесс возобновится.
В противном случае ему придется немного подождать в состоянии готовности, пока не
станет доступен центральный процессор и не придет его очередь.
Использование модели процесса облегчает представление о том, что происходит внутри
системы. Некоторые процессы запускают программу, выполняющую команды, введен-
ные пользователем. Другие процессы являются частью системы, справляясь с такими
задачами, как выполнение запросов на обслуживание файлов или управление деталями
работы дискового или ленточного привода. Когда происходят дисковые прерывания,
система принимает решение остановить выполнение текущего процесса и запустить
120
Глава 2. Процессы и потоки
процесс работы с диском, заблокированный в ожидании этого прерывания. Таким об-
разом, вместо того чтобы думать о прерываниях, мы можем думать о пользовательских
процессах, процессах работы с диском, процессах работы с терминалом и т. д., которые
блокируются, когда ожидают каких-то событий. Когда считана информация с диска
или набран символ, процесс, ожидающий это событие, разблокируется и получает
право на возобновление выполнения.
В результате такого представления возникает модель, показанная на рис. 2.3. На этом
рисунке самым нижним уровнем операционной системы является планировщик, над
которым изображен ряд процессов. Вся обработка прерываний и подробности дей-
ствий, запускающих и останавливающих процессы, здесь скрыты под тем, что называ-
ется планировщиком, для реализации которого используется сравнительно небольшой
объем кода. Вся остальная часть операционной системы неплохо структурирована
в виде процессов. Но такой структурой обладает сравнительно небольшое количество
настоящих систем.
Рис. 2.3. Самый низший уровень структурированной в виде процессов
операционной системы обрабатывает прерывания и планирует выполнение процессов.
Выше этого уровня находятся последовательные процессы
2.1.6. Реализация процессов
Для реализации модели процессов операционная система ведет таблицу (состоящую
из массива структур), называемую таблицей процессов, в которой каждая запись соот-
ветствует какому-нибудь процессу. (Ряд авторов называют эти записи блоками управ-
ления процессом
.) Эти записи содержат важную информацию о состоянии процесса,
включая счетчик команд, указатель стека, распределение памяти, состояние открытых
им файлов, его учетную и планировочную информацию и все остальное, касающееся
процесса, что должно быть сохранено, когда процесс переключается из состояния вы-
полнения в состояние готовности или блокировки, чтобы позже он мог возобновить
выполнение, как будто никогда не останавливался.
В табл. 2.1 показан ряд ключевых полей типовой системы. Поля первого столбца
относятся к управлению процессами. Поля остальных двух столбцов относятся
к управлению памятью и файлами соответственно. Следует заметить, что наличие
тех или иных полей в таблице процессов в большей степени зависит от системы, но
в этой таблице изложено основное представление о типе необходимой информации.
Теперь, после изучения таблицы процессов, появилась возможность чуть лучше объ-
яснить, как создается иллюзия нескольких последовательных процессов, выполняе-
мых на одном (или на каждом) центральном процессоре. Существует область памяти
(обычно это фиксированная область в нижних адресах), связанная с каждым классом
устройств ввода-вывода, которая называется вектором прерывания. В ней содержится
адрес процедуры, обслуживающей прерывание. Предположим, что при возникновении
дискового прерывания выполнялся пользовательский процесс № 3. Счетчик команд