Файл: А. В. Гордеев А. Ю. Молчанов системное программное обеспечение электронный вариант книги издательства Питер СанктПетербург Челябинск юургу каф. Автоматика и управление 2002 2 Предисловие Настоящий учебник.pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 12.01.2024
Просмотров: 1003
Скачиваний: 1
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
394
main(argc, argv)
{
int argc;
char* argv[ ];
if(argc!=2)
{
printf(“usage: %s your-text\n”, argv[0]);
exit;
}
printf(“%s\n”, argv[1]);
}
Процессы
Процесс в ОС UNIX понимается в классическом смысле этого термина, то есть как программа, выполняемая в собственном виртуальном адресном пространстве.
Когда пользователь входит в систему, автоматически создается процесс, в котором выполняется программа командного интерпретатора. Если командному интерпре- татору встречается команда, соответствующая выполняемому файлу, то он создает новый процесс и запускает в нём соответствующую программу, начиная с функции main. Эта запущенная программа, в свою очередь, может создать процесс и запус- тить в нём другую программу (она тоже должна содержать функцию main) и т. д.
Для образования нового процесса и запуска в нём программы используются два системных вызова API – fork( ) и ехес(имя_выполняемого_файла). Системный вызов fork приводит к созданию нового адресного пространства, состояние которо- го абсолютно идентично состоянию адресного пространства основного процесса
(то есть в нём содержатся те же программы и данные). Для дочернего процесса за- водятся копии всех сегментов данных.
Другими словами, сразу после выполнения системного вызова fork основной
(родительский) и порожденный процессы являются абсолютными близнецами;
управление и в том и в другом находится в точке, непосредственно следующей за вызовом fork. Чтобы программа могла разобраться, в каком процессе она теперь работает – в основном или порождённом, функция fork возвращает разные значе- ния: 0 в порождённом процессе и целое положительное число (идентификатор по- рождённого процесса – так называемый PID) в основном процессе.
395
Теперь, если мы хотим запустить новую программу в порождённом процессе,
нужно обратиться к системному вызову ехес, указав в качестве аргументов вызова имя файла, содержащего новую выполняемую программу, и, возможно, одну или несколько текстовых строк, которые будут переданы в качестве аргументов функ- ции main новой программы. Выполнение системного вызова ехес приводит к тому,
что в адресное пространство порожденного процесса загружается новая выполняе- мая программа и запускается с адреса, соответствующего входу в функцию main.
Другими словами, это приводит к замене текущего программного сегмента и теку- щего сегмента данных, которые были унаследованы при выполнении вызова fork,
на новые соответствующие сегменты, заданные в файле. Прежние сегменты теря- ются. Это эффективный метод смены выполняемой процессом программы, но не самого процесса. Файлы, уже открытые до выполнения примитива ехес, остаются открытыми после его выполнения.
В следующем примере пользовательская программа, вызываемая как команда shell, выполняет в отдельном процессе стандартную команду shell ls, которая выда-
ёт на экран содержимое текущего каталога файлов.
main( )
{
if (fork ( )==(0) wait(0); /* родительский процесс */
else execl('ls", "Is", 0); /* порождённый процесс */
}
Таким образом, с практической точки зрения процесс в UNIX является объек- том, создаваемым в результате выполнения функции fork( ). Каждый процесс, за исключением начального (нулевого), порождается в результате запуска другим процессом операции fork( ). Каждый процесс имеет одного родителя, но может по- родить много процессов. Начальный (нулевой) процесс является особенным про- цессом, который создается в результате загрузки системы. После порождения но- вого процесса с идентификатором 1 нулевой процесс становится процессом под- качки и реализует механизм виртуальной памяти. Процесс с идентификатором 1,
известный под именем init, является предком любого другого процесса в системе и связан с каждым процессом особым образом.
396
1 ... 27 28 29 30 31 32 33 34 ... 37
Функционирование системы UNIX
Теперь, когда мы знаем основные понятия, рассмотрим наиболее характерные моменты функционирования этой системы.
Выполнение процессов
Процесс может выполняться в одном из двух состояний, а именно пользова-
тельском и системном. В пользовательском состоянии процесс выполняет пользо- вательскую программу и имеет доступ к пользовательскому сегменту данных. В
системном состоянии процесс выполняет программы ядра и имеет доступ к сис- темному сегменту данных.
Когда пользовательскому процессу требуется выполнить системную функцию,
он создает системный вызов. Фактически происходит вызов ядра системы как под- программы. С момента появления системного вызова процесс считается систем- ным. Таким образом, пользовательский и системный процессы являются двумя фа- зами одного и того же процесса, но они никогда не пересекаются между собой.
Каждая фаза пользуется своим собственным стеком. Стек задачи содержит аргу- менты, локальные переменные и другую информацию относительно функций, вы- полняемых в режиме задачи. Диспетчерский процесс не имеет пользовательской фазы.
В UNIX-системах используется разделение времени, то есть каждому процес- су выделяется квант времени. Либо процесс завершается сам до истечения отве- дённого ему кванта времени, либо он откладывается по истечении кванта. Меха- низм диспетчеризации характеризуется достаточно справедливым распределением процессорного времени между всеми процессами. Пользовательским процессам приписываются приоритеты в зависимости от количества получаемого ими про- цессорного времени. Процессам, которые получили большое количество процес- сорного времени, назначают более низкие приоритеты, в то время как процессам,
которые получили лишь небольшое количество процессорного времени – наобо- рот, повышают приоритет. Вспомните рассмотренные ранее механизмы динамиче- ских приоритетов (см. подраздел «Диспетчеризация задач с использованием дина- мических приоритетов», глава 2). Такой метод диспетчеризации обеспечивает хо-
397
рошее время реакции для всех пользователей системы. Все системные процессы имеют более высокие приоритеты по сравнению с пользовательскими и поэтому всегда обслуживаются в первую очередь.
Подсистема ввода/вывода
Функции ввода/вывода в UNIX задаются в основном с помощью пяти систем- ных вызовов, а именно: open, close, read, write и seek.
Открыть файл можно командой file_descriptor = open (file_name, mode)
где параметр mode
(режим) указывает, разрешено ли чтение, запись или и то и другое; file_descriptor
– дескриптор файла, служит для последующих ссылок на дан- ный файл.
Чтение и запись осуществляется командами следующего вида:
after_reading_bytes = read (fne_descriptor, buffer, bytes)
after_writing_bytes = write (file_descriptor, buffer, bytes)
где bytes
– это число байтов, которые должны быть прочитаны или записаны;
after_reading_bytes и after_writing_bytes
– это реально прочитанное и записанное количество байтов соответственно.
При чтении возможны три ситуации, в каждой из которых чтение происходит последовательно:
♦ если это первое чтение из файла, то оно осуществляется последовательно с самого начала файла;
♦ если операции чтения предшествовала другая операция чтения из этого файла, то текущая операция предоставит нам данные, непосредственно следующие за предыдущими;
♦ если предшествовала операция поиска seek
(см. далее), то чтение осуществ- ляется последовательно от точки смещения, указанной в операции seek
Это же справедливо и по отношению к операции записи в файл. Обратите внимание, что все эти вызовы относятся к последовательному доступу и эффект прямой адресации достигается с помощью команды seek
, смещающей текущую по- зицию файла
Seek (fiIe_descriptor, displacement, displacement_type).
398
Здесь параметр displacement_type
(тип смещения) определяет в команде, явля- ется ли смещение абсолютным или относительным, а также задано ли оно числом байтов или числом блоков по 512 байт.
Важно заметить, что команда seek исполняется для магнитных дисков так же,
как и для магнитных лент, которые нынче уже практически не используются, но во времена появления и становления UNIX-систем были часто используемым устрой- ством.
Чтобы закрыть файл, достаточно выполнить команду close (file_descnptor)
Еще три примитива – gtty
, stty
, stat позволяют получать и задавать информа- цию о файлах и терминалах.
Те же самые команды ввода/вывода применяются и к физическим устройст- вам. В системе UNIX физические устройства представлены специальными файла- ми в единой структуре файловой системы. Это означает, что пользователь не мо- жет написать зависящую от устройств программу, если только эта зависимость не отражена в самом потоке передаваемых данных. Стандартные файлы ввода и вы- вода, приписываемые пользовательскому терминалу, открывать обычным путем не требуется. Терминал открывается автоматически по команде входа в систему –
login
Система ввода/вывода UNIX, в отличие от большинства других систем, ориен- тирована скорее на работу с потоком, а не с записями. Здесь поток (stream) – это последовательность байтов, заканчивающаяся разделителем (то есть символом конца потока end-of-stream
). Понятие потока позволяет проще добиться независи- мости от устройств и унификации файлов с физическими устройствами и транс- портерами (конвейерами). Тем самым пользователь получает гибкость в работе с группами данных, но на него ложатся и дополнительные заботы, поскольку ему приходится писать программы управления данными. Пользователь может при не- обходимости относительно легко самостоятельно реализовать работу с записями.
Чтобы работать с записями фиксированной длины, достаточно просто задавать по- стоянную длину во всех командах чтения и записи. Прямой доступ при фиксиро-
399
ванной длине записей получается путем умножения длины записи на номер записи и выполнения команды seek для нахождения позиций нужной записи. Работу с за- писями переменной длины можно организовать, если разместить в начале каждой записи поле фиксированного размера, содержащее длину записи.
Перенаправление ввода/вывода
Механизм перенаправления ввода/вывода является одним из наиболее эле- гантных, мощных и одновременно простых механизмов ОС UNIX. Цель, которая ставилась при разработке этого механизма, состоит в следующем. Поскольку UNIX
– это интерактивная система, которая создавалась в конце 60-х – начале 70-х годов,
то обычно программы вводили текстовые строки с терминала и выводили резуль- тирующие текстовые строки на экран терминала. Для того чтобы обеспечить более гибкое использование таких программ, желательно было уметь обеспечить им ввод из файла или из вывода других программ и направить их вывод в файл или на ввод другим программам.
Реализация этого механизма основывается на следующих свойствах ОС UNIX.
Во-первых, любой ввод/вывод трактуется как ввод из некоторого файла и вывод в некоторый файл. Клавиатура и экран терминала тоже интерпретируются как файлы
(первый можно только читать, а во второй можно только писать). Во-вторых, дос- туп к любому файлу производится через его дескриптор (положительное целое число). Фиксируются три значения дескрипторов файлов. Файл с дескриптором 1
называется файлом стандартного ввода (stdin), файл с дескриптором 2 – файлом стандартного вывода (stdout) и файл с дескриптором 3 – файлом стандартного вы- вода диагностических сообщений (stderr). В-третьих, программа, запущенная в не- котором процессе, «наследует» от породившего процесса все дескрипторы откры- тых файлов.
В головном процессе интерпретатора командного языка файлом стандартного ввода является клавиатура терминала пользователя, а файлами стандартного выво- да и вывода диагностических сообщений – экран терминала. Однако при запуске любой команды можно сообщить интерпретатору (средствами соответствующего командного языка), какой файл или вывод какой программы должен служить фай-
400
лом стандартного ввода для запускаемой программы и какой файл или ввод какой программы должен служить файлом стандартного вывода или вывода диагности- ческих сообщений для запускаемой программы. Тогда интерпретатор перед вы- полнением системного вызова ехес открывает указанные файлы, подменяя смысл дескрипторов 1, 2 и 3.
То же самое может проделать и любая другая программа, запускающая третью программу в специально созданном процессе. Следовательно, всё, что требуется для нормального функционирования механизма перенаправления ввода/вывода, –
это придерживаться при программировании соглашения об использовании деск- рипторов stdin, stdout и stderr. Это не очень трудно, поскольку в наиболее распро- страненных функциях библиотеки ввода/вывода printf, scanf и error вообще не тре- буется указывать дескриптор файла. Функция printf неявно использует stdout,
функция scanf – stdin, а функция error – stderr.
Файловая система
Файл в системе UNIX представляет собой множество символов с произволь- ным доступом. В файле содержатся произвольные данные, помещенные туда поль- зователем, и файл не имеет никакой другой структуры, кроме той, какую налагает на него пользователь.
Структура файловой системы
Здесь мы рассмотрим одну из первых реализации файловой системы, посколь- ку основные её идеи сохраняются до сих пор.
Информация на дисках размещается поблочно, по 512 байт в каждом блоке.
Обратите внимание, что блок специально взят равным размеру сектора. Диск раз- бивается на следующие области (рис. 8.1):
♦ неиспользуемый блок;
♦ управляющий блок или суперблок, в котором содержится размер диска и границы других областей;
♦ i-список, состоящий из описаний файлов, называемых i-узлами;
♦ область для хранения содержимого файлов.
401
Рис. 8.1. Организация файлов в UNIX на диске
Каждый i-узел содержит:
♦ идентификацию владельца;
♦ идентификацию группы владельца;
♦ биты защиты;
♦ физические адреса на диске или ленте, где находится содержимое файла;
♦ размер файла;
♦ время создания файла;
♦ время последнего использования файла (modification time);
♦ время последнего изменения атрибутов (change time);
♦ число связей-ссылок, указывающих на файл;
♦ индикацию, является ли файл директорией, обычным файлом или специаль- ным файлом. Следом за i-списком идут блоки, предназначенные для хранения со- держимого файлов. Пространство на диске, оставшееся свободным от файлов, об- разует связанный список свободных блоков.
402
Таким образом, файловая система UNIX представляет собой структуру дан- ных, размещённую на диске и содержащую управляющий суперблок, в котором определена файловая система в целом; массив i-узлов, где определены все файлы в файловой системе; сами файлы; и, наконец, совокупность свободных блоков. Вы- деление пространства под данные осуществляется блоками фиксированного разме- ра.
Каждый файл однозначно идентифицируется старшим номером устройства,
младшим номером устройства и i-номером (индексом i-узла данного файла в мас- сиве i-узлов). Когда вызывается драйвер устройства, по старшему номеру индекси- руется массив входных точек в драйверы. По младшему номеру драйвер выбирает одно устройство из группы идентичных физических устройств.
Файл-директория, в котором перечислены имена файлов, позволяет устано- вить соответствие между именами и самими файлами. Директории образуют дре- вовидную структуру. На каждый обычный файл или файл устройства могут иметь- ся ссылки в различных узлах этой структуры. В непривилегированных программах запись в директории не разрешена, но при наличии соответствующих разрешений они могут читать их. Дополнительных связей между директориями нет.
Большое число системных директорий система UNIX использует для своих собственных нужд. Одна из них, корневая директория, является базой для всей структуры директорий, и, «отталкиваясь» от неё, можно найти размещение всех файлов. В других системных директориях содержатся программы и команды, пре- доставляемые пользователям, и файлы устройств.
Имена файлов задаются последовательностью имён директорий, разделенных косой чертой («/») и приводящих к концевому узлу (листу) некоторого дерева. Ес- ли имя файла начинается с косой черты, то поиск по дереву начинается в корневой директории. Если же имя файла не имеет в начале косой черты, то поиск начинает- ся с текущей директории. Имена файлов, начинающиеся с «../», подразумевают на- чало поиска в директории, родительской по отношению к текущей. Имя файла stuff
(персонал) указывает на элемент stuff в текущей директории. Имя файла
/work/alex/stuff приводит к поиску директории work в корневой директории. Затем