Python open() не работает в Windows, когда другой процесс открывает файл

Я наблюдаю некоторое неожиданное поведение в отношении файловых дескрипторов в Windows.

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

Процесс A (двоичный файл Go) открывает доступный только для чтения дескриптор файла для этого файла, а затем использует его для получения эксклюзивной блокировки файла.

Затем процесс B (двоичный файл Python) пытается открыть доступный только для чтения дескриптор файла для того же файла, что приводит к следующему IOError:

IOError: [Errno 13] Permission denied: 'C:\\path\\to\\file.txt'

Когда процесс А отсутствует, процесс Б без проблем открывает файл.

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

Кто-нибудь знает, что может привести к сбою вызова Python open()?

(Если это полезно, я использую реализацию блокировки файлов Go: этот.)


person zeptonaut    schedule 08.02.2018    source источник
comment
Вы даже говорите, что процесс A использует его для создания эксклюзивной блокировки.   -  person Matt_G    schedule 08.02.2018
comment
Я думаю, что мое замешательство связано с тем, что в системах Unix открытие и блокировка файлов являются полностью ортогональными понятиями. Процесс B может открыть файл, который заблокирован исключительно процессом A. Судя по ответу tocode (который я теперь принял), в Windows это не так.   -  person zeptonaut    schedule 08.02.2018


Ответы (1)


Как было предложено в этом ответе, open() выдает IOError либо в том случае, если у процесса нет разрешения на доступ к файлу, либо если файл заблокирован другим процессом.

Чтобы убедиться, что это второй, а не первый файл, вы можете использовать os.access(), чтобы проверить, есть ли у процесса права доступа к файлу.

Например:

if not os.access(FILE_PATH, os.R_OK):
  print "Failure opening file: No read access for %s" % FILE_PATH
  return

try:                                                                                                                                                      
  fd = open(FILE_PATH)
  print "Success opening file"
except IOError:
  print "Failure opening file: Another process holds lock on %s" % FILE_PATH

С flock в системах Unix открытие и блокировка файлов являются отдельными понятиями. Однако LOCK_FILE_EX в системах Windows фактически блокирует открытие другими процессами этой части файла для чтения или записи (источник).

person tocode    schedule 08.02.2018
comment
os.access в данном случае не имеет смысла в Windows. Он проверяет только те атрибуты файла, которые относятся к F_OK и W_OK (атрибут только для чтения). Как написано, это не имеет ничего общего с проверкой прав доступа к файлам. Лучше всего это проверить, вызвав CreateFile напрямую с нужным доступом. Если это не удается с отказом в доступе (5), вы знаете, что вам не разрешен желаемый доступ. Если это не удается с нарушением совместного использования (32), вы знаете, что вам разрешено в целом; однако файл в настоящее время открыт без совместного доступа к желаемому чтению, записи или удалению. - person Eryk Sun; 09.02.2018
comment
Блокировка файлов Windows никоим образом не препятствует открытию файла. Блокировка файла применяется к областям файла и проверяется только при попытке чтения или записи в заблокированную область. Доступ к многократному открытию файла защищен отдельным механизмом совместного использования ввода-вывода, который применяется к файлам, открытым для чтения/выполнения, записи/добавления или удаления. См. создание и открытие файлов, чтобы узнать, как работает режим общего доступа. - person Eryk Sun; 09.02.2018
comment
eryksun, я не уверен, что ваше объяснение объясняет поведение, которое я наблюдаю. С одной программой golang, открывающей файл с доступом для чтения, и одной программой python, открывающей файл с доступом для чтения, я ожидаю, что они обе смогут удерживать параллельные дескрипторы. Однако, похоже, это не так. (Вызов golang выглядит как file, err := os.OpenFile(path, os.O_RDONLY|os.O_CREATE, 0666), а вызов Python — как file = open(path).) - person zeptonaut; 09.02.2018
comment
@candrews, я могу сказать вам, что стандартный Python 2 open вызывает Microsoft C fopen / _wfopen, который в конечном итоге вызывает CreateFile с установленным режимом общего доступа, позволяющим совместное чтение и запись. Навскидку, я не знаю, как golang реализует OpenFile. Если он не устанавливает режим общего доступа, чтобы разрешить совместное чтение, то open в Python завершится ошибкой разрешения. На самом деле это нарушение совместного доступа (winerror 32), а не отказ в доступе (winerror 5), но Microsoft C усложняет нам жизнь, сопоставляя тысячи кодов ошибок Windows примерно с 50 кодами ошибок POSIX, такими как EACCES (13). - person Eryk Sun; 09.02.2018
comment
@candrews, я взглянул на библиотеку блокировки, которую вы используете. Его реализация для Windows не основана на фактической блокировке файлов (т. е. LockFileEx, UnlockFileEx) для блокировки областей файла. Вместо этого он вызывает CreateFile, используя режим общего доступа в качестве «блокировки», если share имеет значение false, и в противном случае разрешает совместное использование чтения и записи (но не удаление). Единственная возможная «разблокировка» в этом случае — закрыть дескриптор файла. - person Eryk Sun; 09.02.2018