Системный вызов, прерванный сигналом, все еще должен быть завершен

много системных вызовов, таких как 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;
}