Выполнение клонированного потомка начинается с функции childFunc(), которая получает файловый дескриптор (посредством аргумента arg), открытый главной программой (на шаге
Листинг 28.3. Использование clone() для создания дочерних процессов
procexec/t_clone.c
#define _GNU_SOURCE
#include
#include
#include
#include
#include "tlpi_hdr.h"
#ifndef CHILD_SIG
#define CHILD_SIG SIGUSR1 /* При завершении клонированного потомка
будет сгенерирован сигнал */
#endif
static int /* Начальная функция для клонированного потомка */
childFunc(void *arg)
{
errExit("close");
return 0; /* Здесь потомок завершается */
}
int
main(int argc, char *argv[])
{
const int STACK_SIZE = 65536; /* Размер стека для клонированного потомка */
char *stack; /* Начало буфера стека */
char *stackTop; /* Конец буфера стека */
int s, fd, flags;
if (fd == –1)
errExit("open");
/* Если argc > 1, потомок будет разделять таблицу файловых дескрипторов с родителем */
/* Выделяем стек для потомка */
if (stack == NULL)
errExit("malloc");
stackTop = stack + STACK_SIZE; /* Предполагается, что стек растет сверху вниз */
/* Игнорируем CHILD_SIG, если это сигнал, который по умолчанию завершает процесс;
при этом не игнорируем SIGCHLD (который игнорируется по умолчанию), иначе это
могло бы помешать созданию процесса-"зомби". */
if (signal(CHILD_SIG, SIG_IGN) == SIG_ERR)
errExit("signal");
/* Создаем потомка; потомок начинает выполнение с функции childFunc() */
errExit("clone");
/* Родитель переходит сюда и ждет потомка; потомку, который применяет
для уведомления любой сигнал, кроме SIGCHLD, нужно значение _WCLONE. */
errExit("waitpid");
printf("child has terminated\n");
/* Повлияло ли на родителя закрытие дескриптора в потомке? */
if (s == –1 && errno == EBADF)
printf("file descriptor %d has been closed\n", fd);
else if (s == –1)
printf("write() on file descriptor %d failed "
"unexpectedly (%s)\n", fd, strerror(errno));
else
printf("write() on file descriptor %d succeeded\n", fd);
exit(EXIT_SUCCESS);
}
procexec/t_clone.c
Запустив программу из листинга 28.3 без аргумента командной строки, мы получим следующий результат:
$ ./t_clone
child has terminated
write() on file descriptor 3 succeeded
Если запустить программу с аргументом командной строки, можно увидеть, что оба процесса используют одну и ту же таблицу файловых дескрипторов:
$ ./t_clone x
child has terminated
file descriptor 3 has been closed
Более сложный пример использования вызова clone() представлен в файле procexec/demo_clone.c, входящем в архив с исходным кодом для этой книги.
28.2.1. Аргумент flags вызова clone()
Аргумент flags вызова clone() представляет собой сочетание (побитовое ИЛИ) значений битовой маски, описанных на следующих страницах. Мы рассмотрим их в порядке, который облегчает объяснение; начнем с тех флагов, что применяются в реализации потоков выполнения POSIX. С этой точки зрения термин
На данном этапе стоит отметить, что наши попытки провести грань между терминами
По мере описания далее мы иногда будем упоминать две главные реализации POSIX-потоков, доступные в Linux: LinuxThreads (старую) и NTPL (более современную). Детальную информацию о них можно найти в разделе 33.5.