У меня была такая же проблема.
Мы постоянно используем subprocess.Popen () для вызова внешних инструментов в среде Windows. В какой-то момент у нас возникла проблема, когда больше не было файловых дескрипторов. Мы углубились в проблему и обнаружили, что экземпляры subprocess.Popen ведут себя иначе в Windows, чем в Linux.
Если экземпляр Popen не уничтожен (например, путем сохранения какой-либо ссылки и, следовательно, не позволяя сборщику мусора уничтожить объект), каналы, созданные во время вызова, остаются открытыми в Windows, тогда как в Linux они были автоматически закрыто после вызова Popen.communicate (). Если это будет продолжено при дальнейших вызовах, дескрипторы файлов «зомби» из каналов будут накапливаться и в конечном итоге вызовут исключение Python IOError: [Errno 24] Too many open files
.
Как получить дескрипторы открытых файлов в Python
Для того чтобы мы могли решать наши проблемы, нам нужен был способ получить действительные файловые дескрипторы в скрипте Python. Итак, мы создали следующий скрипт. Обратите внимание, что мы проверяем дескрипторы файлов только от 0 до 100, так как мы не открываем столько файлов одновременно.
fd_table_status.py :
import os
import stat
_fd_types = (
('REG', stat.S_ISREG),
('FIFO', stat.S_ISFIFO),
('DIR', stat.S_ISDIR),
('CHR', stat.S_ISCHR),
('BLK', stat.S_ISBLK),
('LNK', stat.S_ISLNK),
('SOCK', stat.S_ISSOCK)
)
def fd_table_status():
result = []
for fd in range(100):
try:
s = os.fstat(fd)
except:
continue
for fd_type, func in _fd_types:
if func(s.st_mode):
break
else:
fd_type = str(s.st_mode)
result.append((fd, fd_type))
return result
def fd_table_status_logify(fd_table_result):
return ('Open file handles: ' +
', '.join(['{0}: {1}'.format(*i) for i in fd_table_result]))
def fd_table_status_str():
return fd_table_status_logify(fd_table_status())
if __name__=='__main__':
print fd_table_status_str()
При простом запуске он покажет все открытые дескрипторы файлов и их соответствующий тип:
$> python fd_table_status.py
Open file handles: 0: CHR, 1: CHR, 2: CHR
$>
Вывод такой же, вызывая fd_table_status_str () через код Python. Подробнее о значении «CHR» и значении «коротких кодов» см. Документация Python по stat .
.
Проверка поведения дескриптора файла
Попробуйте запустить следующий скрипт в Linux и Windows:
test_fd_handling.py :
import fd_table_status
import subprocess
import platform
fds = fd_table_status.fd_table_status_str
if platform.system()=='Windows':
python_exe = r'C:\Python27\python.exe'
else:
python_exe = 'python'
print '1) Initial file descriptors:\n' + fds()
f = open('fd_table_status.py', 'r')
print '2) After file open, before Popen:\n' + fds()
p = subprocess.Popen(['python', 'fd_table_status.py'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
print '3) After Popen, before reading piped output:\n' + fds()
result = p.communicate()
print '4) After Popen.communicate():\n' + fds()
del p
print '5) After deleting reference to Popen instance:\n' + fds()
del f
print '6) After deleting reference to file instance:\n' + fds()
print '7) child process had the following file descriptors:'
print result[0][:-1]
Вывод Linux
1) Initial file descriptors:
Open file handles: 0: CHR, 1: CHR, 2: CHR
2) After file open, before Popen:
Open file handles: 0: CHR, 1: CHR, 2: CHR, 3: REG
3) After Popen, before reading piped output:
Open file handles: 0: CHR, 1: CHR, 2: CHR, 3: REG, 5: FIFO, 6: FIFO, 8: FIFO
4) After Popen.communicate():
Open file handles: 0: CHR, 1: CHR, 2: CHR, 3: REG
5) After deleting reference to Popen instance:
Open file handles: 0: CHR, 1: CHR, 2: CHR, 3: REG
6) After deleting reference to file instance:
Open file handles: 0: CHR, 1: CHR, 2: CHR
7) child process had the following file descriptors:
Open file handles: 0: FIFO, 1: FIFO, 2: FIFO, 3: REG
Вывод Windows
1) Initial file descriptors:
Open file handles: 0: CHR, 1: CHR, 2: CHR
2) After file open, before Popen:
Open file handles: 0: CHR, 1: CHR, 2: CHR, 3: REG
3) After Popen, before reading piped output:
Open file handles: 0: CHR, 1: CHR, 2: CHR, 3: REG, 4: FIFO, 5: FIFO, 6: FIFO
4) After Popen.communicate():
Open file handles: 0: CHR, 1: CHR, 2: CHR, 3: REG, 5: FIFO, 6: FIFO
5) After deleting reference to Popen instance:
Open file handles: 0: CHR, 1: CHR, 2: CHR, 3: REG
6) After deleting reference to file instance:
Open file handles: 0: CHR, 1: CHR, 2: CHR
7) child process had the following file descriptors:
Open file handles: 0: FIFO, 1: FIFO, 2: FIFO
Как видно на шаге 4, Windows не ведет себя так же, как Linux. Экземпляр Popen должен быть уничтожен, чтобы трубы были закрыты.
Кстати, различие в шаге 7 показывает другую проблему, касающуюся поведения интерпретатора Python в Windows, вы можете увидеть более подробную информацию об обеих проблемах здесь .