Гордеев А.В. Операционные системы (2-е изд., 2004) (1186250), страница 83
Текст из файла (страница 83)
Если командному интерпретатору встречается команда, соответствующая выполняемому файлу, то онсоздает новый процесс и запускает в нем соответствующую программу, начиная сфункции main. Эта запущенная программа, в свою очередь, может создать процесс изапустить в нем другую программу (та тоже должна содержать функцию main) и т. д.Для образования нового процесса и запуска в нем программы используются двасистемных вызова API — fork() и ехес(имя_выполняемого_файла). Системный вызовfork() приводит к созданию нового адресного пространства, состояние которого абсолютно идентично состоянию адресного пространства основного процесса (то естьв нем содержатся те же программы и данные). Для дочернего процесса заводятсякопии всех сегментов данных.Другими словами, сразу после выполнения системного вызова forkQ основной (родительский) и порожденный процессы являются абсолютными близнецами; управление в том и другом находится в точке, непосредственно следующей за вызовомfork().
Чтобы программа могла разобраться, в каком процессе (основном или порожденном) она теперь работает, функция fork() возвращает разные значения:О в порожденном процессе и целое положительное число в основном процессе. Этимцелым положительным числом является уже упоминавшийся идентификатор процесса (PID).
Таким образом, родительский процесс будет знать идентификаторсвоего дочернего процесса и может при необходимости управлять им.Теперь, если мы хотим запустить новую программу в порожденном процессе, нужно обратиться к системному вызову exec, указав в качестве аргументов вызова имяфайла, содержащего новую выполняемую программу, и, возможно, одну или несколько текстовых строк, которые будут переданы в качестве аргументов функции main новой программы. Выполнение системного вызова exec приводит к тому,что в адресное пространство порожденного процесса загружается новая выполняемая программа и запускается с адреса, соответствующего входу в функцию main.Другими словами, это приводит к замене текущего программного сегмента и текущего сегмента данных, которые были унаследованы при выполнении вызова fork,соответствующими сегментами, заданными в файле. Прежние сегменты теряются.Это эффективный метод смены выполняемой процессом программы, но не самогопроцесса.
Файлы, уже открытые до вызова примитива exec, остаются открытымипосле его выполнения.В следующем примере пользовательская программа, вызываемая как команда оболочки, выполняет в отдельном процессе стандартную команду Is оболочки, которая выдает на экран содержимое текущего каталога.mainO{if(forkO--O) wait(O);/* родительский процесс */else exec!("Is". "Is".
0): /* порожденный процесс */}320Глава 10, Краткий обзор современных операционных системТаким образом, с практической точки зрения процесс в UNIX является объектомсоздаваемым в результате выполнения функции fork(). Каждый процесс за исключением начального (нулевого) порождается в результате вызова другим процессом функции fork(). Каждый процесс имеет одного родителя, но может породитьмного процессов. Начальный (нулевой) процесс является особенным процессомкоторый создается в результате загрузки системы. После порождения нового процесса с идентификатором 1 нулевой процесс становится процессом подкачки иреализует механизм виртуальной памяти. Процесс с идентификатором 1, известный под именем init, является предком любого другого процесса в системе и связан с каждым процессом особым образом.ФункционированиеТеперь, когда мы познакомились с основными понятиями, рассмотрим наиболеехарактерные моменты функционирования UNIX-системы.Выполнение процессовПроцесс может выполняться в одном из двух состояний, а именно пользовательском и системном.
В пользовательском состоянии процесс выполняет пользовательскую программу и имеет доступ к пользовательскому сегменту данных. В системном состоянии процесс выполняет программы ядра и имеет доступ к системномусегменту данных.Когда пользовательскому процессу требуется выполнить системную функцию,он делает системный вызов. Фактически происходит вызов ядра системы как подпрограммы. С момента системного вызова процесс считается системным. Такимобразом, пользовательский и системный процессы являются двумя фазами одного и того же процесса, но они никогда не пересекаются между собой. Каждаяфаза пользуется своим собственным стеком.
Стек задачи содержит аргументы,локальные переменные и другую информацию относительно функций, выполняемых в режиме задачи. Диспетчерский процесс не имеет пользовательскойфазы.В UNIX-системах организуется разделение времени (time sharing), то есть каждому процессу выделяется квант времени.
Либо процесс завершается сам до истечения отведенного ему кванта времени, либо он приостанавливается по истечениикванта и продолжает свое исполнение при очередном получении нового квантавремени. Механизм диспетчеризации характеризуется достаточно справедливымраспределением процессорного времени между всеми процессами. Пользовательским процессам приписываются приоритеты в зависимости от получаемого имипроцессорного времени. Процессам, которые получили много процессорного времени, назначают более низкие приоритеты, в то время как процессам, которые получили лишь немного процессорного времени, наоборот, повышают приоритет.Вспомните рассмотренные ранее механизмы динамических приоритетов.
Такометод диспетчеризации обеспечивает хорошее время реакции для всех пользователей системы. Все системные процессы имеют более высокие приоритеты по еранению с пользовательскими и поэтому всегда обслуживаются в первую очередь.Подсистема ввода-выводафункции ввода-вывода в UNIX задаются в основном с помощью пяти системныхвызовов: open, close, read, write и seek.Открыть файл можно следующей командой:file_descriptor = open (filejiame.
mode)Здесь mode — режим открытия файла (чтение, запись или то и другое); file_descriptor — дескриптор файла, служит для последующих ссылок на данный файл;file_name — имя открываемого файла.Чтение и запись осуществляются командами следующего вида:after_reading_bytes = read (file_descriptor. buffer, bytes)after_writing_bytes = write (file_descriptor, buffer, bytes)Здесь bytes — количество байтов, которые должны быть прочитаны или записаны;after_reading_bytes и after_writing_bytes — реально прочитанное и записанное количество байтов соответственно.При чтении возможны три ситуации, в каждой из которых чтение происходит последовательно:О если это первое чтение из файла, то оно осуществляется последовательно с самого начала файла;Q если операции чтения предшествовала другая операция чтения из этого файла,то текущая операция предоставит данные, непосредственно следующие за предыдущими;• если предшествовала операция поиска seek (см.
далее), то чтение осуществляется последовательно от точки смещения, указанной в операции seek.Это же справедливо и по отношению к операции записи в файл. Обратите внимание, что все эти вызовы относятся к последовательному доступу и эффект прямойадресации достигается с помощью команды seek, смещающей текущую позициюфайла:Seek (fi 1 ejdescriptor,displacement,displacement_type)Здесь параметр displacement_type (тип смещения) определяет, является смещениеабсолютным или относительным, а также задано оно числом байтов или числомблоков по 512 байт.Важно заметить, что команда seek исполняется для магнитных дисков так же, каки для магнитных лент, которые нынче уже практически не используются, но вовремена появления и становления UNIX-систем были часто используемым устройством.Чтобы закрыть файл, достаточно выполнить следующую команду:close (file_descriptor)Еще три примитива — gtty, stty, stat — позволяют получать и задавать информацию о файлах и терминалах.Те же самые команды ввода-вывода применяются и к физическим устройствам.В UNIX-системах физические устройства представлены специальными файламивединой структуре файловой системы.
Это означает, что пользователь не может322Глава 10, Краткий обзор современных операционных системнаписать зависящую от устройств программу, если только эта зависимость не отражена в самом потоке передаваемых данных. Стандартные файлы ввода и вывода, приписываемые пользовательскому терминалу, открывать обычным путем нетребуется. Терминал открывается автоматически по команде входа в систему login.Система ввода-вывода UNIX в отличие от большинства других систем ориентирована на работу скорее с потоком данных, а не с записями. Здесь поток данных1(stream) — это последовательность байтов, закапчивающаяся разделителем (то естьсимволом конца потока). Понятие потока данных позволяет проще добиться независимости от устройств и унификации файлов с физическими устройствами и конвейерами.
Тем самым пользователь получает гибкость в работе с группами данных, но на него ложатся и дополнительные заботы, поскольку ему приходитсяписать программы управления данными. Пользователь может при необходимостиотносительно легко самостоятельно реализовать работу с записями. Чтобы работать с записями фиксированной длины, достаточно просто задавать постояннуюдлину во всех командах чтения и записи. Для нахождения позиции нужной записипри фиксированной длине записей нужно умножить длину записи на номер записи и выполнить команду seek.
Работу с записями переменной длины можно организовать, если разместить в начале каждой записи поле фиксированного размера,содержащее значение длины записи.Перенаправление ввода-выводаМеханизм перенаправления ввода-вывода является одним из наиболее элегантных, мощных и одновременно простых механизмов UNIX. Цель, которая ставилась при разработке этого механизма, состоит в следующем. Поскольку UNIX —это интерактивная система, которая создавалась в конце 60-х — начале 70-х годов,то обычно программы считывали текстовые строки с алфавитно-цифрового терминала и выводили результирующие текстовые строки на экран терминала.