35: struct childProgram {
36: pid_t pid; /* 0 на выходе */
37: char ** argv; /* имя программы с аргументами * /
38: int numRedirections; /* элементы в массиве переадресации */
39: struct redirectionSpecifier * redirections; /* переадресации ввода-вывода */
40: glob_t globResult; /* результат универсализации параметров */
41: int freeGlob; /* должен ли освобождаться globResult? */
42: int isStopped; /* выполняется ли программа в данный момент?*/
43: };
Мы уже различаем работающие и завершенные дочерние программы с помощью элемента pid
структуры struct childProgram
, равного нулю в случае завершения дочерней программы, а в противном случае содержащего действительный идентификатор процесса. Новый элемент, isStopped
, не равен нулю, если процесс был остановлен, в ином же случае он равен нулю. Обратите внимание, что его значение неважно, если pid
равен нулю.
Аналогичные изменения потребуется внести и в struct job
. Ранее эта структура отслеживала определенное количество программ в задании и количество выполняющихся процессов. Ее новый элемент, stoppedProgs
, записывает количество процессов задания, остановленных в данный момент. Он может быть вычислен на основе элементов isStopped
дочерних программ, содержащихся в задании, но лучше отслеживать его отдельно. После этого изменения получаем окончательную форму структуры struct job
.
45: struct job {
46: int jobld; /* номер задания */
47: int numProgs; /* количество программ в задании */
48: int runningProgs; /* количество выполняющихся программ */
49: char * text; /* имя задания */
50: char * cmdBuf; /* буфер различных argv */
51: pid_t pgrp; /* идентификатор группы процессов задания */
52: struct childProgram* progs; /* массив программ в задании */
53: struct job * next; /* для слежения за фоновыми программами */
54: int stopped Progs; /* количество активных, однако остановленных программ */
55: };
Как и предыдущие версии ladsh
, код ladsh4.с
игнорирует SIGTTOU
. Это делается, чтобы позволить использовать tcsetpgrp()
даже тогда, когда оболочка не является процессом переднего плана. Однако поскольку оболочка уже будет поддерживать правильное управление заданиями, дочерним процессам не следует игнорировать сигнал. Как только новый процесс разветвляется с помощью runCommand()
, он устанавливает обработчик для SIGTTOU
в SIG_DFL
. Это позволяет драйверу терминала приостановить фоновые процессы, пытающиеся выполнить запись в терминал или провести с ним еще какие-то действия. Ниже приведен код, который начинается с создания дочернего процесса, где сбрасывается SIGTTOU
и выполняется дополнительная работа по синхронизации.
514: pipe(controlfds);
515:
516: if (!(newJob.progs[i].pid = fork())) {
517: signal(SIGTTOU, SIG_DFL);
518:
519: close(controlfds[1]);
520: /* это чтение вернет 0, когда закроется записывающая сторона*/
521: read(controlfds[0], &len, 1);
522: close(controlfds[0]);
Канал controlfds
используется для приостановки дочернего процесса до того, как оболочка переместит этот процесс в подходящую группу процессов. Закрытием записывающей стороны канала и чтением из считывающей стороны дочерний процесс останавливается до тех пор, пока родительский процесс закроет записывающую сторону, что происходит после вызова setpgid()
в строке 546. Этот тип механизма необходим для гарантии того, что дочерний процесс перемещается в группу процессов до происшествия exec()
. Если подождать до exec()
, то не будет уверенности, что процесс попадет в правильную группу процессов, пока он не начнет доступ к терминалу (который может быть запрещен).
Завершенные дочерние процессы проверяются ladsh
два раза. Первый раз это происходит во время ожидания процессов в группе процессов переднего плана. После завершения либо остановки процесса переднего плана ladsh
проверяет изменения в состояниях своих фоновых процессов с помощью функции checkJobs()
. Обе этих кодовых цепочки необходимо модифицировать с целью обработки остановленных и завершенных дочерних процессов.
Добавление флага WUNTRACED
к вызову waitpid()
, ожидающему на процессах переднего плана, позволяет заметить также остановленные процессы. Когда процесс скорее останавливается, чем завершается, устанавливается флаг дочернего процесса isStopped
и увеличивается номер задания stoppedProgs
. Если все программы задания были остановлены, ladsh
снова перемещается на передний план и ожидает команды пользователя. Вот как выглядит часть главного цикла ladsh
, ожидающая на процессе переднего плана.
Читать дальше