Литмир - Электронная Библиотека
Содержание  
A
A

Чтобы обработать прерванный вызов функции

accept
, мы изменяем вызов функции
accept
, приведенной в листинге 5.1, в начале цикла
for
следующим образом:

for (;;) {

 clilen = sizeof(cliaddr);

 if ((connfd = accept(listenfd, (SA*)&cliaddr, &clilen)) < 0) {

  if (errno == EINTR)

   continue; /* назад в for() */

  else

   err_sys("accept error");

 }

Обратите внимание, что мы вызываем функцию

accept
, а не функцию-обертку
Accept
, поскольку мы должны обработать неудачное выполнение функции самостоятельно.

В этой части кода мы сами перезапускаем прерванный системный вызов. Это допустимо для функции

accept
и таких функций, как
read
,
write
,
select
и
open
. Но есть функция, которую мы не можем перезапустить самостоятельно, — это функция
connect
. Если она возвращает ошибку
EINTR
, мы не можем снова вызвать ее, поскольку в этом случае немедленно возвратится еще одна ошибка. Когда функция connect прерывается перехваченным сигналом и не перезапускается автоматически, нужно вызвать функцию
select
, чтобы дождаться завершения соединения (см. раздел 16.3).

5.10. Функции wait и waitpid

В листинге 5.7 мы вызываем функцию

wait
для обработки завершенного дочернего процесса.

#include <sys/wait.h>

pid_t wait(int *<i>statloc</i>);

pid_t waitpid(pid_t <i>pid</i>, int *<i>statloc</i>, int <i>options</i>);

<i>Обе функции возвращают ID процесса в случае успешного выполнения, -1 в случае ошибки</i>

Обе функции, и

wait
, и
waitpid
, возвращают два значения. Возвращаемое значение каждой из этих функций — это идентификатор завершенного дочернего процесса, а через указатель
statloc
передается статус завершения дочернего процесса (целое число). Для проверки статуса завершения можно вызвать три макроса, которые сообщают нам, что произошло с дочерним процессом: дочерний процесс завершен нормально, уничтожен сигналом или только приостановлен программой управления заданиями (job-control). Дополнительные макросы позволяют получить состояние выхода дочернего процесса, а также значение сигнала, уничтожившего или остановившего процесс. В листинге 15.8 мы используем макроопределения
WIFEXITED
и
WEXITSTATUS
.

Если у процесса, вызывающего функцию

wait
, нет завершенных дочерних процессов, но есть один или несколько выполняющихся, функция
wait
блокируется до тех пор, пока первый из дочерних процессов не завершится.

Функция

waitpid
предоставляет более гибкие возможности выбора ожидаемого процесса и его блокирования. Прежде всего, в аргументе
pid
задается идентификатор процесса, который мы будем ожидать. Значение -1 говорит о том, что нужно дождаться завершения первого дочернего процесса. (Существуют и другие значения идентификаторов процесса, но здесь они нам не понадобятся.) Аргумент
options
позволяет задавать дополнительные параметры. Наиболее общеупотребительным является параметр
WNOHANG
: он сообщает ядру, что не нужно выполнять блокирование, если нет завершенных дочерних процессов.

Различия между функциями wait и waitpid

Теперь мы проиллюстрируем разницу между функциями

wait
и
waitpid
, используемыми для сброса завершенных дочерних процессов. Для этого мы изменим код нашего клиента TCP так, как показано в листинге 5.7. Клиент устанавливает пять соединений с сервером, а затем использует первое из них (
sockfd[0]
) в вызове функции
str_cli
. Несколько соединений мы устанавливаем для того, чтобы породить от параллельного сервера множество дочерних процессов, как показано на рис. 5.2.

UNIX: разработка сетевых приложений - img_40.png

Рис. 5.2. Клиент, установивший пять соединений с одним и тем же параллельным сервером

Листинг 5.7. Клиент TCP, устанавливающий пять соединений с сервером

/

/tcpcliserv/tcpcli04.c

 1 #include &quot;unp.h&quot;

 2 int

 3 main(int argc, char **argv)

 4 {

 5  int i, sockfd[5];

 6  struct sockaddr_in servaddr;

 7  if (argc != 2)

 8   err_quit(&quot;usage: tcpcli &lt;Ipaddress&gt;&quot;);

 9  for (i = 0; i &lt; 5; i++) {

10   sockfd[i] = Socket(AF_INET, SOCK_STREAM, 0);

11   bzero(&amp;servaddr, sizeof(servaddr));

12   servaddr.sin_family = AF_INET;

13   servaddr.sin_port = htons(SERV_PORT);

14   Inet_pton(AF_INET, argv[1], &amp;servaddr.sin_addr);

15   Connect(sockfd[i], (SA*)&amp;servaddr, sizeof(servaddr));

16  }

17  str_cli(stdin, sockfd[0]); /* эта функция выполняет все необходимые

                               действия для формирования запроса клиента */

18  exit(0);

19 }

Когда клиент завершает работу, все открытые дескрипторы автоматически закрываются ядром (мы не вызываем функцию close

,
а пользуемся только функцией
exit
) и все пять соединений завершаются приблизительно в одно и то же время. Это вызывает отправку пяти сегментов FIN, по одному на каждое соединение, что, в свою очередь, вызывает примерно одновременное завершение всех пяти дочерних процессов. Это приводит к доставке пяти сигналов
SIGCHLD
практически в один и тот же момент, что показано на рис. 5.3.

54
{"b":"225366","o":1}