Тайм-аут для функции connect (сигнал SIGALRM)
Тайм-аут для функции connect (сигнал SIGALRM)
В листинге 14.1[1] показана наша функция connect_timeo, вызывающая функцию connect с ограничением по времени, заданным вызывающим процессом. Первые три аргумента — это аргументы, которых требует функция connect, а четвертый — это длительность ожидания в секундах.
Листинг 14.1. Функция connect с тайм-аутом
//lib/connect_timeo.c
1 #include "unp.h"
2 static void connect_alarm(int);
3 int
4 connect_timeo(int sockfd, const SA *saptr, socklen_t salen, int nsec)
5 {
6 Sigfunc *sigfunc;
7 int n;
8 sigfunc = Signal(SIGALRM, connect_alarm);
9 if (alarm(nsec) != 0)
10 err_msg("connect_timeo: alarm was already set");
11 if ((n = connect(sockfd, saptr, salen)) < 0) {
12 close(sockfd);
13 if (errno == EINTR)
14 errno = ETIMEDOUT;
15 }
16 alarm(0); /* отключение alarm */
17 Signal(SIGALRM, sigfunc); /* восстанавливаем прежний обработчик
сигнала */
18 return (n);
19 }
20 static void
21 connect_alarm(int signo)
22 {
23 return; /* просто прерываем connect() */
24 }
Установка обработчика сигналов
8 Для SIGALRM устанавливается обработчик сигнала. Текущий обработчик сигнала (если таковой имеется) сохраняется, и таким образом мы можем восстановить его в конце функции.
Установка таймера
9-10 Таймер для процесса устанавливается на время (число секунд), заданное вызывающим процессом. Возвращаемое значение функции alarm — это число секунд, остающихся в таймере для процесса (если он уже установлен для процесса) в настоящий момент или 0 (если таймер не был установлен прежде). В первом случае мы выводим сообщение с предупреждением, поскольку мы стираем предыдущую установку таймера (см. упражнение 14.2).
Вызов функции connect
11-15 Вызывается функция connect, и если функция прерывается (EINTR), мы присваиваем переменной errno значение ETIMEDOUT. Сокет закрывается, чтобы не допустить продолжения трехэтапного рукопожатия.
Выключение таймера и восстановление предыдущего обработчика сигнала
16-18 Таймер при обнулении выключается, и восстанавливается предыдущий обработчик сигналов (если таковой имеется).
Обработка сигнала SIGALRM
20-24 Обработчик сигнала просто возвращает управление. Предполагается, что это прервет ожидание функции connect, заставив ее возвратить ошибку EINTR. Вспомните нашу функцию signal (см. листинг 5.5), которая не устанавливает флага SA_RESTART, когда перехватываемый сигнал — это сигнал SIGALRM.
Одним из важных моментов в этом примере является то, что мы всегда можем сократить период ожидания для функции connect, используя эту технологию, но мы не можем увеличить период, заданный для ядра. В Беркли-ядре тайм-аут для функции connect обычно равен 75 с. Мы можем задать меньшее значение для нашей функции, допустим 10, но если мы задаем большее значение, скажем 80, тайм- аут самой функции connect все равно составит 75 с.
Другой важный момент в данном примере — то, что мы используем возможность прерывания системного вызова (connect) для того, чтобы возвратить управление, прежде чем истечет время ожидания ядра. Такой подход допустим, когда мы выполняем системный вызов и можем обработать возвращение ошибки EINTR. Но в разделе 29.7 мы встретимся с библиотечной функцией, выполняющей системный вызов, которая сама выполняет заново системный вызов при возвращении ошибки EINTR. Мы можем продолжать работать с сигналом SIGALRM и в этом случае, но в листинге 29.6 мы увидим, что нам придется воспользоваться функциями sigsetjmp и siglongjmp, поскольку библиотечная функция игнорирует ошибку EINTR.
Данный текст является ознакомительным фрагментом.