Главная страница Взаимодействие нетривиальных процессов Листинг 15.22 (продолжение) 5 return; /* просто прерываем door call() */ 6 } 7 int 8 mainCint argc. char **argv) 10 int fd; 11 long ival. oval; 12 door arg t arg; 13 if (argc != 3) 14 err quit( usage: clientintr2 <server-pathname> <integer-value> ); 15 fd = Open(argv[l]. 0 RDWR): /* открываем дверь */ 16 /* подготовка аргументов и указателя на результат */ 17 ival. = atoi(argv[2]); 18 arg .data ptr = (char *) &ival; /* аргументы */ 19 arg.data size = sizeof(long); /* размер аргументов */ 20 arg.descjtr = NULL: 21 arg.desc num = 0: 22 arg.rbuf = (char *) &oval; /* данные */ 23 arg.rsize = sizeof(long): /* размер данных */ 24 Signal(SIGCHLD. sig chld): 25 if (ForkO == 0) { 26 sleep(2); /* дочерний процесс */ 27 exitCO): /* отправка SIGCHLD */ 2B } 29 /* вызов процедуры сервера и вывод результата */ 30 Door call(fd. &arg); 31 printfCresult: ld\n . oval): 32 exitCO): 33 } Клиенту будет возвращена та же ошибка, что и при досрочном заверщении сервера - EINTR: Solaris X clientintr2 /tmp/door2 22 door call error: Interrupted system call Поэтому нужно блокировать все сигналы, которые могут прервать вызов с1оог са11. . Идемпотентные и неидемпотентные процедуры А что произойдет, если мы перехватим сигнал EINTR и вызовем процедуру сервера еще раз, поскольку мы знаем, что эта ошибка возникла из-за нашего собственного прерывания системного вызова перехваченным сигналом (SIGCHLD)? Это может привести к некоторым проблемам, как мы покажем ниже. Изменим сервер так, чтобы он выводил идентификатор вызванного потока, делал паузу в 6 секунд и выводил идентификатор потока по завершении его. В листинге 15.23 приведен текст новой процедуры сервера. Листинг 15.23. Процедура сервера, выводящая свой идентификатор потока дважды doors/serverintr3.с 1 #lnclude unpipc.h 2 void 3 servprocCvoid *cookie. char *dataptr. s1ze t datasize. 4 door desc t *descptr, s1ze t ndesc) 6 long arg. result; 7 printfС thread id ld called\n . pr thread idCNULL)); 8 sleepC6); /* даем клиенту возможность перехватить SIGCHLD */ 9 arg = *CClong *) dataptr); 10 result = arg * arg; 11 printfС thread id *ld returning\n . pr thread idCNULL)): 12 Door returnCCchar *) &result, sizeofCresult), NULL. 0); 13 } В листинге 15.24 приведен текст программы-клиента. Листинг 15.24. Клиент, вызывающий door call еще раз, после перехвата E1NTR doors/clientintr3.c 1 finclude unpipc.h 2 volatile sig atomic t caught sigchld; 3 void 4 sig chldCint signo) 5 {. 6 caught sigchld = 1; 7 return: /* прерываем вызов door callC) */ 9 int 10 mainCint argc. char **argv) 11 { 12 int fd, rc; 13 long ival. oval: 14 door arg t arg; 15 if Cargc != 3) 16 err quitC usage; clientintr3 <server-pathname> <integer-value> ); 17 fd = OpenCargvEl], 0 RDWR); /* открытие двери */ 18 /* подготовка аргументов и указателя на результаты */ 19 ival = atolCargv[2]); 20 arg.data ptr = Cchar *) &ival: /* аргументы */ 21 arg.data size = sizeofClong); /* размер аргументов */ 22 arg.desc ptr = NULL: 23 arg.desc num = 0: 24 arg.rbuf = Cchar *) &oval; /* возвращаемые данные */ 25 arg.rsize = SizeofClong); /* размер данных */ 26 SignalCSIGCHLD, sig chld): продолжение Листинг 15.24 (продолжение) 27 1f (ForkO == 0) { 28 sleep(2): /* дочерний процесс */ 29 ex1t(0): /* отправка SIGCHLD */ 30 } 31 /* родительский процесс : вызов процедуры сервера и вывод результата */ 32 for ( : : ) { 33 printfCcalUng door call\n ); 34 1f ( (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(O): 44 } 2-8 Объявляем глобальную переменную caught sigchlcl, устанавливая ее в единицу при перехвате сигнала SIGCHLD. 31-42 Вызываем с1оог са11 в цикле, пока он не завершится успешно. Глядя на выводимые клиентом результаты, мы можем подумать, что все в порядке: Solaris X clientintr3 /tmp/door3 33 calling door call calling door call result: 1089 Функция doorcal 1 вызывается в первый раз, обработчик сигнала срабатывает через 2 секунды после этого и переменной caught si gchl d присваивается значение 1. door cal 1 при этом возвращает ошибку EINTR и мы вызываем door cal 1 еще раз, Во второй раз процедура завершается успешно. Посмотрев на выводимый сервером текст, мы увидим, что процедура сервера была вызвана дважды: Solaris X serverintr3 /tmp/door3 thread id 4 called thread id 4 returning thread id 5 called thread id 5 returning Когда клиент второй раз вызывает door cal 1, это приводит к запуску нового потока, вызывающего процедуру сервера еще раз. Если процедура сервера идем-потентна, проблем в такой ситуации не возникнет. Однако если она неидемпо-тентна, это может привести к ошибкам. Термин идемпотентность по отношению к процедуре подразумевает, что процедура может быть вызвана произвольное число раз без возникновения ошибок. Наша процедура сервера, вычисляющая квадрат целого числа, идемпотент-на: мы получаем правильный результат вне зависимости от того, сколько раз мы ее вызовем. Другим примером является процедура, возвращающая дату и время. Хотя эта процедура и будет возвращать разную информацию при новых вызовах (поскольку дата и время меняются), это не вызовет проблем. Классическим при-
|
© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования. |