Родительский процесс в ожидании завершения дочернего процесса

17-22 Родительский процесс закрывает другой конец потокового канала и вызывает функцию waitpid для ожидания завершения дочернего процесса. Статус завершения дочернего процесса возвращается в переменной status, и сначала мы проверяем, что программа завершилась нормально (то есть не была завершена из-за возникновения какого-либо сигнала). Затем макрос WEXITSTATUS преобразует статус завершения в статус выхода, значение которого должно быть между 0 и 255. Мы вскоре увидим, что если при открытии необходимого файла программой openfile происходит ошибка, то эта программа завершается, причем статус ее завершения равен соответствующему значению переменной errno.

Получение дескриптора

23 Наша функция read_fd, которую мы показываем в следующем листинге, получает дескриптор потокового канала. Кроме получения дескриптора мы считываем 1 байт данных, но ничего с этими данными не делаем.

ПРИМЕЧАНИЕ

При отправке и получении дескриптора по потоковому каналу мы всегда отправляем как минимум 1 байт данных, даже если получатель никак эти данные не обрабатывает. Иначе получатель не сможет распознать, что значит нулевое возвращаемое значение из функции read_fd: отсутствие данных (но, возможно, есть дескриптор) или конец файла.

В листинге 15.9 показана функция readfd, вызывающая функцию recvmsg для получения данных и дескриптора через доменный сокет Unix. Первые три аргумента этой функции те же, что и для функции read, а четвертый (recvfd) является указателем на целое число. После выполнения этой функции recvfd будет указывать на полученный дескриптор.

Листинг 15.9. Функция read_fd: получение данных и дескриптора

//lib/read_fd.c

 1 #include "unp.h"

 2 ssize_t

 3 read_fd(int fd, void *ptr, size_t nbytes, int *recvfd)

 4 {

 5  struct msghdr msg;

 6  struct iovec iov[1];

 7  ssize_t n;

 8  int newfd;

 9 #ifdef HAVE_MSGHDR_MSG_CONTROL

10  union {

11   struct cmsghdr cm;

12   char control[CMSG_SPACE(sizeof(int))];

13  } control_un;

14  struct cmsghdr *cmptr;

15  msg.msg_control = control_un.control;

16  msg.msg_controllen = sizeof(control_un.control);

17 #else

18  msg.msg_accrights = (caddr_t)&newfd;

19  msg.msg_accrightslen = sizeof(int);

20 #endif

21  msg.msg_name = NULL;

22  msg.msg_namelen = 0;

23  iov[0].iov_base = ptr;

24  iov[0].iov_len = nbytes;

25  msg.msg_iov = iov;

26  msg.msg_iovlen = 1;

27  if ((n = recvmsg(fd, &msg, 0)) <= 0)

28   return (n);

29 #ifdef HAVE_MSGHDR_MSG_CONTROL

30  if ((cmptr = CMSG_FIRSTHDR(&msg)) != NULL &&

31   mptr->cmsg_len == CMSG_LEN(sizeof(int))) {

32   if (cmptr->cmsg_level != SOL_SOCKET)

33    err_quit("control level != SOL_SOCKET");

34   if (cmptr->cmsg_type != SCM_RIGHTS)

35    err_quit("control type != SCM_RIGHTS");

36   *recvfd = *((int*)CMSG_DATA(cmptr));

37  } else

38   *recvfd = -1; /* дескриптор не был передан */

39 #else

40  if (msg.msg_accrightslen == sizeof(int))

41   *recvfd = newfd;

42  else

43   *recvfd = -1; /* дескриптор не был передан */

44 #endif

45  return (n);

46 }

8-26 Эта функция должна работать с обеими версиями функции recvmsg: с элементом msg_control и с элементом msg_accrights. Наш заголовочный файл config.h (см. листинг Г.2) определяет константу HAVE_MSGHDR_MSG_CONTROL, если поддерживается версия функции recvmsg с msg_control.

Проверка выравнивания буфера msg_control

10-13 Буфер msg_control должен быть выровнен в соответствии со структурой msghdr. Просто выделить в памяти массив типа char недостаточно. Здесь мы объявляем объединение, состоящее из структуры cmsghdr и символьного массива, что гарантирует необходимое выравнивание массива. Возможно и другое решение — вызвать функцию malloc, но это потребует освобождения памяти перед завершением функции.

27-45 Вызывается функция recvmsg. Если возвращаются вспомогательные данные, их формат будет таким, как показано на рис. 14.4. Мы проверяем, верны ли длина, уровень и тип, затем получаем вновь созданный дескриптор и возвращаем его через указатель вызывающего процесса recvfd. Макрос CMSG_DATA возвращает указатель на элемент cmsg_data объекта вспомогательных данных как указатель на элемент типа unsigned char. Мы преобразуем его к указателю на элемент типа int и получаем целочисленный дескриптор, на который указывает этот указатель.


Перейти на страницу:
Изменить размер шрифта: