Многие системные вызовы, такие как close( fd )
Может быть прервано сигналом. В этом случае обычно -1
возвращается и errno
установлено EINTR
,
Вопрос в том, что делать правильно? Скажи, я все еще хочу это fd
быть закрытым.
Что я могу придумать это:
while( close( fd ) == -1 )
if( errno != EINTR ) {
ReportError();
break;
}
Кто-нибудь может предложить лучший / более элегантный / стандартный способ справиться с этой ситуацией?
ОБНОВИТЬ:
Как заметил mux, SA_RESTART
Флаг может быть использован при установке обработчика сигнала.
Может кто-нибудь сказать мне, какие функции гарантированно перезапускаются на всех POSIX
системы (не только Linux
)?
Некоторые системные вызовы перезапускаются, это означает, что ядро перезапустит вызов, если прервано, если SA_RESTART
флаг используется при установке обработчика сигнала, Сигнал (7) Страница man говорит:
Если заблокированный вызов на один из следующих интерфейсов прерывается
обработчиком сигнала, тогда звонок будет автоматически перезапущен
после сигнала
обработчик возвращает, если был использован флаг SA_RESTART; в противном случае вызов не будет выполнен с ошибкой EINTR:
Это не упоминает, если close()
перезапускается, но это:
чтение (2), чтение (2), запись (2), запись (2), ioctl (2), открытие (2), ожидание (2),
wait3 (2), wait4 (2), waitid (2) и waitpid, accept (2), connect (2),
recv (2), recvfrom (2), recvmsg (2), send (2), sendto (2) и sendmsg (2)
flock (2) и fcntl (2) mq_receive (3), mq_timedreceive (3), mq_send (3),
и mq_timedsend (3) sem_wait (3) и sem_timedwait (3) futex (2)
Обратите внимание, что эти детали, в частности список не перезагружаемых вызовов, относятся к Linux
Я опубликовал соответствующий вопрос о том, какие системные вызовы перезапускаются, и если он указан где-то в POSIX, он указывается в POSIX, но это необязательно, поэтому вы должны проверить список не перезапускаемых вызовов для вашей ОС, если его там нет, то это должно быть перезапускаемые. Это мой вопрос:
Как узнать, перезапускается ли системный вызов Linux?
Обновление: Close — это особый случай, когда его нельзя перезапустить, и его не следует повторять в Linux, см. Этот ответ для более подробной информации:
https://stackoverflow.com/a/14431867/1157444
Для записи: по сути, на каждом UNIX, close()
не должен быть повторен если он возвращает EINTR. НЕ ДЕЛАЙТЕ поместите повторную петлю EINTR на место для закрытия, как вы бы для waitpid()
или же read()
, Смотрите эту страницу для более подробной информации: http://austingroupbugs.net/view.php?id=529 На Linux, Solaris, BSD и других, повторная попытка close()
это неверно. HP-UX — единственная распространенная (!) Система, которую я могу найти, которая требует этого.
EINTR означает что-то совсем другое для read()
а также select()
а также waitpid()
и так далее, чем для close()
, Для большинства вызовов вы повторяете EINTR, потому что вы просили что-то сделать, какие блоки, и если вас прервали, значит, этого не произошло, поэтому попробуйте снова. За close()
запрошенное вами действие состояло в том, чтобы удалить запись из таблицы fd, что происходит мгновенно, без ошибок и всегда будет происходить независимо от того, что close()
возвращается. [*] Единственная причина close()
Блоки в том, что иногда для особой семантики (например, задержка TCP) он может подождать, пока ввод / вывод будет выполнен, прежде чем вернуться. Если close возвращает EINTR, это означает, что вы попросили его подождать, но это не удалось. Тем не мение, ФД все еще был закрыт; Вы просто потеряли шанс подождать.
Вывод: если вы не знаете, что не можете получать сигналы, используя close()
ждать очень глупо. Используйте ACK на уровне приложения (TCP) или fsync (файловый ввод / вывод), чтобы убедиться, что все записи были завершены перед закрытием fd.
Предполагая, что вам нужен более короткий код, вы можете попробовать что-то вроде:
while (((rc = close (fd)) == -1) && (errno == EINTR));
if (rc == -1)
complainBitterly (errno);
Если вы хотите больше удобочитаемый Код в дополнение к короче, просто создайте функцию:
int closeWithRetry (int fd);
и поместите туда свой читаемый код. Тогда на самом деле не имеет значения, как долго это будет, это все еще одна строка, где вы ее вызываете, но вы можете сделать тело самой функции очень читабельным:
int closeWithRetry (int fd) {
// Initial close attempt.
int rc = close (fd);
// As long as you failed with EINTR, keep trying.
// Possibly with a limit (count or time-based).
while ((rc == -1) && (errno == EINTR))
rc = close (fd);
// Once either success or non-retry failure, return error code.
return rc;
}