Системный вызов, прерванный сигналом, все еще должен быть завершен
много системных вызовов, таких как close( fd )
может прерываться сигналом. В этом случае обычно -1
и errno
установлен EINTR
.
вопрос в том, что это правильно? Скажи, я все еще хочу этого fd
должен быть закрыт.
что я могу придумать-это:
while( close( fd ) == -1 )
if( errno != EINTR ) {
ReportError();
break;
}
может кто-нибудь предложить лучше/более элегантный/стандартный способ справиться с этой ситуацией?
обновление:
Как заметил mux, SA_RESTART
флаг можно использовать когда установка обработчика сигналов.
Может кто-нибудь сказать мне, какие функции гарантированно перезапускаются на всех POSIX
систем(не только Linux
)?
3 ответов
некоторые системные вызовы перезапускаются, что означает, что ядро перезапустит вызов, если прервано, если SA_RESTART
флаг используется при установке обработчика сигнала сигнал(7) man page говорит:
Если заблокированный вызов одного из следующих интерфейсов прерывается обработчиком сигналов,тогда вызов будет автоматически перезапущен после сигнала обработчик возвращает, если использовался флаг SA_RESTART; в противном случае вызов не удастся с ошибкой EINTR:
Он не упоминает, если close()
перезапускается, но это:
read (2), readv( 2), write (2), writev (2), ioctl (2), open (2), wait (2), wait3 (2), wait4(2), waitid (2) и waitpid, accept (2), connect (2), recv (2), recvfrom (2), recvmsg (2), send (2), sendto (2) и sendmsg (2) стадо(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 перезапустить или нет?
Update: Close-это особый случай, он не перезапускается и не должен быть повторен в Linux, см. Этот ответ для получения более подробной информации: https://stackoverflow.com/a/14431867/1157444
для записи: по существу каждый UNIX,close()
не должно повторяться если он возвращает EINTR. НЕ поместите eintr retry-loop на место для закрытия, как вы бы для 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 linger) он может подождать, пока ввод-вывод не будет выполнен, прежде чем возвращаться. Если close возвращает EINTR, это означает, что вы попросили его подождать, но он не смог. Однако,fd все еще был закрыт; вы только что потеряли свой шанс подождать его.
вывод: если вы не знаете, что не можете принимать сигналы, используя close()
ждать-это очень глупо. Используйте ACK уровня приложения (TCP) или fsync (файловый ввод-вывод), чтобы убедиться, что все записи были завершены перед закрытием ФД.
[*] есть оговорка: если другой поток процесса находится внутри блокирующего syscall на том же 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;
}