Н.В. Вдовикина, И.В. Машечкин, А.Н. Терехин, А.Н. Томилин - Операционные системы - взаимодействие процессов (2008), страница 10
Описание файла
PDF-файл из архива "Н.В. Вдовикина, И.В. Машечкин, А.Н. Терехин, А.Н. Томилин - Операционные системы - взаимодействие процессов (2008)", который расположен в категории "". Всё это находится в предмете "операционные системы" из 3 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Просмотр PDF-файла онлайн
Текст 10 страницы из PDF
Для этого служит системный вызовwait():#include <sys/types.h>#include <sys/wait.h>pid_t wait (int *status);При обращении к этому вызову выполнение родительскогопроцесса приостанавливается до тех пор, пока один из его потомковне завершится либо не будет остановлен. Если у процесса имеетсянесколько потомков, процесс будет ожидать завершения любого изних (т.е., если процесс хочет получить информацию о завершениикаждого из своих потомков, он должен несколько раз обратиться квызову wait()).Возвращаемым значением wait() будет идентификаторзавершенного процесса, а через параметр status будет возвращенаинформация о причине завершения процесса (завершен путемвызова _exit(), либо прерван сигналом) и коде возврата.
Еслипроцесс не интересуется этой информацией, он может передать вкачестве аргумента вызову wait() NULL-указатель.Конкретный формат данных, записываемых в параметрstatus, может различаться в разных реализациях ОС. Во всехсовременных версиях UNIX определены специальные макросы дляизвлечения этой информации, например:макрос WIFEXITED(*status) возвращает ненулевое значение,если процесс был завершен путем вызова _exit(), при этом макросWEXITSTATUS(*status) возвращает статус завершения, переданныйчерез _exit();макросWIFSIGNALED(*status)возвращаетненулевоезначение, если процесс был прерван сигналом, при этом макросWTERMSIG(*status) возвращает номер этого сигнала;макрос WIFSTOPPED(*status) возвращает ненулевое значение,если процесс был приостановлен системой управления заданиями,при этом макрос WSTOPSIG(*status) возвращает номер сигнала, cпомощью которого он был приостановлен.Если к моменту вызова wait() один из потомков данногопроцесса уже завершился, перейдя в состояние зомби, товыполнение родительского процесса не блокируется, и wait() сразуже возвращает информацию об этом завершенном процессе.
Если жек моменту вызова wait() у процесса нет потомков, системныйвызов сразу же вернет –1. Также возможен аналогичный возврат из54этого вызова, если его выполнение будет прервано поступившимсигналом.После того, как информация о статусе завершения процессазомби будет доставлена его предку посредством вызова wait(), всеоставшиеся структуры, связанные с данным процессом-зомби,освобождаются, и запись о нем удаляется из таблицы процессов.Таким образом, переход в состояние зомби необходим именно длятого, чтобы процесс-предок мог получить информацию о судьбесвоего завершившегося потомка, независимо от того, вызвал ли онwait() до или после фактического его завершения.Что происходит с процессом-потомком, если его предоквообще не обращался к wait() и/или завершился раньше потомка?Как уже говорилось, при завершении процесса отцом для всех егопотомков становится процесс с идентификатором 1.
Он иосуществляет системный вызов wait(), тем самым освобождая всеструктуры, связанные с потомками-зомби.Часто используется сочетание функций fork()-wait(), еслипроцесс-сын предназначен для выполнения некоторой программы,вызываемой посредством функции exec(). Фактически этимпредоставляется процессу-родителю возможность контролироватьокончание выполнения процессов-потомков.Пример 7. Использование системного вызова wait().Примерпрограммы,последовательнозапускающейпрограммы, имена которых переданы ей в командной строке.#include <sys/types.h>#include <unistd.h>#include <sys/wait.h>#include <stdio.h>int main(int argc, char **argv){int i;for (i=1; i<argc; i++){int status;if(fork()>0){/*процесс-предок ожидает сообщенияот процесса-потомка о завершении */55wait(&status);printf(“process-father\n”);continue;}execlp(argv[i], argv[i], 0);return -1;/*попадем сюда при неуспехе exec()*/}return 0;}Пусть существуют три исполняемых файла print1, print2,print3, каждый из которых печатает текст first, second, thirdсоответственно, а код вышеприведенного примера находится висполняемом файле с именем file.
Тогда результатом работыкоманды file print1 print2 print3 будетfirstprocess-fathersecondprocess-fatherthirdprocess-fatherПример 8. Использование системного вызова wait().В данном примере процесс-предок порождает два процесса,каждый из которых запускает команду echo (как уже говорилось, этакоманда копирует на свой стандартный вывод аргументы,переданные ей в командной строке). Далее процесс-предок ожидаетзавершения своих потомков, после чего продолжает выполнение.#include <sys/types.h>#include <unistd.h>#include <sys/wait.h>#include <stdio.h>int main(int argc, char **argv){if ((fork()) == 0) /*первый процесс-потомок*/{56execl(“/bin/echo”,”string 1”, 0);”echo”,”thisis”,return -1;}if ((fork()) == 0) /*второй процесс-потомок*/{execl(“/bin/echo”,”string 2”, 0);”echo”,”thisis”,return -1;}/*процесс-предок*/printf(“process-fatherchildren\n”);iswaitingforwhile(wait(NULL) != -1);printf(“all children terminated\n”);return 0;}В данном случае wait() вызывается в цикле три раза – первыедва ожидают завершения процессов-потомков, последний вызоввернет неуспех, ибо ждать более некого.3.6 Жизненный цикл процесса в ОС UNIXПодведем короткие итоги.
Итак, процесс в UNIX представляетсобой исполняемую программу вместе с необходимым ейконтекстом. Контекст состоит из информации о процессе, котораясодержится в различных системных структурах данных,информации о содержимом регистров, стеке процесса, информацииоб открытых файлах, обработке сигналов и так далее. Процесспредставляет собой изменяющийся во времени динамическийобъект. Программа представляет собой часть процесса. Процессможет создавать процессы-потомки посредством системного вызоваfork(), может изменять свою программу через системный вызовexec(). Процесс может приостановить свое исполнение, используявызов wait(), а также завершить свое исполнение посредствомфункции exit().С учетом вышеизложенного, рассмотримсостояния, в которых может находится процесс:подробнее1.
процесс только что создан посредством вызова fork();2. процесс находится в очереди готовых на выполнение процессов;573. процесс выполняется в режиме задачи, т.е. реализуется алгоритм,заложенный в программу. Выход из этого состояния можетпроизойти в случае обращения к системному вызову,возникновения прерывания или завершения процесса;4. процесс может выполняться в режиме ядра ОС, если потребованию процесса посредствомсистемного вызовавыполняются определенные инструкции ядра ОС или произошлопрерывание;5. если процесс в ходе выполнения не имеет возможности получитьтребуемый ресурс, он переходит в состояние блокирования;6. процесс осуществил вызов _exit() или получилсигнал,реакцией на который является завершение.
Ядро освобождаетресурсы, связанные с процессом, кроме кода возврата истатистики выполнения. Далее процесс переходит в состояниизомби, а затем уничтожается.планирование выполнения процессовexit()fork()СозданГотов квыполнениюочередь готовыхпроцессовВыполняетсяЗомбиВ режиме ядраВ пользовательскомрежимевнешнее событиеРодительвызывает wait()системный вызов;прерываниеБлокированожидает внешнегособытияРис. 12 Жизненный цикл процесса в ОС UNIX.3.7 Начальнаяпроцессовзагрузка.ФормированиесистемныхРассмотрим подробнее, что происходит в момент начальнойзагрузки OC UNIX. Под загрузкой мы будем понимать процессинициализации системы от момента включения питания и доприведения её в полностью работоспособное состояние.При включении питания ВС начинает работу аппаратныйзагрузчик. Он считывает и загружает в память нулевой блок58системного устройства (т.е.
того внешнего устройства, с которогобудет производиться загрузка). Нулевой блок любой файловойсистемы предназначен для записи короткой программы – т.н.программного загрузчика, смысл которого заключается в том,чтобы запустить ядро ОС9. Именно исполняемый файлпрограммного загрузчика и считывается первым в оперативнуюпамять.Затем запускается программный загрузчик, он ищет исчитывает в память расположенный в корневом каталоге файл/unix10, который содержит код ядра ОС.
После этого запускается наисполнение этот файл, т.е. происходит загрузка ядра.Ядро путем выполнения тестовых программ определяет,сколько памяти имеется в ВС, резервирует необходимый объемпамяти для размещения своих внутренних таблиц и буферов, затемприступает к выявлению и конфигурированию аппаратных ресурсовсистемы. Кроме того, на этом этапе ядром выполняются и другиедействия по инициализации системы, а именно, устанавливаютсясистемные часы (для генерации прерываний), формируетсядиспетчер памяти, формируются значения некоторых структурданных и др.По окончании базовой инициализации ядро формирует рядсистемных процессов.
Их набор и функции могут несколькоотличаться в разных системах UNIX, но, как правило, во всехсистемах имеется начальный системный процесс с номером 0 ипроцесс init с номером 1.Сначала формируется нулевой процесс. По понятнымпричинам для этого невозможно использовать методы порожденияпроцессов, рассмотренные выше, т.е. использовать системныевызовы fork() и exec(). При инициализации нулевого процессарезервируется память под его контекст и формируется нулеваязапись в таблице процессов.
Нулевой процесс не имеет сегментакода, т.е. фактически он представляет собой часть ядра ОС (припомощи которой ядро организует мультипрограммный режим иуправление процессами), лишь выглядящую как процесс.9в реальных системах в нулевом блоке может быть записан не сам программныйзагрузчик, а лишь его адрес. В этом случае следующим шагом будет нахождение и считываниепрограммного загрузчика по указанному адресу. Некоторые системы поддерживают такжеболее сложные программные загрузчики, умеющие работать с несколькими операционнымисистемами и ядрами.10Имена файлов в данном случае представляют собой определенную условность.