Читать «UNIX: взаимодействие процессов» онлайн - страница 258

Уильям Ричард Стивенс

31  printf(result: %ld\n", oval);

32  exit(0);

33 }

Клиенту будет возвращена та же ошибка, что и при досрочном завершении сервера — EINTR:

solaris % clientintr2 /tmp/door2 22

door_call error: interrupted system call

Поэтому нужно блокировать все сигналы, которые могут прервать вызов door_call.

Идемпотентные и неидемпотентные процедуры

А что произойдет, если мы перехватим сигнал EINTR и вызовем процедуру сервера еще раз, поскольку мы знаем, что эта ошибка возникла из-за нашего собственного прерывания системного вызова перехваченным сигналом (SIGCHLD)? Это может привести к некоторым проблемам, как мы покажем ниже.

Изменим сервер так, чтобы он выводил идентификатор вызванного потока, делал паузу в 6 секунд и выводил идентификатор потока по завершении его. В листинге 15.23 приведен текст новой процедуры сервера. 

Листинг 15.23. Процедура сервера, выводящая свой идентификатор потока дважды

//doors/serverintr3.c

1  #include "unpipc.h"

2  void

3  servproc(void *cookie, char *dataptr, size_t datasize,

4   door_desc_t *descptr, size_t ndesc)

5  {

6   long arg, result:

7   printf("thread id %ld called\n", pr_thread_id(NULL));

8   sleep(6); /* даем клиенту возможность перехватить SIGCHLD */

9   arg = *((long*)dataptr);

10  result = arg * arg;

11  printf("thread id %ld returning\n", pr_thread_id(NULL));

12  Door_return((char *) &result, sizeof(result), NULL, 0);

13 }

В листинге 15.24 приведен текст программы-клиента.

Листинг 15.24. Клиент, вызывающий door_call еще раз, после перехвата EINTR

//doors/clientintr3.c

1  #include "unpipc.h"

2  volatile sig_atomic_t caught_sigchld;

3  void

4  sig_chld(int signo)

5  {

6   caught_sigchld = 1;

7   return; /* прерываем вызов door_call() */

8  }

9  int

10 main(int argc, char **argv)

11 {

12  int fd, rc;

13  long ival, oval;

14  door_arg_t arg;

15  if (argc != 3)

16   err_quit("usage: clientintr3 <server-pathname> <integer-value>");

17  fd = Open(argv[1], O_RDWR); /* открытие двери */

18  /* подготовка аргументов и указателя на результаты */

19  ival = atol(argv[2]);

20  arg.data_ptr = (char*)&ival; /* аргументы */

21  arg.data_size = sizeof(long); /* размер аргументов */

22  arg.desc_ptr = NULL;

23  arg.desc_num = 0;

24  arg.rbuf = (char*)&oval; /* возвращаемые данные */

25  arg.rsize = sizeof(long); /* размер данных */

26  Signal(SIGCHLD, sig_chld);

27  if (Fork() == 0) {

28   sleep(2); /* дочерний процесс */

29   exit(0); /* отправка SIGCHLD */

30  }

31  /* родительский процесс : вызов процедуры сервера и вывод результата */

32  for (;;) {

33   printf("calling door_call\n");

34   if ((rc = door_call(fd, &arg)) == 0)

35    break; /* успешное завершение */

36   if (errno == EINTR && caught_sigchld) {

37    caught_sigchld = 0;

38    continue; /* повторный вызов door_call */

39   }

40   err_sys("door_call error");

41  }

42  printf("result: %ld\n", oval);

43  exit(0);

44 }

2-8 Объявляем глобальную переменную caught_sigchld, устанавливая ее в единицу при перехвате сигнала SIGCHLD.

31-42 Вызываем door_call в цикле, пока он не завершится успешно.

Глядя на выводимые клиентом результаты, мы можем подумать, что все в порядке: