Создание дочернего процесса linux
Для порождения процессов в ОС Linux существует два способа. Один из них позволяет полностью заменить другой процесс, без замены среды выполнения. Другим способом можно создать новый процесс с помощью системного вызова fork() . Синтаксис вызова следующий:
pid_t является примитивным типом данных, который определяет идентификатор процесса или группы процессов. При вызове fork() порождается новый процесс (процесс-потомок), который почти идентичен порождающему процессу-родителю. Процесс-потомок наследует следующие признаки родителя:
- сегменты кода, данных и стека программы;
- таблицу файлов, в которой находятся состояния флагов дескрипторов файла, указывающие, читается ли файл или пишется. Кроме того, в таблице файлов содержится текущая позиция указателя записи-чтения;
- рабочий и корневой каталоги;
- реальный и эффективный номер пользователя и номер группы;
- приоритеты процесса (администратор может изменить их через nice );
- контрольный терминал;
- маску сигналов;
- ограничения по ресурсам;
- сведения о среде выполнения;
- разделяемые сегменты памяти.
Потомок не наследует от родителя следующих признаков:
- идентификатора процесса (PID, PPID);
- израсходованного времени ЦП (оно обнуляется);
- сигналов процесса-родителя, требующих ответа;
- блокированных файлов (record locking).
При вызове fork() возникают два полностью идентичных процесса. Весь код после fork() выполняется дважды, как в процессе-потомке, так и в процессе-родителе.
Процесс-потомок и процесс-родитель получают разные коды возврата после вызова fork() . Процесс-родитель получает идентификатор (PID) потомка. Если это значение будет отрицательным, следовательно при порождении процесса произошла ошибка. Процесс-потомок получает в качестве кода возврата значение 0, если вызов fork() оказался успешным.
Таким образом, можно проверить, был ли создан новый процесс:
Пример порождения процесса через fork() приведен ниже:
Когда потомок вызывает exit() , код возврата передается родителю, который ожидает его, вызывая wait() . WEXITSTATUS() представляет собой макрос, который получает фактический код возврата потомка из вызова wait() .
Функция wait() ждет завершения первого из всех возможных потомков родительского процесса. Иногда необходимо точно определить, какой из потомков должен завершиться. Для этого используется вызов waitpid() с соответствующим PID потомка в качестве аргумента. Еще один момент, на который следует обратить внимание при анализе примера, это то, что и родитель, и потомок используют переменную rv . Это не означает, что переменная разделена между процессами. Каждый процесс содержит собственные копии всех переменных.
Рассмотрим следующий пример:
В этом случае будет создано семь процессов-потомков. Первый вызов fork() создает первого потомка. Как указано выше, процесс наследует положение указателя команд от родительского процесса. Указатель команд содержит адрес следующего оператора программы. Это значит, что после первого вызова fork() указатель команд и родителя, и потомка находится перед вторым вызовом fork() .После второго вызова fork() и родитель, и первый потомок производят потомков второго поколения — в результате образуется четыре процесса. После третьего вызова fork() каждый процесс производит своего потомка, увеличивая общее число процессов до восьми.
Так называемые процессы-зомби возникают, если потомок завершился, а родительский процесс не вызвал wait() . Для завершения процессов используют либо оператор возврата, либо вызов функции exit() со значением, которое нужно возвратить операционной системе. Операционная система оставляет процесс зарегистрированным в своей внутренней таблице данных, пока родительский процесс не получит кода возврата потомка, либо не закончится сам. В случае процесса-зомби его код возврата не передается родителю, и запись об этом процессе не удаляется из таблицы процессов операционной системы. При дальнейшей работе и появлении новых зомби таблица процессов может быть заполнена, что приведет к невозможности создания новых процессов.
Источник
Linux.yaroslavl.ru
Процессы — примитивные модули для резервирования ресурсов системы. Каждый процесс имеет собственное адресное пространство. Процесс выполняет программу; Вы можете иметь многократные процессы, выполняющие ту же самую программу, но каждый процесс имеет собственную копию программы внутри собственного адресного пространства и выполняет это независимо от других копий.
Процессы организованы иерархически. Каждый процесс имеет родительский процесс. Процессы, созданные данным родителем называются дочерними процессами. Дочерний наследует многие из атрибутов родительского процесса.
Эта глава описывает, как программа может создавать, завершать, и управлять дочерними процессами. Фактически, имеются три различных операции: создание нового дочернего процесса, назначение новому процессу выполнить программу, и координирование завершения дочернего процесса.
Функция системы обеспечивает простой механизм для выполнения другой программы; он делает все три шага автоматически. Если Вы нуждаетесь в большом количестве контроля, Вы можете использовать примитивные функции, чтобы делать каждый шаг индивидуально.
Простой способ выполнять другую программу состоит в том, чтобы использовать функцию system. Эта функция делает всю работу выполнения подпрограммы, но она не дает Вам контроля над подробностями: Вы должны ждать, пока подпрограмма не завершится прежде, чем Вы сможете делать что-нибудь еще.
Функция system объявлена в заглавном файле » stdlib.h «.
Примечание Переносимости: Некоторые реализации C могут не иметь понятие командного процессора, который может выполнять другие программы. Вы можете определить, существует ли командный процессор, выполняя system (NULL); если возвращаемое значение отлично от нуля, командный процессор доступен.
Popen и pclose функции (см. Раздел 10.2 [Трубопровод на Подпроцесса]) близко связаны функцией system. Они позволяют родительскому процессу связываться со стандартным вводом и выводом выполняемой команды.
Этот раздел дает краткий обзор действий и шагов по созданию процесса и выполнения им другой программы.
Каждый процесс именован ID процесса. Уникальный ID процесса дан каждому процессу при создании.
Процессы создаются системным вызовом fork (так что операция создания нового процесса иногда вызывает раздваивание процесса). Дочерний процесс, созданный fork — точный аналог первоначального родительского процесса, за исключением того, что он имеет собственный ID.
Если Вы хотите, чтобы ваша программа ждала завершения дочернего процесса, Вы должен делать это явно после операции fork, вызовом wait или waitpid (см. Раздел 23.6 [Завершение Процесса]). Эти функции дают Вам ограниченную информацию относительно того, почему завершился дочерний прцесс — например, код состояния exit.
Раздвоенный дочерний процесс продолжает выполнять ту же самую программу как родительский процесс, в точке возвращения fork. Вы можете использовать возвращаемое значение от fork, чтобы отличить, выполняется ли программа в родительском процессе или в дочернем.
Наличие нескольких процессов выполняющх ту же самую программу не очень полезно. Но дочерний может выполнять другую программу, используя одну из запускающих функций; см. Раздел 23.5 [Выполнение Файла]. Программа, которую процесс выполняет, называется образом процесса. Начало выполнения новой программы заставляет процесс забыть все относительно предыдущего образа процесса; когда программа выходит, процесс тоже выходит, вместо того, чтобы возвратиться к предыдущему образу процесса.
Pid_t тип данных для ID процесса. Вы можете получить ID процесса, вызывая getpid. Функция getppid возвращает ID родителя текущего процесса (это также известно как ID родительского процесса). Ваша программа должна включить заглавные файлы » unistd.h » и » sys/types.h » чтобы использовать эти функции.
Функция fork — примитив для создания процесса. Она объявлена в заглавном файле » unistd.h «.
Если операция является успешной, то и родительский и дочерний процессы видят что fork возвращается, но с различными значениями: она возвращает значение 0 в дочернем процессе и ID порожденного процесса (ребенка) в родительском процессе.
Если создание процесса потерпело неудачу, fork возвращает значение -1 в родительском процессе. Следующие errno условия ошибки определены для fork: EAGAIN
не имеется достаточных ресурсов системы чтобы создать другой процесс, или пользователь уже имеет слишком много процессов. ENOMEM
процесс требует большего количества места чем система могла обеспечить. Специфические атрибуты дочернего процесса, которые отличаются от родительского процесса:
- Дочерний процесс имеет собственный уникальный ID.
- ID родителя дочернего процесса — ID родительского процесса.
- Дочерний процесс получает собственные копии описателей открытых файлов родительского процесса. Впоследствии изменение атрибутов описателей файла в родительском процессе не будет воздействовать на описатели файла в дочернем, и наоборот. См. Раздел 8.7 [Операции Управления].
- Прошедшее процессорное время для дочернего процесса установлено на нуль; см. Раздел 17.1 [Процессорное время].
- Дочерний не наследует набор блокировок файла родительского процессоа. См. Раздел 8.7 [Операции Управления].
- Дочерний не наследует набор таймеров родительского процесса. См. Раздел 17.3 [Установка Сигнализации].
- Набор отложенных сигналов (см. Раздел 21.1.3 [Получение Сигналов] ) для дочернего процесса, очищен. (Дочерний процесс наследует маску блокированных сигналов и действий сигналов из родительского процесса.)
В то время как fork делает полную копию адресного пространства вызывающего процесса и позволяет, и родителю и дочернему выполняться независимо, vfork не делает эту копию.
Взамен, дочерний процесс, созданный с vfork совместно использует адресное пространство родителя, пока он не вызывает одну из функций exec. Тем временем, родительский процесс приостанавливает свое выполнение.
Вы должны быть очень осторожны, чтобы не позволить дочернему процессу, созданному с vfork изменять любые глобальные данные или даже локальные переменные, общедоступнные с родителем. Кроме того, дочерний процесс не может возвращаться из (или делать длинный переход) функции, которая вызвала vfork! Это спутало бы информацию управления родительского процесса. Если Вы сомневаетесь, используйте fork.
Некоторые операционные системы не выполняют vfork. Библиотека GNU C разрешает Вам использовать vfork на всех системах, но фактически выполняет fork, если vfork не доступна. Если Вы соблюдаете соответствующие предосторожности при использовании vfork, ваша программа будет работать, даже если система использует fork взамен.
Этот раздел описывает совокупность exec функций, для выполнения файла как образа процесса. Вы можете использовать эти функции, чтобы заставить дочерний процесс выполнить новую программу после того, как он был раздвоен.
Эти функции отличаются тем, как Вы определяете аргументы, но они все делают ту же самую вещь. Они объявлены в заглавном файле » unistd.h «.
Аргумент argv — массив строк с нулевым символом в конце, который используется, чтобы обеспечить значение для аргумента argv функции main программы, которая будет выполнена. Последний элемент этого массива должен быть пустой указатель. Обычно, первый элемент этого массива — имя файла программы. См. Раздел 22.1 [Аргументы Программы] , для подробностей относительно того, как программы могут обращаться к этим аргументам.
Среда для нового образа процесса берется из переменной environ текущего образа процесса; см. Раздел 22.2 [Переменные среды], для уточнения инфрмации относительно сред.
Эта функция полезна для выполняющихся утилит системы, потому что она ищет их в местах, которые пользователь выбрал. Оболочки используют ее, чтобы выполнить команды написанные пользователем.
Размер списка параметров и списка среды, вместе не должен быть больше чем ARG_MAX байт. См. Раздел 27.1 [Общие Ограничения]. В системе GNU, размер (который сравнивается c ARG_MAX) включает, для каждой строки, число символов в строке, плюс размер char*, плюс один, округленный вверх после умножения на размер char*. Другие системы могут иметь несколько отличные правила для подсчета.
Эти функции обычно не возвращаются, так как выполнение новой программы заставляет завершиться программу выполнения в настоящее время. Значение -1 возвращено в случае отказа. В дополнение к обычным синтаксическим ошибкам имени файла (см. Раздел 6.2.3 [Ошибки Имени файла]), следующие errno условия ошибки определены для этих функций: E2BIG объединенный размер списка параметров новой программы и списка среды больше чем ARG_MAX байт. Система GNU не имеет никакого специфического ограничения размера списка параметров, так что этот код ошибки не может получиться, но Вы можете получать ENOMEM взамен, если аргументы слишком большие для доступной памяти. ENOEXEC
заданный файл не может быть выполнен, потому что он не находится в правильном формате.
Выполнение заданного файла требует большего количества памяти чем было доступно. Если выполнение нового файла преуспевает, это модифицирует поле времени доступа файла, как будто файл был прочитан. См. Раздел 9.8.9 [Времена Файла].
Выполнение нового образа процесса полностью не изменяет содержимое памяти, копируются только аргументы и строки среды. Но много других атрибутов процесса неизменяемы:
- ID процесса и ID родительского процесса. См. Раздел 23.2 [Понятия Создания Процесса].
- Групповая принадлежность сеанса и процесса. См. Раздел 24.1 [Понятия Управления заданиями].
- Реальный пользовательский ID, ID группы, и дополнительный ID группы. См. Раздел 25.2 [Владелец Процесса].
- Отложенные таймеры. См. Раздел 17.3 [Установка Сигнализации].
- Текущий рабочий каталог и корневой каталог. См. Раздел 9.1 [Рабочий каталог].
- Маска режима создаваемого файла. См. Раздел 9.8.7 [Установка Прав].
- Маска сигналов Процесса; см. Раздел 21.7.3 [Маска сигналов Процесса].
- Отложенные сигналы; см. Раздел 21.7 [Блокированные Сигналы].
- Прошедшее процессорное время, связанное с процессом; см. Раздел 17.1 [Процессорное время]. Если set-user-ID и set-group-ID биты режима файла образа процесса установлены, это воздействует на эффективный ID пользователя и эффективный ID группы (соответственно) процесса. Эти понятия обсуждены подробно в Разделе 25.2 [Владелец Процесса].
Сигналы которые игнорируются в существующем образе процесса, также будут установлены, чтобы игнорироваться в новом образе процесса. Все другие сигналы будт установлены по умолчанию в новом образе процесса. См. Главу 21 [Обработка Сигнала].
Описатели Файла, открытые в существующем образе процесса остаются открытыми в новом образе процесса, если они не имеют FD_CLOEXEC флага. Файлы, которые остаются открытыми, наследуют все атрибуты описания открытого файла из существующего образа процесса, включая блокировки файла. Описатели Файла обсуждены в Главе 8 [Ввод — вывод низкого уровня].
Новый образ процесса не имеет никаких потоков за исключением тех, что он создает заново.
Каждый из потоков в предыдущем образе процесса имеет описатель внутри него, и эти описатели остаются после exec (если они не имеют FD_CLOEXEC). Новый образ процесса может повторно соединять их с новыми потоками, используя fdopen (см. Раздел 8.4 [Описатели и Потоки]).
Функции, описанные в этом разделе используются, чтобы ждать завершения или останова дочернего процесса и определять его состояние. Эти функции объявлены в заглавном файле » sys/wait.h «.
Другие значения для pid аргумента имеют специальные интерпретации. Значение -1 или WAIT_ANY информация состояния для любого дочернего процесса; значение 0 или WAIT_MYPGRP запрашивает информацию для любого дочернего процесса в той же самой группе процесса как вызывающий процесс; и любое другое отрицательное значение — pgid запрашивает информацию для любого дочернего процесса, чей ID группы — pgid.
Если информация состояния дочернего процесса доступна немедленно, эта функция возвращается немедленно без ожидания. Если доступна информация состояния больше чем одного готового продолжиться дочернего процесса, один из них будет выбран беспорядочно, и его состояние возвращено немедленно.
Чтобы получить состояние других готовых продолжиться дочерних процессов, Вы должны вызвать waitpid снова.
Аргумент options — битовая маска. Значение должно быть поразрядным ИЛИ (то есть `|’) нуля или большого количества WNOHANG и WUNTRACED флагов. Вы можете использовать WNOHANG флаг, чтобы указать, что родительский процесс не должен ждать; и WUNTRACED флаг, чтобы запросить информацию состояния остановленных процессов также как процессов, которые завершились.
Информация состояния дочернего процесса сохранена в объекте, на который указывает status_ptr, если status_ptr не пустой указатель.
Возвращаемое значение — обычно ID дочернего процесса, о чьем состояние сообщено. Если WNOHANG опция была определена и никакой дочерний процесс, не ждет, чтобы быть отмеченным, то значение — нуль. Значение -1 возвращено в случае ошибки. Следующие errno ошибки определены для этой функции: EINTR
Функция была прервана получением сигнала. См. Раздел 21.5 [Прерванные Примитивы]. ECHILD
Не имеется никаких дочерних процессов, или заданный pid не дочерний для вызывающего процесса. EINVAL
Недопустимое значение аргумента options. Эти символические константы определены как значения для pid аргумента waitpid функции.
Эти символические константы определены как флаги для аргумента options функции waitpid.
Вы можете сделать OR флагов вместе, чтобы получить значение, и использовать его как аргумент.
Если значение состояния выхода (см. Раздел 22.3 [Завершение Программы]) дочернего процесса — нуль, то значение состояния, сообщенное waitpid или wait — также нуль. Вы можете проверять другие виды информации, закодированные в возвращенном значении состояния, используя следующие макрокоманды. Эти макрокоманды определены в заглавном файле » sys/wait.h «.
Библиотека GNU также обеспечивает эти средства для совместимости с UNIX BSD. BSD использует тип данных union, чтобы представить значения состояния, а не int. Два представления фактически взаимозаменяемы; они описывают те же самые битовые шаблоны. Библиотека GNU C определяет макрокоманды типа WEXITSTATUS так, чтобы они работали на любом виде объекта, и функция wait определена, чтобы принять любой тип указателя как аргумент status_ptr.
Эти функции объявлены в » sys/wait.h «.
Вместо того, чтобы обращаться к этим элементам непосредственно, Вы должны использовать эквивалентные макрокоманды.
Если usage — не пустой символ, wait3 сохраняет тип использования для дочернего процесса в *rusage (но только, если дочерний завершился, а не остановился). См. Раздел 17.5 [Использование Ресурсов].
Если usage — не пустой символ, wait4 сохраняет тип использования для дочернего процесса в *rusage (но только, если дочерний завершился, а не остановился). См. Раздел 17.5 [Использование Ресурсов].
Вот пример программы, показывающий, как Вы могли бы написать функцию, подобную встроенной системе. Она выполняет аргумент command, используя » sh -c command «.
Не забудьте, что первый аргумент argv, представляет имя выполняемой программы. Именно поэтому, в обращении к execl, SHELL обеспечена один раз, чтобы назвать выполняемую программу, и второй раз, чтобы обеспечить значение для argv [0].
Вызов execl в дочернем процессе не возвращается, если он успешен. Если он терпит неудачу, Вы должен делать кое-что, чтобы заставить дочерний процесс завершиться. Правильное поведение для дочернего процесса — сообщить отказ родительскому процессу.
Вызовите _exit, чтобы выполнить это. Причина для использования _exit вместо exit состоит в том, чтобы избежать flush полностью буферизированных потоков типа stdout. Буфера этих потоков возможно содержат данные, которые были скопированы из родительского процесса функцией fork, эти данные будут выводиться в конечном счете родительским процессом. Вызов exit в дочернем вывел бы данные дважды. См. Раздел 22.3.5 [Внутренняя организация Окончания].
Источник