Тупик с стадом, вилкой и завершением родительского процесса

У меня довольно сложная программа на питоне. Внутри у него есть система ведения журнала, которая использует эксклюзивный (LOCK_EX) fcntl.flock для управления глобальной блокировкой. По сути, всякий раз, когда сообщение журнала сбрасывается, устанавливается глобальная блокировка файла, сообщение отправляется в файл (отличный от файла блокировки) и глобальная блокировка файла снимается.

Программа также несколько раз разветвляется (после настройки управления логами). Вообще все работает.

Если родительский процесс убит (а дети остаются в живых), я иногда получаю взаимоблокировку. Все программы блокируются на fcntl.flock() навсегда. Попытка получить блокировку извне также блокирует навсегда. Я должен убить детские программы, чтобы решить проблему.

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

Есть ли у flock проблемы с разветвлением? Удерживает ли мертвый родитель блокировку, даже если его больше нет в таблице процессов? Как мне решить эту проблему?


person UsAaR33    schedule 02.02.2012    source источник
comment
Хорошо, я переключился на fcntl.lockf, который обертывает блокировки fcntl (а не flock). Тупики ушли.   -  person UsAaR33    schedule 02.02.2012
comment
Я подозреваю, что это связано с тем, что flock блокирует дескриптор файла (который все еще существует в дочерних процессах), в то время как fcntl использует для блокировки inode/pid. Что странно, так это то, что они не решают, что дети фактически владеют стадом; Почему это так?   -  person UsAaR33    schedule 02.02.2012


Ответы (1)


lsof почти наверняка просто не показывает flock() замков, поэтому отсутствие одного из них ничего не говорит вам о том, есть ли он.

flock() блокировки наследуются через fd-sharing (системный вызов dup() или fork-and-exec, который оставляет файл открытым), и любой, у кого есть общий дескриптор, может разблокировать блокировку, но если блокировка уже удерживается, любая попытка снова заблокировать ее заблокирует. Итак, да, вполне вероятно, что родитель заблокировал дескриптор, а затем умер, оставив дескриптор заблокированным. Затем дочерний процесс также пытается заблокироваться и блокируется, потому что дескриптор уже заблокирован. (То же самое произойдет, если дочерний процесс заблокирует файл, а затем умрет.)

Поскольку блокировки `fcntl()' устанавливаются для каждого процесса, умирающий процесс освобождает все свои блокировки, чтобы вы могли продолжить работу, что вам здесь и нужно.

person torek    schedule 09.03.2012
comment
Спасибо @torek. Вы упомянули ... вполне вероятно, что родитель заблокировал дескриптор, а затем умер, оставив дескриптор заблокированным. Мой вопрос в том, что когда родитель умирает, разве fd не освобождается автоматически? - person kgf3JfUtW; 25.07.2019
comment
@sam: когда процесс умирает, его открытые файлы закрываются. Некоторые блокировки будут сняты в этом случае, а некоторые нет. flock() не будут, если только это не последнее закрытие файла. fcntl(fd, F_SETLK, ...) будут, даже если это не последнее закрытие. Последний здесь определяется совместным использованием дескриптора файла через dup (есть второй тип последнего закрытия, определяемый самим файлом, используемый в основном для закрытия устройства и повторного использования vnode в ОС на основе vnode). Проверьте документацию для конкретного типа блокировки, которую вы используете. - person torek; 25.07.2019