Основную массу работы по разветвлению процесса выполняет функция do_fork
kernel/fork.c. Эта функция, в свою очередь, вызывает функцию copy_process и запускает новый процесс на выполнение. Ниже описана та интересная работа, которую выполняет функция copy_process.• Вызывается функция dup_task_struct
thread_info и task_struct для нового процесса, причем все значения указанных структур данных идентичны для порождающего и порожденного процессов. На этом этапе дескрипторы родительского и порожденного процессов идентичны.• Проверяется, не произойдет ли при создании нового процесса переполнение лимита на количество процессов для данного пользователя.
• Теперь необходимо сделать порожденный процесс отличным от родительского. При этом различные поля дескриптора порожденного процесса очищаются или устанавливаются в начальные значения. Большое количество данных дескриптора процесса является совместно используемым.
• Далее состояние порожденного процесса устанавливается в значение TASK_UNINTERRUPTIBLE
• Из функции copy_process
copy_flags, которая обновляет значение поля flags структуры task struct. При этом сбрасывается флаг PF_SUPERPRIV, который определяет, имеет ли процесс права суперпользователя. Флаг PF_FORKNOEXEC, который указывает на то, что процесс не вызвал функцию exec, — устанавливается.• Вызывается функция get_pid
PID для новой задачи.• В зависимости от значений флагов, переданных в функцию clone
• Происходит разделение оставшейся части кванта времени между родительским и порожденным процессами (это более подробно обсуждается в главе 4, "Планирование выполнения процессов").
• Наконец, происходит окончательная зачистка структур данных и возвращается указатель на новый порожденный процесс.
Далее происходит возврат в функцию do_fork
copy_process происходит успешно, то новый порожденный процесс возобновляет выполнение. Порожденный процесс намеренно запускается на выполнение раньше родительского[16].В обычной ситуации, когда порожденный процесс сразу же вызывает функцию exec
vforkСистемный вызов vfork
fork, за исключением того, что записи таблиц страниц родительского процесса не копируются. Вместо этого порожденный процесс запускается как отдельный поток в адресном пространстве родительского процесса и родительский процесс блокируется до того момента, пока порожденный процесс не вызовет функцию exec или не завершится. Порожденному процессу запрещена запись в адресное пространство. Такая оптимизация была желанной в старые времена 3BSD, когда реализация системного вызова fork не базировалась на технике копирования страниц памяти при записи. Сегодня, при использовании техники копирования страниц памяти при записи и запуске порожденного процесса перед родительским, единственное преимущество вызова vfork — это отсутствие копирования таблиц страниц родительского процесса. Если когда-нибудь в операционной системе Linux будет реализовано копирование полей таблиц страниц при записи[17], то вообще не останется никаких преимуществ. Поскольку семантика функции vfork достаточно ненадежна (что, например, будет, если вызов exec завершится неудачно?), то было бы здорово, если бы системный вызов vfork умер медленной и мучительной смертью. Вполне можно реализовать системный вызов vfork через обычный вызов fork, что действительно имело место в ядрах Linux до версии 2.2.Сейчас системный вызов vfork
clone, как показано ниже.• При выполнении функции copy_process
vfork_done структуры task_struct устанавливается в значение NULL.• При выполнении функции do_fvork
vfork_done устанавливается в ненулевое значение (начинает указывать на определенный адрес).