| } |
+------------------------------------------------------------+
Рисунок 7.34. Пример программы, в которой процесс-родитель и
процесс-потомок не разделяют доступ к файлу

* ядро не заменяет функцию обработки сигналов до тех пор, пока пользо-
ватель явно не потребует этого;

225

* ядро заставляет процесс игнорировать сигналы до тех пор, пока пользо-
ватель не обратится к функции signal вновь.
9. Переработайте алгоритм обработки сигналов так, чтобы ядро автоматически
перенастраивало процесс на игнорирование всех последующих поступлений
сигналов по возвращении из функции, обрабатывающей их. Каким образом
ядро может узнать о завершении функции обработки сигналов, выполняющей-
ся в режиме задачи ? Такого рода перенастройка приблизила бы нас к
трактовке сигналов в системе BSD.
*10. Если процесс получает сигнал, находясь в состоянии приостанова во время
выполнения системной функции с допускающим прерывания приоритетом, он
выходит из функции по алгоритму longjump. Ядро производит необходимые
установки для запуска функции обработки сигнала; когда процесс выйдет
из функции обработки сигнала, в версии V это будет выглядеть так, слов-
но он вернулся из системной функции с признаком ошибки (как бы прервав
свое выполнение). В системе BSD системная функция в этом случае автома-
тически перезапускается. Каким образом можно реализовать этот момент в
нашей системе?

+------------------------------------------------------------+
| #include |
| main() |
| { |
| extern catcher(); |
| |
| signal(SIGCLD,catcher); |
| if (fork() == 0) |
| exit(); |
| /* пауза до момента получения сигнала */ |
| pause(); |
| } |
| |
| catcher() |
| { |
| printf("процесс-родитель получил сигнал\n"); |
| signal(SIGCLD,catcher); |
| } |
+------------------------------------------------------------+

Рисунок 7.35. Программа, в которой процесс принимает сигналы
типа "гибель потомка"

11. В традиционной реализации команды mkdir для создания новой вершины в
дереве каталогов используется системная функция mknod, после чего дваж-
ды вызывается системная функция link, привязывающая точки входа в ката-
лог с именами "." и ".." к новой вершине и к ее родительскому каталогу.
Без этих трех операций каталог не будет иметь надлежащий формат. Что
произойдет, если во время исполнения команды mkdir процесс получит сиг-
нал ? Что если при этом будет получен сигнал SIGKILL, который процесс
не распознает ? Эту же проблему рассмотрите применительно к реализации
системной функции mkdir.
12. Процесс проверяет наличие сигналов в моменты перехода в состояние при-
останова и выхода из него (если в состоянии приостанова процесс нахо-
дился с приоритетом, допускающим прерывания), а также в момент перехода
в режим задачи из режима ядра по завершении исполнения системной функ-
ции или после обработки прерывания. Почему процесс не проверяет наличие
сигналов в момент обращения к системной функции ?
*13. Предположим, что после исполнения системной функции процесс готовится к
возвращению в режим задачи и не обнаруживает ни одного необработанного
сигнала. Сразу после этого ядро обрабатывает прерывание и посылает про-

226

цессу сигнал. (Например, пользователем была нажата клавиша "break".)
Что делает процесс после того, как ядро завершает обработку прерывания?
*14. Если процессу одновременно посылается несколько сигналов, ядро обраба-
тывает их в том порядке, в каком они перечислены в описании. Существуют
три способа реагирования на получение сигнала - прием сигналов, завер-
шение выполнения со сбросом на внешний носитель (дампированием) образа
процесса в памяти и завершение выполнения без дампирования. Можно ли
указать наилучший порядок обработки одновременно поступающих сигналов ?
Например, если процесс получает сигнал о выходе (вызывающий дампирова-
ние образа процесса в памяти) и сигнал о прерывании (выход без дампиро-
вания), то какой из этих сигналов имело бы смысл обработать первым ?
15. Запомните новую системную функцию newpgrp(pid,ngrp);
которая включает процесс с идентификатором pid в группу процессов с но-
мером ngrp (устанавливает для процесса новую группу). Подумайте, для
каких целей она может использоваться и какие опасности таит в себе ее
вызов.
16. Прокомментируйте следующее утверждение: по алгоритму wait процесс может
приостановиться до наступления какого-либо события и это не отразилось
бы на работе всей системы.
17. Рассмотрим новую системную функцию
nowait(pid);
где pid - идентификатор процесса, являющегося потомком того процесса,
который вызывает функцию. Вызывая функцию, процесс тем самым сообщает
ядру о том, что он не собирается дожидаться завершения выполнения свое-
го потомка, поэтому ядро может по окончании существования потомка сразу
же очистить занимаемое им место в таблице процессов. Каким образом это
реализуется на практике ? Оцените достоинства новой функции и сравните
ее использование с использованием сигналов типа "гибель потомка".
18. Загрузчик модулей на Си автоматически подключает к основному модулю на-
чальную процедуру (startup), которая вызывает функцию main, принадлежа-
щую программе пользователя. Если в пользовательской программе отсутст-
вует вызов функции exit, процедура startup сама вызывает эту функцию
при выходе из функции main. Что произошло бы в том случае, если бы и в
процедуре startup отсутствовал вызов функции exit (из-за ошибки загруз-
чика) ?
19. Какую информацию получит процесс, выполняющий функцию wait, если его
потомок запустит функцию exit без параметра ? Имеется в виду, что про-
цесс-потомок вызовет функцию в формате exit() вместо exit(n). Если
программист постоянно использует вызов функции exit без параметра, то
насколько предсказуемо значение, ожидаемое функцией wait ? Докажите
свой ответ.
20. Объясните, что произойдет, если процесс, исполняющий программу на Ри-
сунке 7.36 запустит с помощью функции exec самого себя. Как в таком
случае ядро сможет избежать возникновения тупиковых ситуаций, связанных
с блокировкой индексов ?
+----------------------------------+
| main(argc,argv) |
| int argc; |
| char *argv[]; |
| { |
| execl(argv[0],argv[0],0); |
| } |
+----------------------------------+
Рисунок 7.36

21. По условию первым аргументом функции exec является имя (последняя ком-
понента имени пути поиска) исполняемого процессом файла. Что произойдет
в результате выполнения программы, приведенной на Рисунке 7.37 ? Каков
будет эффект, если в качестве файла "a.out" выступит загрузочный мо-

227

дуль, полученный в результате трансляции программы, приведенной на Ри-
сунке 7.36 ?
22. Предположим, что в языке Си поддерживается новый тип данных "read-only"
(только для чтения), причем процесс, пытающийся записать информацию в
поле с этим типом, получает отказ системы защиты. Опишите реализацию
этого момента. (Намек: сравните это понятие с понятием "разделяемая об-
ласть команд".) В какие из алгоритмов ядра потребуется внести изменения
? Какие еще объекты могут быть реализованы аналогичным с областью обра-
зом ?
23. Какие изменения имеют место в алгоритмах open, chmod, unlink и unmount
при работе с файлами, для которых установлен режим "sticky-bit" ? Какие
действия, например, следует предпринять в отношении такого файла ядру,
когда с файлом разрывается связь ?
24. Суперпользователь является единственным пользователем, имеющим право на
запись в файл паролей "/etc/passwd", благодаря чему содержимое файла
предохраняется от умышленной или случайной порчи. Программа passwd дает
пользователям возможность изменять свой собственный пароль, защищая от
изменений чужие записи. Каким образом она работает ?

+-----------------------------------------------------+
| main() |
| { |
| if (fork() == 0) |
| { |
| execl("a.out",0); |
| printf("неудачное завершение функции exec\n");|
| } |
| } |
+-----------------------------------------------------+
Рисунок 7.37


*25. Поясните, какая угроза безопасности хранения данных возникает, если
setuid-программа не защищена от записи.
26. Выполните следующую последовательность команд, в которой "a.
out" - имя исполняемого файла:

+-----------------------------------------------------+
| main() |
| { |
| char *endpt; |
| char *sbrk(); |
| int brk(); |
| |
| endpt = sbrk(0); |
| printf("endpt = %ud после sbrk\n", (int) endpt); |
| |
| while (endpt--) |
| { |
| if (brk(endpt) == -1) |
| { |
| printf("brk с параметром %ud завершилась |
| неудачно\n",endpt); |
| exit(); |
| } |
| } |
| } |
+-----------------------------------------------------+
Рисунок 7.38

228

chmod 4777 a.out
chown root a.out
Команда chmod "включает" бит setuid (4 в 4777); пользователь "root"
традиционно является суперпользователем. Может ли в результате выполне-
ния этой последовательности произойти нарушение защиты информации ?
27. Что произойдет в процессе выполнения программы, представленной на Ри-
сунке 7.38 ? Поясните свой ответ.
28. Библиотечная подпрограмма malloc увеличивает область данных процесса с
помощью функции brk, а подпрограмма free освобождает память, выделенную
подпрограммой malloc. Синтаксис вызова подпрограмм:

ptr = malloc(size);
free(ptr);

где size - целое число без знака, обозначающее количество выделяемых
байт памяти, а ptr - символьная ссылка на вновь выделенное пространст-
во. Прежде чем появиться в качестве параметра в вызове подпрограммы
free, указатель ptr должен быть возвращен подпрограммой malloc. Выпол-
ните эти подпрограммы.
29. Что произойдет в процессе выполнения программы, представленной на Ри-
сунке 7.39 ? Сравните результаты выполнения этой программы с результа-
тами, предусмотренными в системном описании.

+-----------------------------------------------------+
| main() |
| { |
| int i; |
| char *cp; |
| extern char *sbrk(); |
| |
| cp = sbrk(10); |
| for (i = 0; i < 10; i++) |
| *cp++ = 'a' + i; |
| sbrk(-10); |
| cp = sbrk(10); |
| for (i = 0; i < 10; i++) |
| printf("char %d = '%c'\n",i,*cp++); |
| } |
+-----------------------------------------------------+

Рисунок 7.39. Пример программы, использующей подпрограмму sbrk

30. Каким образом командный процессор shell узнает о том, что файл исполня-
емый, когда для выполнения команды создает новый процесс ? Если файл
исполняемый, то как узнать, создан ли он в результате трансляции исход-
ной программы или же представляет собой набор команд языка shell ? В
каком порядке следует выполнять проверку указанных условий ?
31. В командном языке shell символы ">>" используются для направления выво-
да данных в файл с указанной спецификацией, например, команда: run >>
outfile открывает файл с именем "outfile" (а в случае отсутствия файла
с таким именем создает его) и записывает в него данные. Напишите прог-
рамму, в которой используется эта команда.
32. Процессор командного языка shell проверяет код, возвращаемый функцией
exit, воспринимая нулевое значение как "истину", а любое другое значе-
ние как "ложь" (обратите внимание на несогласованность с языком Си).
Предположим, что файл, исполняющий программу на Рисунке 7.40, имеет имя
"truth". Поясните, что произойдет, когда shell будет исполнять следую-
щий набор команд:
while truth

229

+------------------+
| main() |
| { |
| exit(0); |
| } |
+------------------+

Рисунок 7.40

do
truth &
done
33. Вопрос по Рисунку 7.29: В связи с чем возникает необходимость в созда-
нии процессов для конвейерной обработки двухкомпонентной команды в ука-
занном порядке ?
34. Напишите более общую программу работы основного цикла процессора shell
в части обработки каналов. Имеется в виду, что программа должна уметь
обрабатывать случайное число каналов, указанных в командной строке.
35. Переменная среды PATH описывает порядок, в котором shell'у следует
просматривать каталоги в поисках исполняемых файлов. В библиотечных
функциях execlp и execvp перечисленные в PATH каталоги присоединяются к
именам файлов, кроме тех, которые начинаются с символа "/". Выполните
эти функции.
*36. Для того, чтобы shell в поисках исполняемых файлов не обращался к теку-
щему каталогу, суперпользователь должен задать переменную среды PATH.
Какая угроза безопасности хранения данных может возникнуть, если shell
попытается исполнить файлы из текущего каталога ?
37. Каким образом shell обрабатывает команду cd (создать каталог) ? Какие
действия предпринимает shell в процессе обработки следующей командной
строки: cd pathname & ?
38. Когда пользователь нажимает на клавиатуре терминала клавиши "delete"
или "break", всем процессам, входящим в группу регистрационного
shell'а, терминальный драйвер посылает сигнал о прерывании. Пользова-
тель может иметь намерение остановить все процессы, порожденные
shell'ом, без выхода из системы. Какие усовершенствования в связи с
этим следует произвести в теле основного цикла программы shell (Рисунок
7.28) ?
39. С помощью команды nohup command_line
пользователь может отменить действие сигналов о "зависании" и о завер-
шении (quit) в отношении процессов, реализующих командную строку
(command_line). Как эта команда будет обрабатываться в основном цикле
программы shell ?
40. Рассмотрим набор команд языка shell:
nroff -mm bigfile1 > big1out &
nroff -mm bigfile2 > big2out
и вновь обратимся к основному циклу программы shell (Рисунок 7.28). Что
произойдет, если выполнение первой команды nroff завершится раньше вто-
рой ? Какие изменения следует внести в основной цикл программы shell на
этот случай ?
41. Часто во время выполнения из shell'а непротестированных программ появ-
ляется сообщение об ошибке следующего вида: "Bus error - core dumped"
(Ошибка в магистрали - содержимое памяти сброшено на внешний носитель).
Очевидно, что в программе выполняются какие-то недопустимые действия;
откуда shell узнает о том, что ему нужно вывести сообщение об ошибке ?
42. Процессом 1 в системе может выступать только процесс init. Тем не ме-
нее, запустив процесс init, администратор системы может тем самым изме-
нить состояние системы. Например, при загрузке система может войти в
однопользовательский режим, означающий, что в системе активен только
консольный терминал. Для того, чтобы перевести процесс init в состояние

230

2 (многопользовательский режим), администратор системы вводит с консоли
команду
init 2 .
Консольный shell порождает свое ответвление и запускает init. Что имело
бы место в системе в том случае, если бы активен был только один про-
цесс init ?
43. Формат записей в файле "/etc/inittab" допускает задание действия, свя-
занного с каждым порождаемым процессом. Например, с getty-процессом
связано действие "respawn" (возрождение), означающее, что процесс init
должен возрождать getty-процесс, если последний прекращает существова-
ние. На практике, когда пользователь выходит из системы процесс init
порождает новый getty-процесс, чтобы другой пользователь мог получить
доступ к временно бездействующей терминальной линии. Каким образом это
делает процесс init ?
44. Некоторые из алгоритмов ядра прибегают к просмотру таблицы процессов.
Время поиска данных можно сократить, если использовать указатели на:
родителя процесса, любого из потомков, другой процесс, имеющий того же
родителя. Процесс обнаруживает всех своих потомков, следуя сначала за
указателем на любого из потомков, а затем используя указатели на другие
процессы, имеющие того же родителя (циклы недопустимы). Какие из алго-
ритмов выиграют от этого ? Какие из алгоритмов нужно оставить без изме-






























231