Командный процессор shell и процесс начальной загрузки init используют стандартные обращения к системным функциям, производя набор операций, в других системах обычно выполняемых ядром. Shell интерпретирует команды пользователя, переназначает стандартные файлы ввода-вывода данных и выдачи ошибок, порождает процессы, организует каналы между порожденными процессами, синхронизирует свое выполнение с этими процессами и формирует коды, возвращаемые командами. Процесс init тоже порождает различные процессы, в частности, управляющие работой пользователя за терминалом. Когда такой процесс завершается, init может породить для выполнения той же самой функции еще один процесс, если это вытекает из информации файла „/etc/inittab“.
1. Запустите с терминала программу, приведенную на Рисунке 7.33. Переадресуйте стандартный вывод данных в файл и сравните результаты между собой.
main() {
printf("hello\n“);
if (fork() == 0) printf("world\n“);
}
Рисунок 7.33. Пример модуля, содержащего вызов функции fork и обращение к стандартному выводу
2. Разберитесь в механизме работы программы, приведенной на Рисунке 7.34, и сравните ее результаты с результатами программы на Рисунке 7.4.
#include ‹fcntl.h›
int fdrd, fdwt;
char c;
main(argc, argv)
int argc; char *argv[];
{
if (argc != 3) exit(1);
fork();
if ((fdrd = open(argv[1], O_RDONLY)) == -1) exit(1);
if (((fdwt = creat(argv[2], 0666)) == -1) && ((fdwt = open(argv[2], O_WRONLY)) == -1)) exit(1);
rdwrt();
}
rdwrt() {
for (;;) {
if (read(fdrd, &c, 1) != 1) return;
write(fdwt, &c, 1);
}
}
Рисунок 7.34. Пример программы, в которой процесс-родитель и процесс-потомок не разделяют доступ к файлу
3. Еще раз обратимся к программе, приведенной на Рисунке 7.5 и показывающей, как два процесса обмениваются сообщениями, используя спаренные каналы. Что произойдет, если они попытаются вести обмен сообщениями, используя один канал?
4. Возможна ли потеря информации в случае, когда процесс получает несколько сигналов прежде чем ему предоставляется возможность отреагировать на них надлежащим образом? (Рассмотрите случай, когда процесс подсчитывает количество полученных сигналов о прерывании.) Есть ли необходимость в решении этой проблемы?
5. Опишите механизм работы системной функции kill.
6. Процесс в программе на Рисунке 7.35 принимает сигналы типа „гибель потомка“ и устанавливает функцию обработки сигналов в исходное состояние. Что происходит при выполнении программы?
#include ‹signal.h›
main() {
extern catcher();
signal(SIGCLD, catcher);
if (fork() == 0) exit();
/* пауза до момента получения сигнала */
pause();
}
catcher() {
printf("процесс-родитель получил сигнал\n");
signal(SIGCLD, catcher);
}
Рисунок 7.35. Программа, в которой процесс принимает сигналы типа „гибель потомка“
7. Когда процесс получает сигналы определенного типа и не обрабатывает их, ядро дампирует образ процесса в том виде, который был у него в момент получения сигнала. Ядро создает в текущем каталоге процесса файл с именем „core“ и копирует в него пространство процесса, области команд, данных и стека. Впоследствии пользователь может тщательно изучить дамп образа процесса с помощью стандартных средств отладки. Опишите алгоритм, которому на Ваш взгляд должно следовать ядро в процессе создания файла „core“. Что нужно предпринять в том случае, если в текущем каталоге файл с таким именем уже существует? Как должно вести себя ядро, когда в одном и том же каталоге дампируют свои образы сразу несколько процессов?
8. Еще раз обратимся к программе (Рисунок 7.12), описывающей, как один процесс забрасывает другой процесс сигналами, которые принимаются их адресатом. Подумайте, что произошло бы в том случае, если бы алгоритм обработки сигналов был переработан в любом из следующих направлений:
• ядро не заменяет функцию обработки сигналов до тех пор, пока пользователь явно не потребует этого;
• ядро заставляет процесс игнорировать сигналы до тех пор, пока пользователь не обратится к функции signal вновь.
9. Переработайте алгоритм обработки сигналов так, чтобы ядро автоматически перенастраивало процесс на игнорирование всех последующих поступлений сигналов по возвращении из функции, обрабатывающей их. Каким образом ядро может узнать о завершении функции обработки сигналов, выполняющейся в режиме задачи? Такого рода перенастройка приблизила бы нас к трактовке сигналов в системе BSD.
Читать дальше