Читать «Разработка ядра Linux» онлайн - страница 33

Роберт Лав

• В зависимости от значений флагов, переданных в функцию clone(), осуществляется копирование или совместное использование открытых файлов, информации о файловой системе, обработчиков сигналов, адресного пространства процесса и пространства имен (namespace). Обычно эти ресурсы совместно используются потоками одного процесса. В противном случае они будут уникальными и будут копироваться на этом этапе.

• Происходит разделение оставшейся части кванта времени между родительским и порожденным процессами (это более подробно обсуждается в главе 4, "Планирование выполнения процессов").

• Наконец, происходит окончательная зачистка структур данных и возвращается указатель на новый порожденный процесс.

Далее происходит возврат в функцию do_fork(). Если возврат из функции copy_process() происходит успешно, то новый порожденный процесс возобновляет выполнение. Порожденный процесс намеренно запускается на выполнение раньше родительского.

В обычной ситуации, когда порожденный процесс сразу же вызывает функцию exec(), это позволяет избежать накладных расходов, связанных с тем, что если родительский процесс начинает выполняться первым, то он будет ожидать возможности записи в адресное пространство посредством механизма копирования при записи.

Функция vfork()

Системный вызов vfork() позволяет получить тот же эффект, что и системный вызов fork(), за исключением того, что записи таблиц страниц родительского процесса не копируются. Вместо этого порожденный процесс запускается как отдельный поток в адресном пространстве родительского процесса и родительский процесс блокируется до того момента, пока порожденный процесс не вызовет функцию exec() или не завершится. Порожденному процессу запрещена запись в адресное пространство. Такая оптимизация была желанной в старые времена 3BSD, когда реализация системного вызова fork() не базировалась на технике копирования страниц памяти при записи. Сегодня, при использовании техники копирования страниц памяти при записи и запуске порожденного процесса перед родительским, единственное преимущество вызова vfork() — это отсутствие копирования таблиц страниц родительского процесса. Если когда-нибудь в операционной системе Linux будет реализовано копирование полей таблиц страниц при записи, то вообще не останется никаких преимуществ. Поскольку семантика функции vfork() достаточно ненадежна (что, например, будет, если вызов exec() завершится неудачно?), то было бы здорово, если бы системный вызов vfork() умер медленной и мучительной смертью. Вполне можно реализовать системный вызов vfork() через обычный вызов fork(), что действительно имело место в ядрах Linux до версии 2.2.

Сейчас системный вызов vfork() реализован через специальный флаг в системном вызове clone(), как показано ниже.

• При выполнении функции copy_process() поле vfork_done структуры task_struct устанавливается в значение NULL.

• При выполнении функции do_fvork(), если соответствующий флаг установлен, поле vfork_done устанавливается в ненулевое значение (начинает указывать на определенный адрес).