Linux программирование в примерах
19661967 return TRUE;1968 }...1977 }Строки 1960–1961 устанавливают флаг close-on-exec для двух дескрипторов, которые остались открытыми.
является простой функцией-оболочкой, которая выполняет эту работу на Unix- и POSIX-совместимых системах, но ничего не делает на системах, в которых нет флага close-on-exec. Это скрывает проблему переносимости в одном месте и позволяет избежать в коде множества запутывающихos_close_on_exec()здесь и в других местах#ifdef.io.cНаконец, строки 1963–1964 закрывают концы каналов, которые не нужны родителю, а строка 1967 возвращает TRUE для обозначения успеха.
9.6. Рекомендуемая литература
Управление заданиями сложно, включает группы процессов, сеансы, механизмы ожидания, сигналы и манипулирование группой процессов терминала. По существу, мы решили не вдаваться в детали. Однако, вы можете захотеть взглянуть на следующие книги:
1. Advanced Programming in the UNIX Environment, 2nd edition, by W. Richard Stevens and Stephen Rago. Addison-Wesley, Reading Massachusetts, USA, 2004. ISBN: 0-201-43307-9.
Эта книга и полна, и основательна, охватывая элементарное и продвинутое программирование под Unix. Она превосходно освещает группы процессов, сеансы, управление заданиями и сигналы
2. The Design and Implementation of the 4.4 BSD Operating System, by Marshall Kirk McKusick, Keith Bostic, Michael J. Karels, and John S. Quarterman. Addison-Wesley, Reading, Massachusetts, USA, 1996. ISBN: 0-201-54979-4.
Эта книга дает хороший обзор того же материала, включая обсуждение структур данных ядра, которое можно найти в разделе 4.8 этой книги.
9.7. Резюме
• Новые процессы создаются с помощью
. После этого оба процесса исполняют один и тот же код, причем единственным различием является возвращаемое значение: 0 в порожденном процессе и положительный номер PID в родительском. Порожденный процесс наследует копии почти всех атрибутов родителя, наиболее важными из которых являются, пожалуй, открытые файлы.fork()• Унаследованные разделяемые дескрипторы файлов делают возможным многое из высокоуровневой семантики Unix и элегантные управляющие структуры оболочки. Это одна из наиболее фундаментальных частей оригинального дизайна Unix. Из-за разделения дескрипторов файл на самом деле не закрывается до тех пор, пока не будет закрыт последний открытый дескриптор файла. Это в особенности касается каналов, но затрагивает также освобождение дисковых блоков для удаленных, но все еще открытых файлов.
• Вызовы
иgetpid()возвращают ID текущего и родительского процессов соответственно. Родителем процесса, первоначальный родитель которого завершается, становится специальный процессgetppid()с PID 1. Таким образом, PPID может меняться, и приложения должны быть готовы к этому.init• Системный вызов
дает возможность настраивать приоритет вашего процесса. Чем приятнее вы по отношению к другим процессам, тем меньше ваш относительный приоритет, и наоборот. Лишь суперпользователь может иметь больший приоритет по сравнению с другими процессами. На современных системах, особенно однопользовательских, нет действительных причин для изменения знамения относительного приоритета.nice()• Системный вызов
начинает исполнение новой программы в существующем процессе. Шесть различных версий вызова предоставляют гибкость в установке списков аргументов и окружения ценой первоначальной путаницы по поводу того, какую из них лучше всего использовать. Два варианта имитируют механизм поиска оболочки и отступают к использованию оболочки для интерпретации файла в случае, если он не является двоичным исполняемым файлом; эти варианты должны использоваться с предусмотрительностью.exec()• Значение
для новой программы обычно происходит от имени исполняемого файла, но это лишь соглашение. Как и в случае сargv[0], значительный, но не идентичный набор атрибутов наследуется черезfork(). Другие атрибуты сбрасываются для использования подходящих значений по умолчанию.exec• Функция
регистрирует функции обратного вызова для вызова в порядке LIFO при завершении программы. Функцииatexit(),exit()и_exit()все завершают программу, передавая статус завершения обратно родителю,_Exit()очищает открытые потокиexit()и запускает функции, зарегистрированные с помощьюFILE*. Две другие функции завершаются немедленно и должны использоваться, лишь когдаatexit()в порожденном процессе завершилась неудачей. Возвращение изexecподобно вызовуmain()с данным возвращаемым значением. В C99 и C++ выпадение изexit()в конце функции дает тот же результат, что и 'main()', но является плохой практикой.exit(0)•
иwait()являются функциями POSIX для получения статуса завершения порожденного процесса. Различные макросы позволяют определить, завершился ли порожденный процесс нормально, и в таком случае определить статус его завершения, или же порожденный процесс претерпел сигнал завершения, и в этом случае определить совершивший этот проступок сигнал. Со специальными опциямиwaitpid()предоставляет также сведения о потомках, которые не завершились, но изменили состояние.waitpid()• Системы GNU/Linux и большинство Unix-систем поддерживают также функции BSD
иwait3(). GNU/Linux поддерживает также выходящий из употребленияwait4(). Функции BSD предоставляютunion wait, давая доступ к сведениям об использовании времени процессора, что может быть удобным. Хотя еслиstruct rusageбудет достаточной, то это наиболее переносимый способ выполнения.waitpid()• Группы процессов являются частью более крупного механизма управления заданиями, который включает сигналы, сеансы и манипулирование состоянием терминала,
возвращает ID группы процессов текущего процесса, agetpgrp()возвращает PGID определенного процесса. Сходным образом,getpgid()устанавливает PGID текущего процесса равным его PID, делая его лидером группы процессов;setpgrp()дает возможность родительскому процессу установить PGID порожденного, который еще не выполнилsetpgid().exec• Каналы и FIFO предоставляют односторонний коммуникационный канал между двумя процессами. Каналы должны быть установлены общим предком, тогда как FIFO могут использоваться любыми двумя процессами. Каналы создаются с помощью
, а файлы FIFO создаются с помощьюpipe(). Каналы и FIFO буферируют свои данные, останавливая производителя или потребителя, когда канал заполняется или пустеет.mkfifo()