LINUX.ORG.RU

Как удалить файл только если он не открыт более никем?

 , ,


1

1

Пишу приложение, работающее в режиме «демона».

Использую PID-файл и как блокировку от повторного запуска, и как просто файл, содержащий PID процесса.

В принципе мне бы хватило алгоритма «сделал flock(LOCK_EX) после форка - сделал flock(LOCK_UN) на выходе». Для самого Perl'а этого бы хватило за глаза. Но для удобства и для порядку так сказать хотелось бы невозможного: закрывать PID-файл и затем удалять его.

Есть опасения race conditions: я могу закрыть файл, потом следующий мой инстанс получит управление и откроет его, после чего я возьму и удалю уже открытый файлик. Таким образом, будет существовать инод, содержащий PID следующего инстанса, но у файла уже не будет записи в каталоге и с точки зрения Shell-скриптов, например, PID-файл уже не будет существовать.

Сейчас попробую вариант «открыть файл - блокировать хэндлер - удалить файл - разблокировать хэндлер - закрыть файл». Выглядит последовательность странновато, но наверное должна работать...

А вообще кто что думает по этому поводу?

★★★★★

Последнее исправление: DRVTiny (всего исправлений: 1)

А зачем ты удалишь pid-файл живого процесса?
Удаляй сразу при завершении процесса. В чём, собственно, проблема?

Для надёжности, перед удалением можно заглянуть в него и проверить, свой ли это pid и удалять только если свой, если уж стартанули не глядя на существование pid-файла. Но тогда, наверно, уже поздно пить боржоми - второй процесс у тебя уже наделал делов, а не просто заблудился в pid-файлах.

blexey ★★★★★
()
Последнее исправление: blexey (всего исправлений: 1)

Вроде никак надёжно не сделать с удалением.

Надёжно - делать flock и проверять не только, что файл существует, но и блокировка жива:

flock -n /dir/my.lock true

disarmer ★★★
()
Ответ на: комментарий от blexey

Удаляй сразу при завершении процесса. В чём, собственно, проблема?

В том, чтобы удалить сразу по завершении процесса - и есть проблема!

Я могу запросто удалить файл, который был создан между flock(LOCK_UN) и выполнением unlink, это же обычный race condition.

DRVTiny ★★★★★
() автор топика
Последнее исправление: DRVTiny (всего исправлений: 1)
Ответ на: комментарий от disarmer

Кстати, это... попробовал делать unlink до close файла, открытого в режиме «append», оно же «>>» - так вроде работает, и пока я не могу придумать возможные побочные нехорошие эффекты такого подхода.

Собственно простейший тест:

[myhost]$ perl -e 'print "My pid is $$\n"; open my $f, ">", "/tmp/1.txt"; print $f "something\n"; close $f; open $f, ">>", "/tmp/1.txt"; flock $f, 2; print $f "hello\n"; system(qq(ls -l /proc/$$/fd/)); system(qq(xargs -0 </proc/$$/cmdline)); sleep 2; unlink("/tmp/1.txt") or die "cant unlink: $!"; system(qq(ls -l /proc/$$/fd/)); flock $f,8; close $f'
My pid is 31022
total 0
lrwx------ 1 a.konovalov a.konovalov 64 Sep 22 16:19 0 -> /dev/pts/1
lrwx------ 1 a.konovalov a.konovalov 64 Sep 22 16:19 1 -> /dev/pts/1
lrwx------ 1 a.konovalov a.konovalov 64 Sep 22 16:19 2 -> /dev/pts/1
l-wx------ 1 a.konovalov a.konovalov 64 Sep 22 16:19 3 -> /tmp/1.txt
lr-x------ 1 a.konovalov a.konovalov 64 Sep 22 16:19 4 -> pipe:[450566667]
perl -e print "My pid is $$\n"; open my $f, ">", "/tmp/1.txt"; print $f "something\n"; close $f; open $f, ">>", "/tmp/1.txt"; flock $f, 2; print $f "hello\n"; system(qq(ls -l /proc/$$/fd/)); system(qq(xargs -0 </proc/$$/cmdline)); sleep 2; unlink("/tmp/1.txt"); system(qq(ls -l /proc/$$/fd/)); flock $f,8; close $f
total 0
lrwx------ 1 a.konovalov a.konovalov 64 Sep 22 16:19 0 -> /dev/pts/1
lrwx------ 1 a.konovalov a.konovalov 64 Sep 22 16:19 1 -> /dev/pts/1
lrwx------ 1 a.konovalov a.konovalov 64 Sep 22 16:19 2 -> /dev/pts/1
l-wx------ 1 a.konovalov a.konovalov 64 Sep 22 16:19 3 -> /tmp/1.txt (deleted)
lr-x------ 1 a.konovalov a.konovalov 64 Sep 22 16:19 4 -> pipe:[450566673]

DRVTiny ★★★★★
() автор топика
Последнее исправление: DRVTiny (всего исправлений: 1)
Ответ на: комментарий от DRVTiny

Удалил открытый файл, а в чём соль?

И я не уверен что можно использовать tmpfs, flock из perl работает, но /usr/bin/flock говорит: «flock: bad file descriptor»

disarmer ★★★
()
Ответ на: комментарий от disarmer

Удалил открытый файл, а в чём соль?

В том, что пока процесс запущен - файл существует и в нём pid есть. Как только процесс завершается - PID-файл удаляется.

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от DRVTiny

/tmp/1.txt (deleted)

Он удалён уже и существует в воображении этого процесса до закрытия файла. Другой процесс его в /tmp/1.txt уже не увидит.

И с flock и переоткрытием с append это никак не связано, без них выведет то же самое:

perl -e 'open my $f, ">", "/tmp/1.txt";system(qq(ls -l /proc/$$/fd/));unlink("/tmp/1.txt") or die "cant unlink: $!"; system(qq(ls -l /proc/$$/fd/))'

disarmer ★★★
()

хотелось бы невозможного
кто что думает

Ты в своем репертуаре.

Есть опасения race conditions

Используй consul/etcd/postgres если нужен ACID. Ты еще скажи что доверяешь файловым системам :)

outtaspace ★★★
()
Ответ на: комментарий от disarmer

Тест же не мультитасковый, конечно же он выведет тоже самое :)

DRVTiny ★★★★★
() автор топика

Открывай PID-файл в эксклюзивном режиме (O_CREAT|O_EXCL) и удаляй его до закрытия файлового дескриптора.

При этом, как правильно отметили, файл удалится _сразу_, поэтому делай это в самом конце завершения работы программы, когда все остальные ресурсы освобождены и умирающий инстанс никак не может помешать вновь запускаемому (если он запустится между удалением PID-файла и смертью старого инстанса, это тоже будет своего рода рейс).

intelfx ★★★★★
()
Последнее исправление: intelfx (всего исправлений: 3)
Ответ на: комментарий от intelfx

Открывай PID-файл в эксклюзивном режиме (O_CREAT|O_EXCL) и удаляй его до закрытия файлового дескриптора.

Не самая удачная идея. Если его приложение убьют через SIGKILL, он не сможет запуститься ещё раз без обработки существования файла.

Вот этого:

open
lockf (F_TLOCK)
unlink
lockf (F_ULOCK)
close

хватит всем.

kirk_johnson ★☆
()
Последнее исправление: kirk_johnson (всего исправлений: 1)
Ответ на: комментарий от kirk_johnson

Оу, мне почему-то подумалось, что O_EXCL — это взаимоисключение при открытии, а не при создании. Конечно, ты прав.

intelfx ★★★★★
()
Ответ на: комментарий от DRVTiny

Спасибо огромное, отличный совет!

Чувак, это аксимы программирования под UNIX. Если ты даже этого не знаешь, я не очень понимаю, что ты вообще собрался писать.

kirk_johnson ★☆
()

P.S. Забыл сказать — если твое приложение делает fork(), то тебе нужен flock. Потому что lockf не переживает close'а. Даже если дескрипторов два (один в родительском, один в потомке). POSIX то ещё дерьмище, да.

kirk_johnson ★☆
()
Ответ на: комментарий от kirk_johnson

Спасибо! :)

Порка ещё не делает fork, но всё к тому идёт, ибо AnyEvent внутрях своих именно этим и занимается (хотя есть люди, далёкие от системного программирования, которые реально думают, что в коде AnyEvent происходят какие-то чудеса, а не совершенно банальные fork'и).

DRVTiny ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.