Ответ 1
Если проблема в том, что слишком много файлов открывается, вам нужно установить флаг FD_CLOEXEC
в дескрипторах файла, чтобы закрыть их, когда произойдет exec
. Вот фрагмент кода, который имитирует удар по ограничению дескриптора файла при перезагрузке и содержит исправление, не допускающее ограничение. Если вы хотите симулировать сбои, установите fixit
на False
. Когда fixit
True
, код проходит через список дескрипторов файлов и устанавливает их как FD_CLOEXEC
. Это работает в Linux. Люди, работающие над системами, не имеющими /proc/<pid>/fd/
, должны найти подходящий для системы способ отображения дескрипторов открытых файлов. Этот question может помочь.
import os
import sys
import fcntl
pid = str(os.getpid())
def fds():
return os.listdir(os.path.join("/proc", pid, "fd"))
files = []
print "Number of files open at start:", len(fds())
for i in xrange(0, 102):
files.append(open("/dev/null", 'r'))
print "Number of files open after going crazy with open()", len(fds())
fixit = True
if fixit:
# Cycle through all file descriptors opened by our process.
for f in fds():
fd = int(f)
# Transmit the stds to future generations, mark the rest as close-on-exec.
if fd > 2: .
try:
fcntl.fcntl(fd, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
except IOError:
# Some files can be closed between the time we list
# the file descriptors and now. Most notably,
# os.listdir opens the dir and it will probably be
# closed by the time we hit that fd.
pass
print "reloading"
python = sys.executable
os.execl(python, python, *sys.argv)
С помощью этого кода, что я получаю на stdout, эти три строки повторяются, пока я не убью процесс:
Number of files open at start: 4
Number of files open after going crazy with open() 106
reloading
Как работает код
Приведенный выше код получает список дескрипторов открытых файлов с помощью функции fds()
. В системе Linux файловые дескрипторы, открытые определенным процессом, перечислены по адресу:
/proc/<process id of the process we want>/fd
Итак, если ваш идентификатор процесса вашего процесса равен 100, и вы делаете:
$ find /proc/100/fd
Вы получите список, например:
/proc/100/fd/0
/proc/100/fd/1
/proc/100/fd/2
[...]
Функция fds()
просто получает базовое имя всех этих файлов ["0", "1", "2", ...]
. (Более общее решение может сразу преобразовать их в целые числа. Я решил не делать этого.)
Вторая ключевая часть - установка FD_CLOEXEC
во всех дескрипторах файлов, кроме std{in,out,err}
. Установка FD_CLOEXEC
в дескрипторе файла сообщает операционной системе, что в следующий раз exec
, ОС должна закрыть дескриптор файла, прежде чем давать управление следующему исполняемому файлу. Этот флаг определен на странице руководства для fcntl.
В приложении, использующем потоки, открывающие файлы, возможно, что код, который у меня выше, пропустил настройку FD_CLOEXEC
для некоторых файловых дескрипторов, если поток выполняется между тем, когда получен список файловых дескрипторов, и время exec
, и этот поток открывает новые файлы. Я считаю, что единственный способ гарантировать, что этого не произойдет, - заменить os.open
кодом, вызывающим запас os.open
, а затем установить FD_CLOEXEC
сразу после возврата дескриптора файла.