The design of the unix operating system by Maurice J

Вид материалаРеферат
5.17 Абстрактные обращения к файловым системам
5.18 Сопровождение файловой системы
6.1 Состояния процесса и переходы между ними
Подобный материал:
1   ...   14   15   16   17   18   19   20   21   ...   55
│ ние файла по имени */ │

│ printf("stat %s завершилась неудачно\n",argv[1]);│

│ /* как и следовало бы */ │

│ else │

│ printf("stat %s завершилась успешно!\n",argv[1]);│

│ if (fstat(fd,&statbuf) == -1) /* узнать состояние │

│ файла по идентификатору */ │

│ printf("fstat %s сработала неудачно!\n",argv[1]);│

│ else │

│ printf("fstat %s завершилась успешно\n",argv[1]);│

│ /* как и следовало бы */ │

│ while (read(fd,buf,sizeof(buf)) > 0) /* чтение откры- │

│ того файла с удаленной связью */ │

│ printf("%1024s",buf); /* вывод на печать поля │

│ размером 1 Кбайт */ │

│ } │

L-------------------------------------------------------------


Рисунок 5.33. Удаление связи с открытым файлом


лагается, что тем временем никакой другой процесс не создал файл

с тем же именем), но функция fstat завершится успешно, так как

она выбирает индекс по дескриптору файла. Процесс выполняет цикл,

считывая на каждом шаге по 1024 байта и пересылая файл в стан-

дартный вывод. Когда при чтении будет обнаружен конец файла, про-

цесс завершает работу: после завершения процесса файл перестает

существовать. Процессы часто создают временные файлы и сразу же

удаляют связь с ними; они могут продолжать ввод-вывод в эти фай-

лы, но имена файлов больше не появляются в иерархии каталогов.

Если процесс по какой-либо причине завершается аварийно, он не

оставляет от временных файлов никакого следа.


5.17 АБСТРАКТНЫЕ ОБРАЩЕНИЯ К ФАЙЛОВЫМ СИСТЕМАМ


Уайнбергером было введено понятие "тип файловой системы" для

объяснения механизма работы принадлежавшей ему сетевой файловой

системы (см. краткое описание этого механизма в [Killian 84]) и в

позднейшей версии системы V поддерживаются основополагающие прин-

ципы его схемы. Наличие типа файловой системы дает ядру возмож-

ность поддерживать одновременно множество файловых систем, таких

как сетевые файловые системы (глава 13) или даже файловые системы

из других операционных систем. Процессы пользуются для обращения

к файлам обычными функциями системы UNIX, а ядро устанавливает

соответствие между общим набором файловых операций и операциями,

специфичными для каждого типа файловой системы.


Операции файловой Общие индексы Индекс файловой

системы системы версии V

----------------┐ -------┐ --------┐

Версия V │ open │ ------+- -+-------->│ │

│ close │ │ +------+ +-------+

│ read │ │ ----+- -+---┐ │ │

│ write │<---- │ +------+ │ +-------+

│ │<-----│---+- -+---│---->│ │

│ │ │ +------+ │ +-------+

│ │ │ │ │ │ │ │

│ │ │ +------+ │ │ │

+---------------+ │ │ │ │ │ │

Удаленная │ ropen │ │ +------+ │ L--------

система │ rclose │ │ │ │ │

│ rread │ │ │ │ │ Индекс удален-

│ rwrite │<------ │ │ │ ной системы

│ │ │ │ │ --------┐

│ │ │ │ │ │ │

│ │ │ │ │ +-------+

│ │ │ │ L---->│ │

+---------------+ │ │ +-------+

│ │ │ │ │ │

│ │ │ │ +-------+

│ │ │ │ │ │

│ │ │ │ +-------+

│ │ │ │ │ │

│ │ │ │ │ │

│ │ │ │ │ │

L---------------- L------- L--------


Рисунок 5.34. Индексы для файловых систем различных типов


Индекс выступает интерфейсом между абстрактной файловой сис-

темой и отдельной файловой системой. Общая копия индекса в памяти

содержит информацию, не зависящую от отдельной файловой системы,

а также указатель на частный индекс файловой системы, который уже

содержит информацию, специфичную для нее. Частный индекс файловой

системы содержит такую информацию, как права доступа и расположе-

ние блоков, а общий индекс содержит номер устройства, номер ин-

декса на диске, тип файла, размер, информацию о владельце и счет-

чик ссылок. Другая частная информация, описывающая отдельную

файловую систему, содержится в суперблоке и структуре каталогов.

На Рисунке 5.34 изображены таблица общих индексов в памяти и две

таблицы частных индексов отдельных файловых систем, одна для

структур файловой системы версии V, а другая для индекса удален-

ной (сетевой) системы. Предполагается, что последний индекс со-

держит достаточно информации для того, чтобы идентифицировать

файл, находящийся в удаленной системе. У файловой системы может

отсутствовать структура, подобная индексу; но исходный текст

программ отдельной файловой системы позволяет создать объектный

код, удовлетворяющий семантическим требованиям файловой системы

UNIX и назначающий свой "индекс", который соответствует общему

индексу, назначаемому ядром.

Файловая система каждого типа имеет некую структуру, в кото-

рой хранятся адреса функций, реализующих абстрактные действия.

Когда ядру нужно обратиться к файлу, оно вызывает косвенную функ-

цию в зависимости от типа файловой системы и абстрактного дейс-

твия (см. Рисунок 5.34). Примерами абстрактных действий являются:

открытие и закрытие файла, чтение и запись данных, возвращение

индекса для компоненты имени файла (подобно namei и iget), осво-

бождение индекса (подобно iput), коррекция индекса, проверка прав

доступа, установка атрибутов файла (прав доступа к нему), а также

монтирование и демонтирование файловых систем. В главе 13 будет

проиллюстрировано использование системных абстракций при рассмот-

рении распределенной файловой системы.


5.18 СОПРОВОЖДЕНИЕ ФАЙЛОВОЙ СИСТЕМЫ


Ядро поддерживает целостность системы в своей обычной работе.

Тем не менее, такие чрезвычайные обстоятельства, как отказ пита-

ния, могут привести к фатальному сбою системы, в результате кото-

рого содержимое системы утрачивает свою согласованность: боль-


шинство данных в файловой системе доступно для использования, но

некоторая несогласованность между ними имеет место. Команда fsck

проверяет согласованность данных и в случае необходимости вносит

в файловую систему исправления. Она обращается к файловой системе

через блочный или строковый интерфейс (глава 10) в обход традици-

онных методов доступа к файлам. В этом разделе рассматриваются

некоторые примеры противоречивости данных, которая обнаруживается

командой fsck.

Дисковый блок может принадлежать более чем одному индексу или

списку свободных блоков. Когда файловая система открывается в

первый раз, все дисковые блоки находятся в списке свободных бло-

ков. Когда дисковый блок выбирается для использования, ядро уда-

ляет его номер из списка свободных блоков и назначает блок индек-

су. Ядро не может переназначить дисковый блок другому индексу до

тех пор, пока блок не будет возвращен в список свободных блоков.

Таким образом, дисковый блок может либо находиться в списке сво-

бодных блоков, либо быть назначенным одному из индексов. Рассмот-

рим различные ситуации, могущие иметь место при освобождении яд-

ром дискового блока, принадлежавшего файлу, с возвращением номера

блока в суперблок, находящийся в памяти, и при выделении дисково-

го блока новому файлу. Если ядро записывало на диск индекс и бло-

ки нового файла, но перед внесением изменений в индекс прежнего

файла на диске произошел сбой, оба индекса будут адресовать к од-

ному и тому же номеру дискового блока. Подобным же образом, если

ядро переписывало на диск суперблок и его списки свободных ресур-

сов и перед переписью старого индекса случился сбой, дисковый

блок появится одновременно и в списке свободных блоков, и в ста-

ром индексе.

Если блок отсутствует как в списке свободных блоков, так и в

файле, файловая система является несогласованной, ибо, как уже

говорилось выше, все блоки обязаны где-нибудь присутствовать. Та-

кая ситуация могла бы произойти, если бы блок был удален из файла

и помещен в список свободных блоков в суперблоке. Если производи-

лась запись прежнего файла на диск и система дала сбой перед за-

писью суперблока, блок будет отсутствовать во всех списках, хра-

нящихся на диске.

Индекс может иметь счетчик связей с ненулевым значением при

том, что его номер отсутствует во всех каталогах файловой систе-

мы. Все файлы, за исключением каналов (непоименованных), должны

присутствовать в древовидной структуре файловой системы. Если

система дала сбой после создания канала или обычного файла, но

перед созданием соответствующей этому каналу или файлу точки вхо-

да в каталог, индекс будет иметь в поле счетчика связей установ-

ленное значение, пусть даже он явно не присутствует в файловой

системе. Еще одна проблема может возникнуть, если с помощью функ-

ции unlink была удалена связь каталога без проверки удаления из

каталога всех содержащихся в нем связей с отдельными файлами.

Если формат индекса неверен (например, если значение поля ти-

па файла не определено), значит где-то имеется ошибка. Это может

произойти, если администратор смонтировал файловую систему, кото-

рая отформатирована неправильно. Ядро обращается к тем дисковым

блокам, которые, как кажется ядру, содержат индексы, но в дейс-


твительности оказывается, что они содержат данные.

Если номер индекса присутствует в записи каталога, но сам

индекс свободен, файловая система является несогласованной, пос-

кольку номер индекса в записи каталога должен быть номером назна-

ченного индекса. Это могло бы произойти, если бы ядро, создавая

новый файл и записывая на диск новую точку входа в каталог, не

успела бы скопировать на диск индекс файла из-за сбоя. Также это

может случиться, если процесс, удаляя связь файла с каталогом,

запишет освободившийся индекс на диск, но не успеет откорректиро-

вать каталог из-за сбоя. Возникновение подобных ситуаций можно

предотвратить, копируя на диск результаты работы в надлежащем по-

рядке.

Если число свободных блоков или свободных индексов, записан-

ное в суперблоке, не совпадает с их количеством на диске, файло-

вая система так же является несогласованной. Итоговая информация

в суперблоке всегда должна соответствовать информации о текущем

состоянии файловой системы.


5.19 ВЫВОДЫ


Этой главой завершается первая часть книги, посвященная расс-

мотрению особенностей файловой системы. Глава познакомила пользо-

вателя с тремя таблицами, принадлежащими ядру: таблицей пользова-

тельских дескрипторов файла, системной таблицей файлов и таблицей

монтирования. В ней рассмотрены алгоритмы выполнения системных

функций, имеющих отношение к файловой системе, и взаимодействие

между этими функциями. Исследованы некоторые абстрактные свойства

файловой системы, позволяющие системе UNIX поддерживать файловые

системы различных типов. Наконец, описан механизм выполнения ко-

манды fsck, контролирующей целостность и согласованность данных в

файловой системе.


5.20 УПРАЖНЕНИЯ


1. Рассмотрим программу, приведенную на Рисунке 5.35. Какое

значение возвращает каждая операция read и что при этом со-

держится в буфере ? Опишите, что происходит в ядре во время

выполнения каждого вызова read.

2. Вновь вернемся к программе на Рисунке 5.35 и предположим,

что оператор

lseek(fd,9000L,0);

стоит перед первым обращением к функции read. Что ищет про-

цесс и что при этом происходит в ядре ?

3. Процесс может открыть файл для работы в режиме добавления

записей в конец файла, при этом имеется в виду, что каждая

операция записи располагает данные по адресу смещения, ука-

зывающего текущий конец файла. Таким образом, два процесса

могут открыть файл для работы в режиме добавления записей в

конец файла и вводить данные, не опасаясь затереть записи

друг другу. Что произойдет, если процесс откроет файл в ре-

жиме добавления в конец, а записывающую головку установит

на начало файла ?

4. Библиотека стандартных подпрограмм ввода-вывода повышает

эффективность выполнения пользователем операций чтения и

записи благодаря буферизации данных в библиотеке и сохране-

нию большого количества модулей обращения к операционной

системе, необходимых пользователю. Как бы вы реализовали

библиотечные функции fread и fwrite ? Что должны делать

библиотечные функции fopen и fclose ?


-------------------------------------------------------------┐

│ #include

│ main() │

│ { │

│ int fd; │

│ char buf[1024]; │

│ fd = creat("junk",0666); │

│ lseek(fd,2000L,2); /* ищется байт с номером 2000 */ │

│ write(fd,"hello",5); │

│ close(fd); │

│ │

│ fd = open("junk",O_RDONLY); │

│ read(fd,buf,1024); /* читает нули */ │

│ read(fd,buf,1024); /* считывает нечто, отличное от 0 */│

│ read(fd,buf,1024); │

│ } │

L-------------------------------------------------------------


Рисунок 5.35. Считывание нулей и конца файла


5. Если процесс читает данные из файла последовательно, ядро

запоминает значение блока, прочитанного с продвижением, в

индексе, хранящемся в памяти. Что произойдет, если несколь-

ко процессов будут одновременно вести последовательное счи-

тывание данных из одного и того же файла ?


----------------------------------------------------------┐

│ #include

│ main() │

│ { │

│ int fd; │

│ char buf[256]; │

│ │

│ fd = open("/etc/passwd",O_RDONLY); │

│ if (read(fd,buf,1024) < 0) │

│ printf("чтение завершается неудачно\n"); │

│ } │

L----------------------------------------------------------


Рисунок 5.36. Чтение большой порции данных в маленький буфер


6. Рассмотрим программу, приведенную на Рисунке 5.36. Что про-

изойдет в результате выполнения программы ? Обоснуйте от-

вет. Что произошло бы, если бы объявление массива buf было

вставлено между объявлениями двух других массивов размером

1024 элемента каждый ? Каким образом ядро устанавливает,

что прочитанная порция данных слишком велика для буфера ?

*7. В файловой системе BSD разрешается фрагментировать послед-

ний блок файла в соответствии со следующими правилами:

* Свободные фрагменты отслеживаются в структурах, подобных

суперблоку;

* Ядро не поддерживает пул ранее выделенных свободных фраг-

ментов, а разбивает на фрагменты в случае необходимости

свободный блок;

* Ядро может назначать фрагменты блока только для последне-

го блока в файле;

* Если блок разбит на несколько фрагментов, ядро может наз-

начить их различным файлам;

* Количество фрагментов в блоке не должно превышать величи-

ну, фиксированную для данной файловой системы;

* Ядро назначает фрагменты во время выполнения системной

функции write.


Разработайте алгоритм, присоединяющий к файлу фрагменты

блока. Какие изменения должны быть сделаны в индексе, чтобы

позволить использование фрагментов ? Какие преимущества с

системной точки зрения предоставляет использование фрагмен-

тов для тех файлов, которые используют блоки косвенной ад-

ресации ? Не выгоднее ли было бы назначать фрагменты во

время выполнения функции close вместо того, чтобы назначать

их при выполнении функции write ?

*8. Вернемся к обсуждению, начатому в главе 4 и касающемуся

расположения данных в индексе файла. Для того случая, когда

индекс имеет размер дискового блока, разработайте алгоритм,

по которому остаток данных файла переписывается в индексный

блок, если помещается туда. Сравните этот метод с методом,

предложенным для решения предыдущей проблемы.

*9. В версии V системы функция fcntl используется для реализа-

ции механизма захвата файла и записи и имеет следующий фор-

мат:

fcntl(fd,cmd,arg);

где fd - дескриптор файла, cmd - тип блокирующей операции,

а в arg указываются различные параметры, такие как тип бло-

кировки (записи или чтения) и смещения в байтах (см. прило-

жение). К блокирующим операциям относятся

* Проверка наличия блокировок, принадлежащих другим процес-

сам, с немедленным возвратом управления в случае обнару-

жения таких блокировок,

* Установка блокировки и приостанов до успешного заверше-

ния,

* Установка блокировки с немедленным возвратом управления в

случае неудачи.

Ядро автоматически снимает блокировки, установленные про-

цессом, при закрытии файла. Опишите работу алгоритма, реа-

лизующего захват файла и записи. Если блокировки являются

обязательными, другим процессам следует запретить доступ к

файлу. Какие изменения следует сделать в операциях чтения и

записи ?

*10. Если процесс приостановил свою работу в ожидании снятия с

файла блокировки, возникает опасность взаимной блокировки:

процесс A может заблокировать файл "one" и попытаться заб-

локировать файл "two", а процесс B может заблокировать файл

"two" и попытаться заблокировать файл "one". Оба процесса

перейдут в состояние, при котором они не смогут продолжить

свою работу. Расширьте алгоритм решения предыдущей проблемы

таким образом, чтобы ядро могло обнаруживать ситуации вза-

имной блокировки и прерывать выполнение системных функций.

Следует ли поручать обнаружение взаимных блокировок ядру ?

11. До существования специальной системной функции захвата фай-

ла пользователям приходилось прибегать к услугам параллель-

но действующих процессов для реализации механизма захвата

путем вызова системных функций, выполняющих элементарные

действия. Какие из системных функций, описанных в этой гла-

ве, могли бы использоваться ? Какие опасности подстерегают

при использовании этих методов ?

12. Ричи заявлял (см. [Ritchie 81]), что захвата файла недоста-

точно для того, чтобы предотвратить путаницу, вызываемую

такими программами, как редакторы, которые создают копию

файла при редактировании и переписывают первоначальный файл

по окончании работы. Объясните, что он имел в виду, и про-

комментируйте.

13. Рассмотрим еще один способ блокировки файлов, предотвращаю-

щий разрушительные последствия корректировки. Предположим,


что в индексе содержится новая установка прав доступа, поз-

воляющая только одному процессу в текущий момент открывать

файл для записи и нескольким процессам открывать файл для

чтения. Опишите реализацию этого способа.


-----------------------------------------------------------┐

│ main(argc,argv) │

│ int argc; │

│ char *argv[]; │


│ { │

│ if (argc != 2) │

│ { │

│ printf("введите: команда имя каталога\n"); │

│ exit(); │

│ } │

│ │

│ /* права доступа к каталогу: запись, чтение и ис- │

│ полнение разрешены для всех */ │

│ /* только суперпользователь может делать следую- │

│ щее */ │

│ if (mknod(argv[1],040777,0) == -1) │

│ printf("mknod завершилась неудачно\n"); │

│ } │

L-----------------------------------------------------------


Рисунок 5.37. Каталог, создание которого не завершено


*14. Рассмотрим программу (Рисунок 5.37), которая создает ката-

лог с неверным форматом (в каталоге отсутствуют записи с

именами "." и ".."). Попробуйте, находясь в этом каталоге,

выполнить несколько команд, таких как ls -l, ls -ld, или

cd. Что произойдет при этом ?

15. Напишите программу, которая выводит для файлов, имена кото-

рых указаны в качестве параметров, информацию о владельце,

типе файла, правах доступа и времени доступа. Если файл

(параметр) является каталогом, программа должна читать за-

писи из каталога и выводить вышеуказанную информацию для

всех файлов в каталоге.

16. Предположим, что у пользователя есть разрешение на чтение

из каталога, но нет разрешения на исполнение. Что произой-

дет, если каталог использовать в качестве параметра команды

ls, заданной с опцией "-i" ? Что будет, если указана опция

"-l" ? Поясните свои ответы. Ответьте на вопрос, сформули-

рованный для случая, когда есть разрешение на исполнение,

но нет разрешения на чтение из каталога.

17. Сравните права доступа, которые должны быть у процесса для

выполнения следующих действий, и прокомментируйте:

* Для создания нового файла требуется разрешение на запись

в каталог.

* Для "создания" существующего файла требуется разрешение

на запись в файл.

* Для удаления связи файла с каталогом требуется разрешение

на запись в каталог, а не в файл.

*18. Напишите программу, которая навещает все каталоги, начиная

с текущего. Как она должна управлять циклами в иерархии ка-

талогов ?

19. Выполните программу, приведенную на Рисунке 5.38, и объяс-

ните, что при этом происходит в ядре. (Намек: выполните ко-

манду pwd, когда программа закончится).

20. Напишите программу, которая заменяет корневой каталог

указанным каталогом, и исследуйте дерево каталогов, доступ-

ное для этой программы.

21. Почему процесс не может отменить предыдущий вызов функции

chroot ? Измените конкретную реализацию процесса таким об-

разом, чтобы он мог менять текущее значение корня на преды-

дущее. Какие у этой возможности преимущества и неудобства ?

22. Рассмотрим простой пример канала (Рисунок 5.19), когда

процесс записывает в канал строку "hello" и затем считывает


-----------------------------------------------------------┐

│ main(argc,argv) │

│ int argc; │

│ char *argv[]; │

│ { │

│ if (argc != 2) │

│ { │

│ printf("нужен 1 аргумент - имя каталога\n"); │

│ exit(); │

│ } │

│ │

│ if (chdir(argv[1]) == -1) │

│ printf("%s файл не является каталогом\n",argv[1]);│

│ } │

L-----------------------------------------------------------


Рисунок 5.38. Пример программы с использованием функции

chdir


ее. Что произошло бы, если бы массив для записи данных в

канал имел размер 1024 байта вместо 6 (а объем считываемых

за одну операцию данных оставался равным 6) ? Что произой-

дет, если порядок вызова функций read и write в программе

изменить, поменяв функции местами ?

23. Что произойдет при выполнении программы, иллюстрирующей ис-

пользование поименованных каналов (Рисунок 5.19), если

функция mknod обнаружит, что канал с таким именем уже су-

ществует ? Как этот момент реализуется ядром ? Что произош-

ло бы, если бы вместо подразумеваемых в тексте программы

одного считывающего и одного записывающего процессов связь

между собой через канал попытались установить несколько

считывающих и записывающих процессов ? Как в этом случае

гарантировалась бы связь одного считывающего процесса с од-

ним записывающим процессом ?

24. Открывая поименованный канал для чтения, процесс приоста-

навливается до тех пор, пока еще один процесс не откроет

канал для записи. Почему ? Не мог бы процесс успешно пройти

функцию open, продолжить работу до того момента, когда им

будет предпринята попытка чтения данных из канала, и приос-

тановиться при выполнении функции read ?


25. Как бы вы реализовали алгоритм выполнения системной функции

dup2 (из версии 7), вызываемой следующим образом:

dup2(oldfd,newfd);

где oldfd - файловый дескриптор, который дублируется деск-

риптором newfd ? Что произошло бы, если бы дескриптор newfd

уже принадлежал открытому файлу ?

*26. Какие последствия имело бы решение ядра позволить двум про-

цессам одновременно смонтировать одну и ту же файловую сис-

тему в двух точках монтирования ?

27. Предположим, что один процесс меняет свой текущий каталог

на каталог "/mnt/a/b/c", после чего другой процесс в ката-

логе "/mnt" монтирует файловую систему. Завершится ли функ-

ция mount успешно ? Что произойдет, если первый процесс вы-


полнит команду pwd ? Ядро не позволит функции mount успешно

завершиться, если значение счетчика ссылок в индексе ката-

лога "/mnt" превышает 1. Прокомментируйте этот момент.

28. При исполнении алгоритма пересечения точки монтирования по

имени ".." в маршруте поиска файла ядро проверяет выполне-

ние трех условий, связанных с точкой монтирования: что но-

мер обнаруженного индекса совпадает с номером корневого ин-

декса, что рабочий индекс является корнем файловой системы

и что имя компоненты маршрута поиска - "..". Почему необхо-

димо проверять выполнение всех трех условий ? Докажите, что

проверки любых двух условий недостаточно для того, чтобы

разрешить процессу пересечь точку монтирования.

29. Если пользователь монтирует файловую систему только для

чтения, ядро устанавливает соответствующий флаг в супербло-

ке. Как ядро может воспрепятствовать выполнению операций

записи в функциях write, creat, link, unlink, chown и

chmod ? Какого рода информацию записывают в файловую систе-

му все перечисленные функции ?

*30. Предположим, что один процесс пытается демонтировать файло-

вую систему, в то время как другой процесс пытается создать

в файловой системе новый файл. Только одна из функций

umount и creat выполнится успешно. Подробно рассмотрите

возникшую конкуренцию.

*31. Когда функция umount проверяет отсутствие в файловой систе-

ме активных файлов, возникает одна проблема, связанная с

тем, что корневой индекс файловой системы, назначаемый при

выполнении функции mount с помощью алгоритма iget, имеет

счетчик ссылок с положительным значением. Как функция

umount сможет убедиться в отсутствии активных файлов и

отчитаться перед корнем файловой системы ? Рассмотрите два

случая:

* функция umount освобождает корневой индекс по алгоритму

iput перед проверкой активных индексов. (Как функции вер-

нуть этот индекс обратно, если будут обнаружены активные

файлы ?)

* функция umount проверяет отсутствие активных файлов до

того, как освободить корневой индекс, и разрешая корнево-

му индексу оставаться активным. (Насколько активным может

быть корневой индекс ?)

32. Обратите внимание на то, что при выполнении команды ls -ld

количество связей с каталогом никогда не равно 1. Почему ?

33. Как работает команда mkdir (создать новый каталог) ? (На-

водящий вопрос: какие номера по завершении выполнения ко-

манды имеют индексы для файлов "." и ".." ?)

*34. Понятие "символические связи" имеет отношение к возможности

указания с помощью функции link связей между файлами, при-

надлежащими к различным файловым системам. С файлом симво-

лической связи ассоциирован указатель нового типа; содержи-

мым файла является имя пути поиска того файла, с которым он

связан. Опишите реализацию символических связей.

*35. Что произойдет, если процесс вызовет функцию

unlink(".");

Каким будет текущий каталог процесса ? Предполагается, что

процесс обладает правами суперпользователя.

36. Разработайте системную функцию, которая усекает существую-

щий файл до произвольных размеров, указанных в качестве ар-

гумента, и опишите ее работу. Реализуйте системную функцию,

которая позволяла бы пользователю удалять сегмент файла,

расположенный между двумя адресами, заданными в виде смеще-

ний, и сжимать файл. Напишите программу, которая не вызыва-

ла бы эти функции, но обладала бы теми же функциональными

возможностями.

37. Опишите все условия, при которых счетчик ссылок в индексе

может превышать значение 1.

38. Затрагивая тему абстрактных обращений к файловым системам,

ответьте на вопрос: следует ли файловой системе каждого ти-

программы, или же достаточно общей операции блокирования ? СТРУКТУРА ПРОЦЕССОВ


В главе 2 были сформулированы характеристики процессов. В

настоящей главе на более формальном уровне определяется понятие

"контекст процесса" и показывается, каким образом ядро идентифи-

цирует процесс и определяет его местонахождение. В разделе 6.1

описаны модель состояний процессов для системы UNIX и последова-

тельность возможных переходов из состояния в состояние. В ядре

находится таблица процессов, каждая запись которой описывает сос-

тояние одного из активных процессов в системе. В пространстве

процесса хранится дополнительная информация, используемая в уп-

равлении протеканием процесса. Запись в таблице процессов и

пространство процесса составляют в совокупности контекст процес-

са. Аспектом контекста процесса, наиболее явно отличающим данный

контекст от контекста другого процесса, без сомнения является со-

держимое адресного пространства процесса. В разделе 6.2 описыва-

ются принципы управления распределением памяти для процессов и

ядра, а также взаимодействие операционной системы с аппаратными

средствами при трансляции виртуальных адресов в физические. Раз-

дел 6.3 посвящен рассмотрению составных элементов контекста про-

цесса, а также описанию алгоритмов управления контекстом процес-

са. Раздел 6.4 демонстрирует, каким образом осуществляется

сохранение контекста процесса ядром в случае прерывания, вызова

системной функции или переключения контекста, а также каким обра-

зом возобновляется выполнение приостановленного процесса. В раз-

деле 6.5 приводятся различные алгоритмы, используемые в тех сис-

темных функциях, которые работают с адресным пространством

процесса и которые будут рассмотрены в следующей главе. И, нако-

нец, в разделе 6.6 рассматриваются алгоритмы приостанова и возоб-

новления выполнения процессов.


6.1 СОСТОЯНИЯ ПРОЦЕССА И ПЕРЕХОДЫ МЕЖДУ НИМИ


Как уже отмечалось в главе 2, время жизни процесса можно тео-

ретически разбить на несколько состояний, описывающих процесс.

Полный набор состояний процесса содержится в следующем перечне:

1. Процесс выполняется в режиме задачи.

2. Процесс выполняется в режиме ядра.

3. Процесс не выполняется, но готов к запуску под управлением

ядра.

4. Процесс приостановлен и находится в оперативной памяти.

5. Процесс готов к запуску, но программа подкачки (нулевой про-

цесс) должна еще загрузить процесс в оперативную память,

прежде чем он будет запущен под управлением ядра. Это состоя-

ние будет предметом обсуждения в главе 9 при рассмотрении

системы подкачки.

6. Процесс приостановлен и программа подкачки выгрузила его во

внешнюю память, чтобы в оперативной памяти освободить место

для других процессов.

7. Процесс возвращен из привилегированного режима (режима ядра)

в непривилегированный (режим задачи), ядро резервирует его и

переключает контекст на другой процесс. Об отличии этого сос-

тояния от состояния 3 (готовность к запуску) пойдет речь ни-

же.

8. Процесс вновь создан и находится в переходном состоянии; про-

цесс существует, но не готов к выполнению, хотя и не

приостановлен. Это состояние является начальным состоянием

всех процессов, кроме нулевого.

9. Процесс вызывает системную функцию exit и прекращает сущест-

вование. Однако, после него осталась запись, содержащая код

выхода, и некоторая хронометрическая статистика, собираемая

родительским процессом. Это состояние является последним сос-

тоянием процесса.

Рисунок 6.1 представляет собой полную диаграмму переходов

процесса из состояния в состояние. Рассмотрим с помощью модели

переходов типичное поведение процесса. Ситуации, которые будут

обсуждаться, несколько искусственны и процессы не всегда имеют

дело с ними, но эти ситуации вполне применимы для иллюстрации

различных переходов. Начальным состоянием модели является созда-

ние процесса родительским процессом с помощью системной функции

fork; из этого состояния процесс неминуемо переходит в состояние

готовности к запуску (3 или 5). Для простоты предположим, что

процесс перешел в состояние "готовности к запуску в памяти" (3).

Планировщик процессов в конечном счете выберет процесс для выпол-

нения и процесс перейдет в состояние "выполнения в режиме ядра",

где доиграет до конца роль, отведенную ему функцией fork.

После всего этого процесс может перейти в состояние "выполне-

ния в режиме задачи". По прохождении определенного периода време-


ни может произойти прерывание работы процессора по таймеру и про-

цесс снова перейдет в состояние "выполнения в режиме ядра". Как

только программа обработки прерывания закончит работу, ядру может

понадобиться подготовить к запуску другой процесс, поэтому первый

процесс перейдет в состояние "резервирования", уступив дорогу

второму процессу. Состояние "резервирования" в действительности

не отличается от состояния "готовности к запуску в памяти" (пунк-

тирная линия на рисунке, соединяющая между собой оба состояния,

подчеркивает их эквивалентность), но они выделяются в отдельные

состояния, чтобы подчеркнуть, что процесс, выполняющийся в режиме

ядра, может быть зарезервирован только в том случае, если он со-


Выполняется в

режиме задачи

--------┐

│ │

│ 1 │

Вызов функ- │ │

ции, преры- LT-------

вание │

Преры- ------┐ --------- │ │

вание, │ │ │ --------- L---┐ Возврат в

возврат│ │ │ │ Возврат │ режим задачи

из пре-│ │ │ │ │

рыва-│ v v │ Выполняет- │

--------┐ ния │ -------+┐ся в режи- -+------┐

│ │ L-->│ │ме ядра │ │

│ 9 │<-----------+ 2 +------------>│ 7 │

│ │ Выход │ │ Резервирует-│ │

L-------- LT------- ся L--------

Прекращение │ Зарезер-

существования │ │ вирован

│ │

----------------- L------┐

│ Приостанов Запуск │

v │

При---------┐ --+-----┐ Готов к

ос- │ │ Возобновление │ │ запуску

та- │ 4 +----------------------->│ 3 │ в памяти

нов-│ │ │ │

лен L---T---- LT-------

в па- │ │

мяти │ │ │ │ Достаточно

│ │ │ │ памяти

│ │ │ L---┐


│ Вы- Вы- │ │ │

│ грузка грузка │ │ │ Создан

│ │ │За- -+------┐

│ │ │груз-│ │ fork

│ │ │ка │ 8 │<-----

│ │ │ │ │

│ │ │ LT-------

│ │ │ │

│ │ │ │ Недоста-

│ │ │ ----- точно

│ │ │ │ памяти

│ │ │ │ (только система

│ │ │ │ подкачки)

v v │ v

--------┐ ----+---┐

│ │ Возобновление │ │

│ 6 +----------------------->│ 5 │

│ │ │ │

L-------- L--------

Приостановлен, Готов к запуску,

выгружен выгружен


Рисунок 6.1. Диаграмма переходов процесса из состояния в сос-

тояние


бирается вернуться в режим задачи. Следовательно, ядро может при

необходимости подкачивать процесс из состояния "резервирования".

При известных условиях планировщик выберет процесс для исполнения

и тот снова вернется в состояние "выполнения в режиме задачи".

Когда процесс выполняет вызов системной функции, он из состо-

яния "выполнения в режиме задачи" переходит в состояние "выполне-

ния в режиме ядра". Предположим, что системной функции требуется

ввод-вывод с диска и поэтому процесс вынужден дожидаться заверше-

ния ввода-вывода. Он переходит в состояние "приостанова в памя-

ти", в котором будет находиться до тех пор, пока не получит изве-

щения об окончании ввода-вывода. Когда ввод-вывод завершится,

произойдет аппаратное прерывание работы центрального процессора и

программа обработки прерывания возобновит выполнение процесса, в

результате чего он перейдет в состояние "готовности к запуску в

памяти".

Предположим, что система выполняет множество процессов, кото-

рые одновременно никак не могут поместиться в оперативной памяти,

и программа подкачки (нулевой процесс) выгружает один процесс,

чтобы освободить место для другого процесса, находящегося в сос-

тоянии "готов к запуску, но выгружен". Первый процесс, выгружен-

ный из оперативной памяти, переходит в то же состояние. Когда

программа подкачки выбирает наиболее подходящий процесс для заг-

рузки в оперативную память, этот процесс переходит в состояние

"готовности к запуску в памяти". Планировщик выбирает процесс для

исполнения и он переходит в состояние "выполнения в режиме ядра".

Когда процесс завершается, он исполняет системную функцию exit,

последовательно переходя в состояния "выполнения в режиме ядра"

и, наконец, в состояние "прекращения существования".

Процесс может управлять некоторыми из переходов на уровне за-

дачи. Во-первых, один процесс может создать другой процесс. Тем

не менее, в какое из состояний процесс перейдет после создания

(т.е. в состояние "готов к выполнению, находясь в памяти" или в

состояние "готов к выполнению, но выгружен") зависит уже от ядра.

Процессу эти состояния не подконтрольны. Во-вторых, процесс может

обратиться к различным системным функциям, чтобы перейти из сос-

тояния "выполнения в режиме задачи" в состояние "выполнения в ре-

жиме ядра", а также перейти в режим ядра по своей собственной во-

ле. Тем не менее, момент возвращения из режима ядра от процесса

уже не зависит; в результате каких-то событий он может никогда не

вернуться из этого режима и из него перейдет в состояние "прекра-

щения существования" (см. раздел 7.2, где говорится о сигналах).

Наконец, процесс может завершиться с помощью функции exit по сво-

ей собственной воле, но как указывалось ранее, внешние события

могут потребовать завершения процесса без явного обращения к

функции exit. Все остальные переходы относятся к жестко закреп-

ленной части модели, закодированной в ядре, и являются результа-

том определенных событий, реагируя на них в соответствии с прави-

лами, сформулированными в этой и последующих главах. Некоторые из

правил уже упоминались: например, то, что процесс может выгрузить

другой процесс, выполняющийся в ядре.

Две принадлежащие ядру структуры данных описывают процесс:

запись в таблице процессов и пространство процесса. Таблица про-

цессов содержит поля, которые должны быть всегда доступны ядру, а

пространство процесса - поля, необходимость в которых возникает

только у выполняющегося процесса. Поэтому ядро выделяет место для

пространства процесса только при создании процесса: в нем нет не-

обходимости, если записи в таблице процессов не соответствует

конкретный процесс.

Запись в таблице процессов состоит из следующих полей:

* Поле состояния, которое идентифицирует состояние процесса.

* Поля, используемые ядром при размещении процесса и его прост-

ранства в основной или внешней памяти. Ядро использует инфор-

мацию этих полей для переключения контекста на процесс, когда

процесс переходит из состояния "готов к выполнению, находясь

в памяти" в состояние "выполнения в режиме ядра" или из сос-

тояния "резервирования" в состояние "выполнения в режиме за-

дачи". Кроме того, ядро использует эту информацию при пере-

качки процессов из и в оперативную память (между двумя состо-

яниями "в памяти" и двумя состояниями "выгружен"). Запись в

таблице процессов содержит также поле, описывающее размер

процесса и позволяющее ядру планировать выделение пространс-

тва для процесса.

* Несколько пользовательских идентификаторов (UID), устанавли-

вающих различные привилегии процесса. Поля UID, например,

описывают совокупность процессов, могущих обмениваться сигна-

лами (см. следующую главу).


* Идентификаторы процесса (PID), указывающие взаимосвязь между

процессами. Значения полей PID задаются при переходе процесса

в состояние "создан" во время выполнения функции fork.

* Дескриптор события (устанавливается тогда, когда процесс при-

остановлен). В данной главе будет рассмотрено использование

дескриптора события в алгоритмах функций sleep и wakeup.

* Параметры планирования, позволяющие ядру устанавливать поря-

док перехода процессов из состояния "выполнения в режиме яд-

ра" в состояние "выполнения в режиме задачи".

* Поле сигналов, в котором перечисляются сигналы, посланные

процессу, но еще не обработанные (раздел 7.2).

* Различные таймеры, описывающие время выполнения процесса и

использование ресурсов ядра и позволяющие осуществлять слеже-

ние за выполнением и вычислять приоритет планирования процес-

са. Одно из полей является таймером, который устанавливает

пользователь и который необходим для посылки процессу сигнала

тревоги (раздел 8.3).

Пространство процесса содержит поля, дополнительно характери-

зующие состояния процесса. В предыдущих главах были рассмотрены

последние семь из приводимых ниже полей пространства процесса,

которые мы для полноты вновь кратко перечислим:

* Указатель на таблицу процессов, который идентифицирует за-

пись, соответствующую процессу.

* Пользовательские идентификаторы, устанавливающие различные

привилегии процесса, в частности, права доступа к файлу (см.

раздел 7.6).

* Поля таймеров, хранящие время выполнения процесса (и его по-

томков) в режиме задачи и в режиме ядра.

* Вектор, описывающий реакцию процесса на сигналы.

* Поле операторского терминала, идентифицирующее "регистрацион-

ный терминал", который связан с процессом.

* Поле ошибок, в которое записываются ошибки, имевшие место при

выполнении системной функции.

* Поле возвращенного значения, хранящее результат выполнения

системной функции.

* Параметры ввода-вывода: объем передаваемых данных, адрес ис-

точника (или приемника) данных в пространстве задачи, смеще-

ния в файле (которыми пользуются операции ввода-вывода) и

т.д.

* Имена текущего каталога и текущего корня, описывающие файло-

вую систему, в которой выполняется процесс.

* Таблица пользовательских дескрипторов файла, которая описыва-

ет файлы, открытые процессом.

* Поля границ, накладывающие ограничения на размерные характе-

ристики процесса и на размер файла, в который процесс может

вести запись.

* Поле прав доступа, хранящее двоичную маску установок прав

доступа к файлам, которые создаются процессом.

Пространство состояний процесса и переходов между ними расс-

матривалось в данном разделе на логическом уровне. Каждое состоя-

ние имеет также физические характеристики, управляемые ядром, в

частности, виртуальное адресное пространство процесса. Следующий