Литмир - Электронная Библиотека
Содержание  
A
A

  }

  /* процесс init не дожидается завершения потомка */

  /* возврат в цикл while */

 }

 while ((id = wait((int*) 0)) != -1) {

  /* проверка существования потомка; если потомок прекратил существование, рассматривается возможность его перезапуска, в противном случае, основной процесс просто продолжает работу */

 }

}

Рисунок 7.31. Алгоритм выполнения процесса init

Формат: идентификатор, состояние, действие, спецификация процесса

Поля разделены между собой двоеточиями

Комментарии в конце строки начинаются с символа '#

'

 co::respawn:/etc/getty console console #Консоль в машзале

 46:2:respawn:/etc/getty -t 60 tty46 4800H #комментарии

Рисунок 7.32. Фрагмент файла inittab

7.10 ВЫВОДЫ

В данной главе были рассмотрены системные функции, предназначенные для работы с контекстом процесса и для управления выполнением процесса. Системная функция fork создает новый процесс, копируя для него содержимое всех областей, подключенных к родительскому процессу. Особенность реализации функции fork состоит в том, что она выполняет инициализацию сохраненного регистрового контекста порожденного процесса, таким образом этот процесс начинает выполняться, не дожидаясь завершения функции, и уже в теле функции начинает осознавать свою предназначение как потомка. Все процессы завершают свое выполнение вызовом функции exit, которая отсоединяет области процесса и посылает его родителю сигнал „гибель потомка“. Процесс-родитель может совместить момент продолжения своего выполнения с моментом завершения процесса-потомка, используя системную функцию wait. Системная функция exec дает процессу возможность запускать на выполнение другие программы, накладывая содержимое исполняемого файла на свое адресное пространство. Ядро отсоединяет области, ранее занимаемые процессом, и назначает процессу новые области в соответствии с потребностями исполняемого файла. Совместное использование областей команд и наличие режима „sticky-bit“ дают возможность более рационально использовать память и экономить время, затрачиваемое на подготовку к запуску программ. Простым пользователям предоставляется возможность получать привилегии других пользователей, даже суперпользователя, благодаря обращению к услугам системной функции setuid и setuid-программ. С помощью функции brk процесс может изменять размер своей области данных. Функция signal дает процессам возможность управлять своей реакцией на поступающие сигналы. При получении сигнала производится обращение к специальной функции обработки сигнала с внесением соответствующих изменений в стек задачи и в сохраненный регистровый контекст задачи. Процессы могут сами посылать сигналы, используя системную функцию kill, они могут также контролировать получение сигналов, предназначенных группе процессов, прибегая к услугам функции setpgrp.

Командный процессор shell и процесс начальной загрузки init используют стандартные обращения к системным функциям, производя набор операций, в других системах обычно выполняемых ядром. Shell интерпретирует команды пользователя, переназначает стандартные файлы ввода-вывода данных и выдачи ошибок, порождает процессы, организует каналы между порожденными процессами, синхронизирует свое выполнение с этими процессами и формирует коды, возвращаемые командами. Процесс init тоже порождает различные процессы, в частности, управляющие работой пользователя за терминалом. Когда такой процесс завершается, init может породить для выполнения той же самой функции еще один процесс, если это вытекает из информации файла „/etc/inittab“.

7.11 УПРАЖНЕНИЯ

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");

81
{"b":"96903","o":1}