Строка 1935 сохраняет значение ID процесса. Строка 1936 выделяет память для новой IOBUF
для данных дескриптора файла и командной строки. Третий аргумент здесь равен NULL
: он позволяет при необходимости использовать предварительно выделенный IOBUF
.
Если выделение памяти потерпело неудачу, строки 1937–1942 производят очистку, закрывая каналы и посылая сигнал «kill» порожденным процессам, чтобы заставить их завершить работу. (Функция kill()
описана в разделе 10.6.7 «Отправка сигналов kill()
и killpg()
».)
1946 rp->fp = fdopen(ptoc[1], "w");
1947 if (rp->fp == NULL) {
1948 iop_close(rp->iop);
1949 rp->iop = NULL;
1950 (void)close(ctop[0]);
1951 (void)close(ctop[1]);
1952 (void)close(ptoc[0]);
1953 (void)close(ptoc[1]);
1954 (void)kill(pid, SIGKILL); /* избыточно? (пардон, каламбур)
[104]*/
1955
1956 return FALSE;
1957 }
Строки 1946–1957 аналогичны. Они устанавливают вывод родителя на потомка, сохраняя дескриптор файла для записывающего конца канала от родителя к потомку в FILE*
, используя функцию fdopen()
. Если это завершается неудачей, строки 1947–1957 предпринимают те же действия, что и ранее: закрывают все дескрипторы каналов и посылают сигнал порожденным процессам.
С этого момента записываемый конец канала от родителя к потомку и читаемый конец канала от потомка к родителю хранятся в более крупных структурах: FILE*
и IOBUF
соответственно. Они автоматически закрываются обычными процедурами, которые закрывают эти структуры. Однако, остаются две задачи:
1960 os_close_on_exec(ctop[0], str, "pipe", "from");
1961 os_close_on_exec(ptoc[1], str, "pipe", "from");
1962
1963 (void)close(ptoc[0]);
1964 (void)close(ctop[1]);
1966
1967 return TRUE;
1968 }
...
1977 }
Строки 1960–1961 устанавливают флаг close-on-exec для двух дескрипторов, которые остались открытыми. os_close_on_exec()
является простой функцией-оболочкой, которая выполняет эту работу на Unix- и POSIX-совместимых системах, но ничего не делает на системах, в которых нет флага close-on-exec. Это скрывает проблему переносимости в одном месте и позволяет избежать в коде множества запутывающих #ifdef
здесь и в других местах io.c
.
Наконец, строки 1963–1964 закрывают концы каналов, которые не нужны родителю, а строка 1967 возвращает TRUE для обозначения успеха.
9.6. Рекомендуемая литература
Управление заданиями сложно, включает группы процессов, сеансы, механизмы ожидания, сигналы и манипулирование группой процессов терминала. По существу, мы решили не вдаваться в детали. Однако, вы можете захотеть взглянуть на следующие книги:
1.
Эта книга и полна, и основательна, охватывая элементарное и продвинутое программирование под Unix. Она превосходно освещает группы процессов, сеансы, управление заданиями и сигналы
2.
Эта книга дает хороший обзор того же материала, включая обсуждение структур данных ядра, которое можно найти в разделе 4.8 этой книги.
9.7. Резюме
• Новые процессы создаются с помощью fork()
. После этого оба процесса исполняют один и тот же код, причем единственным различием является возвращаемое значение: 0 в порожденном процессе и положительный номер PID в родительском. Порожденный процесс наследует копии почти всех атрибутов родителя, наиболее важными из которых являются, пожалуй, открытые файлы.
• Унаследованные разделяемые дескрипторы файлов делают возможным многое из высокоуровневой семантики Unix и элегантные управляющие структуры оболочки. Это одна из наиболее фундаментальных частей оригинального дизайна Unix. Из-за разделения дескрипторов файл на самом деле не закрывается до тех пор, пока не будет закрыт последний открытый дескриптор файла. Это в особенности касается каналов, но затрагивает также освобождение дисковых блоков для удаленных, но все еще открытых файлов.