Тот факт, что направление роста стека зависит от архитектуры, является концептуальным недостатком вызова clone(). Архитектура Intel IA-64 предоставляет улучшенный программный интерфейс клонирования в виде вызова clone2(). Он описывает диапазон дочернего стека таким образом, что направление его роста не имеет значения, поскольку передается сразу два адреса — начальный и конечный. Подробности ищите на справочной странице.

Аргумент flags вызова clone() служит сразу двум целям. Во-первых, его нижний байт содержит сигнал завершения потомка, который передается родителю в момент завершения дочернего процесса (если клонированный потомок останавливается по сигналу, родитель все равно получает SIGCHLD). Байт может быть равен 0; в этом случае сигнал не генерируется (сигнал завершения любого процесса можно определить с помощью файла вида /proc/PID/stat, доступного в Linux; дальнейшие подробности ищите на справочной странице proc(5)).

Вызовы fork() и vfork() не позволяют выбрать сигнал завершения; это всегда будет SIGCHLD.

Оставшиеся байты аргумента flags отводятся под битовую маску, которая определяет поведение вызова clone(). Ее значения собраны в табл. 28.2 и подробно описаны в подразделе 28.2.1.

Таблица 28.2. Значения битовой маски flags вызова clone()

Флаг — Действие

CLONE_CHILD_CLEARTID — Очищает ctid, когда потомок вызывает exec() или _exit() (начиная с 2.6) (здесь и далее в скобках версия Linux)

CLONE_CHILD_SETTID — Записывает идентификатор дочернего потока в ctid (начиная с 2.6)

CLONE_FILES — Родитель и потомок разделяют таблицу дескрипторов открытых файлов

CLONE_FS — Родитель и потомок разделяют атрибуты, относящиеся к файловой системе

CLONE_IO — Потомок получает доступ к контексту ввода/вывода родителя (начиная с 2.6.25)

CLONE_NEWIPC — Потомок получает новое пространство имен в System V IPC (начиная с 2.6.19)

CLONE_NEWNET — Потомок получает сетевое пространство имен (начиная с 2.4.24)

CLONE_NEWNS — Потомок получает копию родительского пространства имен файловой системы (2.4.19)

CLONE_NEWPID — Потомок получает новое пространство имен с идентификаторами процессов (начиная с 2.6.19)

CLONE_NEWUSER — Потомок получает новое пространство имен с идентификаторами пользователей (начиная с 2.6.23)

CLONE_NEWUTS — Потомок получает новое пространство имен UTS (utsname()) (начиная с 2.6.19)

CLONE_PARENT — Делает родителя потомка тождественным вызывающему процессу родителя (начиная с 2.4)

CLONE_PARENT_SETTID — Записывает идентификатор дочернего потока в ptid (начиная с 2.6)

CLONE_PID — Устаревший флаг, который используется только процессом загрузки системы (до 2.4)

CLONE_PTRACE — Трассирует потомка, если трассируется его родитель

CLONE_SETTLS — tls описывает для потомка хранилище на уровне потока выполнения (начиная с 2.6)

CLONE_SIGHAND — Родитель и потомок разделяют действия сигналов

CLONE_SYSVSEM — Родитель и потомок разделяют значения отмены семафоров (начиная с 2.6)

CLONE_THREAD — Помещает потомка в одну группу потоков выполнения с его родителем (начиная с 2.4)

CLONE_UNTRACED — Не позволяет применить к потомку значение CLONE_PTRACE (начиная с 2.6)

CLONE_VFORK — Родитель приостанавливает работу, пока потомок не сделает вызов exec() или _exit()

CLONE_VM — Родитель и потомок разделяют виртуальную память

Оставшиеся аргументы вызова clone() — ptid, tls и ctid. Они относятся к реализации потока выполнения — в частности, к использованию его идентификатора и локального хранилища. Мы рассмотрим применение этих аргументов, когда будем описывать значения битовой маски flags в подразделе 28.2.1 (в версиях Linux до 2.4 включительно эти три аргумента отсутствуют в вызове clone(); их специально добавили в Linux 2.6 для поддержки библиотеки потоков POSIX NPTL).

Пример программы

В листинге 28.3 показан простой пример использования вызова clone() для создания дочернего процесса. Главная программа делает следующее.

• Открывает файловый дескриптор (для /dev/null), который будет закрыт потомком .

• Присваивает аргументу flags вызова clone() значение CLONE_FILES (3). Если был предоставлен аргумент командной строки, родитель и потомок будут использовать единую таблицу файловых дескрипторов. В противном случае flags получает значение 0.

• Выделяет стек для потомка .

• Если значение CHILD_SIG не равно 0 или SIGCHLD, оно будет игнорироваться в случае завершения процесса по сигналу. Мы не игнорируем SIGCHLD, поскольку это помешало бы нам ждать потомка для получения его статуса.

• Вызывает clone() для создания потомка . Третий аргумент (битовая маска) включает в себя сигнал завершения. Четвертый аргумент (func_arg) указывает на дескриптор ранее открытого файла .

• Ждет завершения работы потомка .

• Проверяет, открыт ли все еще файловый дескриптор (созданный на шаге ), пытаясь записать в него что-нибудь с помощью вызова write() , и сообщает результат этой попытки.

Перейти на страницу:

Похожие книги