Как использование инструкции try исключает условие гонки?
При определении того, существует или нет файл, как использование инструкции try исключает "условие гонки"?
Я спрашиваю, потому что высокий ответ (обновление: оно было удалено), по-видимому, подразумевает, что использование os.path.exists()
создает возможность, которая не существовала бы иначе.
Приведенный пример:
try:
with open(filename): pass
except IOError:
print 'Oh dear.'
Но я не понимаю, как это исключает условие гонки по сравнению с:
if not os.path.exists(filename):
print 'Oh dear.'
Как вызов os.path.exists(filename)
позволяет злоумышленнику что-то делать с файлом, который они еще не могли сделать?
Ответы
Ответ 1
Состояние гонки, конечно, между вашей программой и другим кодом, который работает с файлом (условие гонки всегда требует по крайней мере двух параллельных процессов или потоков, см. подробнее). Это означает, что использование open()
вместо exists()
может действительно помочь только в двух ситуациях:
- Вы проверяете наличие файла, который был создан или удален каким-либо фоновым процессом (однако, если вы запустите внутри веб-сервера, это часто означает, что многие копии вашего процесса работают параллельно с обработкой HTTP-запросов, поэтому для условия участия в веб-приложениях возможны, даже если нет других программ).
- Может быть запущена какая-то вредоносная программа, которая пытается свернуть ваш код, уничтожив файл в моменты, когда вы ожидаете его существования.
exists()
выполняет только одну проверку. Если файл существует, он может быть удален через микросекунду после exists()
возвращен True
. Если файл отсутствует, он может быть создан немедленно.
Однако open()
не просто проверяет существование файла, но также открывает файл (и делает эти два действия атомарно, поэтому между проверкой и открытием ничего не может произойти). Обычно файлы не могут быть удалены, пока они открыты кем-либо. Это означает, что внутри with
вы можете быть абсолютно уверены: файл действительно существует, так как он открыт. Хотя это верно только внутри with
, и файл по-прежнему может быть удален сразу после выхода with
блоков, размещение кода, в котором должен существовать файл внутри with
, гарантирует, что код не будет работать.
Ответ 2
Вот пример использования:
try:
with open('filename') as f:
do_stuff_that_depends_on_the_existence_of_the_file(f)
except IOError as e:
print 'Trouble opening file'
Если вы открываете файл с любым доступом вообще, тогда ОС будет гарантировать, что файл существует, иначе он сбой произойдет с ошибкой. Если доступ является исключительным, любой другой процесс в конфликте для файла будет либо заблокирован вами, либо заблокирует вас.
try
- это просто способ обнаружить ошибку или успех действия открытия файла, поскольку API-интерфейсы ввода-вывода файлов в Python обычно не имеют кодов возврата (вместо этого используются исключения). Чтобы действительно ответить на ваш вопрос, это не try
, что позволяет избежать состояния гонки, это open
. Это в основном то же самое в C (на котором основан Python), но без исключений. Подробнее читайте .
Обратите внимание, что вы, вероятно, захотите выполнить код, который зависит от доступа к файлу внутри блока try. Когда вы закрываете файл, его существование больше не гарантируется.
Вызов os.path.exists
просто дает моментальный снимок в момент времени, когда файл может существовать или не существовать, и вы не знаете о существовании файла после возвращения os.path.exists
. Код Malevolent или неожиданная логика могут удалить или изменить файл, если вы его не ожидаете. Это похоже на поворот головы, чтобы проверить, что дорога чиста, прежде чем ехать в нее. Как только вы вернете голову, у вас нет ничего, кроме предположения о том, что происходит, когда вы больше не смотрите. Сохранение открытого файла гарантирует расширенное согласованное состояние, что невозможно (для хорошего или плохого) во время вождения.:)
Ваше предложение проверить, что файл не существует, а не использовать try/open
, по-прежнему недостаточен из-за характера моментального снимка os.path.exists
. К сожалению, я не знаю, как предотвратить создание файлов в каталоге во всех случаях, поэтому я считаю, что лучше проверить наличие файла, а не его отсутствие.
Ответ 3
Я думаю, что вы спрашиваете, какое конкретное условие гонки:
- открыт файл
- контекст переключается и файл удаляется
- контекст переключается обратно, и попытки файла выполняются в "открывшемся" файле
В этом случае вы можете защитить весь код обработки файлов в блоке try
, если в какой-то момент файл станет недоступным/поврежденным, ваши файловые операции смогут "изящно" выйти из строя, через блок catch
.
Обратите внимание, что, конечно, современная ОС этого не может произойти в любом случае, когда файл "удален", удаление не будет выполняться до тех пор, пока все открытые дескрипторы файла не будут разрешены (выпущены)