Читать «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 в цикле, пока он не завершится успешно.
Глядя на выводимые клиентом результаты, мы можем подумать, что все в порядке: