Если закрытие прерывается или терпит неудачу, каково состояние fd? - PullRequest
1 голос
/ 15 июля 2011

Чтение справочной страницы для закрытия, если она прерывается сигналом, тогда состояние fd не указывается. Существует ли наилучшая практика для обработки этого случая, или предполагается, что она впоследствии станет проблемой ОС.

Я предполагаю, что сбой после EIO закрывает fd соответствующим образом.

1 Ответ

1 голос
/ 14 марта 2012

Если вы хотите, чтобы ваша программа работала долгое время, возможная утечка файлового дескриптора никогда не будет проблемой только операционной системы.Краткосрочные программы, которые не используют много файловых дескрипторов, конечно, имеют возможность завершать дескрипторы незамкнутыми и полагаться на то, что ядро ​​закрывает их после завершения программы.Итак, в остальной части моего ответа я буду считать, что ваша программа работает долго.

Если ваша программа не многопоточная, у вас очень простая ситуация:

int close_some_fd(int fd, int *was_interrupted) {
  *was_interrupted = 0;
  /* this is point X to which I will draw your attention later. */
  for (;;) {
    if (0 == close(fd))
      return 0; /* Success. */
    if (EIO != errno)
      return -1; /* Some failure, not interrupted by a signal. */

    /* Our close attempt was interrupted. */
    *was_interrupted = 1;
    int fdflags = 0;
    /* Just use the fd to find out if it is still open. */
    if (0 != fcntl(fd, F_GETFD, &fdflags))
      return 0;  /* Interrupted, but file is also closed. So we are done. */
  }
}

С другой стороны, если ваш код многопоточный, другой поток (и, возможно, тот, который вы не контролируете, например, кэш службы имен) может вызвать dup, dup2, socket, open, accept или какая-либо другая аналогичная функция, которая заставляет ядро ​​выделять дескриптор файла.

Чтобы сделать подобный подход работающим в такой среде, вам нужно будет определить разницу между дескриптором файла, который выи файловый дескриптор, вновь открытый другим потоком.Знание того, что уже существует файл с меньшим номером, который все еще не открыт, достаточно, чтобы обесценить его, но в многопоточной среде у вас нет простого способа выяснить, что это все еще так.

Один из вариантов - полагаться на некоторые общие аспекты всех дескрипторов файлов, с которыми работает ваша программа.Например, если он никогда не использует O_CLOEXEC, вы можете использовать fcntl, чтобы установить флаг O_CLOEXEC в точке, помеченной X в коде, а затем просто измените существующий вызов на fcntl, например:

    if (0 = fcntl(fd, F_GETFD, &fdflags)) {
      if (fdflags & O_CLOEXEC) {
        /* open, and still marked with O_CLOEXEC, unused elsewhere. */
        continue;
      } else {
        return 0;  /* Interrupted, but file is also closed. So we are done. */
      }
    }

Вы можете настроить этот подход, чтобы использовать что-то отличное от O_CLOEXEC (возможно, fstat, например, запись st_dev и st_ino), но если вы не можете быть уверены, что остальные вашиМногопоточная программа делает, эта общая идея, вероятно, будет неудовлетворительной.

Есть другой подход, который также немного проблематичен, но может послужить.Это означает, что вместо закрытия дескриптора файла вы используете sendmsg для передачи дескриптора файла через сокет домена Unix на отдельный однопоточный сервер специального назначения, единственной задачей которого является закрытие дескриптора файла.Да, это немного неприлично.Некоторые интересные свойства этого подхода:

  1. Во избежание неопределенности относительно того, был ли ваш fd действительно передан на сервер и успешно закрыт, вам, вероятно, следует прочитать из обратного канала fd-closed-OK, сообщения возвращаются с сервера.Это избавляет от необходимости блокировать доставку сигнала, пока вы выполняете sendmsg.Однако это означает издержки переключения контекста в пользовательском пространстве для каждого закрытия файлового дескриптора (если только вы не пакетируете их для амортизации стоимости).Вам нужно избегать ситуации, когда поток A может читать отчет fd-closed-OK, соответствующий запросу, сделанному из потока B. Вы можете избежать этой проблемы, сериализовав операции закрытия (что ограничит производительность) или демультиплексировав ответы (что являетсясложный).В качестве альтернативы, вы могли бы использовать какой-то другой механизм IPC, чтобы избежать необходимости сериализации или демультиплексирования (например, семафоры SYSV).
  2. Для процесса с большими объемами это приведет к очень высокой нагрузке на сборщик мусора дескриптора файла ядра.аппарат, который обычно не сильно подвержен стрессу и поэтому может вызывать у вас некоторые интересные симптомы.

Что касается того, что я буду делать лично в приложениях, с которыми я работаю чаще всего, я бы выяснил, в какой степени я мог бы делать предположения о том, какой вид дескриптора файла я закрывал.Если, например, они обычно были сокетами, я бы просто попробовал это с помощью тестовой программы и выяснил, оставляет ли EIO дескриптор файла закрытым или нет.То есть определить, является ли теоретически неопределенное состояние файлового дескриптора на EIO на практике предсказуемым.Это не будет работать хорошо, если fd может быть чем угодно (например, файл на диске, сокет, tty, ...).Конечно, если вы используете какую-то операционную систему с открытым исходным кодом, вы можете просто прочитать исходный код ядра и определить возможные последствия.

Конечно, я бы попробовал описанную выше экспериментальную систему добеспокоясь о том, чтобы серверы fd-close могли масштабироваться при закрытии fd.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...