[Решено] Как грамотно работать с lock-файлами?

Модератор: Модераторы разделов

Аватара пользователя
Bizdelnick
Модератор
Сообщения: 18483
Статус: grammatikführer
ОС: Debian GNU/Linux

[Решено] Как грамотно работать с lock-файлами?

Сообщение Bizdelnick »

Язык не важен, возьмём для примера shell.
Задача, вроде как, типовая: нужно избежать гонки при одновременном запуске нескольких экземпляров программы. Для этого создаём lock-файл и блокируем его (вариант без блокировки не рассматриваем: файл может остаться после падения программы, это не должно застопорить работу).

Код: Выделить всё

#!/bin/sh

do_something() {
        {
                flock 3
                echo started "$@"
                sleep 1
                echo finished "$@"
        } 3>something.lock
}

do_something 1 &
do_something 2 &
do_something 3 &

sleep 2

do_something 4 &

wait
Всё хорошо:

Shell

% ./test.sh
started 1
finished 1
started 2
finished 2
started 3
finished 3
started 4
finished 4
% ./test.sh
started 1
finished 1
started 2
finished 2
started 3
finished 3
started 4
finished 4
% ./test.sh
started 2
finished 2
started 1
finished 1
started 3
finished 3
started 4
finished 4
%
За одним исключением: после работы остаётся ненужный lock-файл. Как его грамотно удалить, если это в принципе возможно?
А может быть, я вообще всё делаю неправильно?
Добавлено (22.05.2021 20:19):
TL;DR: всё сделано правильно, удалять созданный таким образом лок-файл нельзя.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:

Аватара пользователя
Hephaestus
Сообщения: 3729
Статус: Многоуважаемый джинн...
ОС: Slackware64-14.1/14.2

Re: Как грамотно работать с lock-файлами?

Сообщение Hephaestus »

Bizdelnick писал:
18.05.2021 17:57
Задача, вроде как, типовая: нужно избежать гонки при одновременном запуске нескольких экземпляров программы. Для этого создаём lock-файл и блокируем его
Не очень понятно. Вообще-то lock-файл как раз препяствует запуску нескольких экземпляров программы.
Bizdelnick писал:
18.05.2021 17:57
вариант без блокировки не рассматриваем: файл может остаться после падения программы, это не должно застопорить работу
Это сводит на нет всю идею.
Bizdelnick писал:
18.05.2021 17:57
За одним исключением: после работы остаётся ненужный lock-файл.
Корректное завершение программы предполагает удаление lock-файла.
Оставшийся lock-файл косвенно свидетельствует о некорректном завершении.

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

Похожая ситуация наблюдается и с файлами сокетов.
Пускай скрипят мои конечности.
Я - повелитель бесконечности...
Мой блог
Спасибо сказали:

Аватара пользователя
Bizdelnick
Модератор
Сообщения: 18483
Статус: grammatikführer
ОС: Debian GNU/Linux

Re: Как грамотно работать с lock-файлами?

Сообщение Bizdelnick »

Hephaestus, вариант без блокировки файла мне в данном случае неинтересен. Да и как его можно было бы реализовать на чистом шелле, полностью избежав гонки, я всё равно не представляю.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:

Аватара пользователя
Hephaestus
Сообщения: 3729
Статус: Многоуважаемый джинн...
ОС: Slackware64-14.1/14.2

Re: Как грамотно работать с lock-файлами?

Сообщение Hephaestus »

Bizdelnick
Вы можете привести пример программы, которая позволяла бы запуск нескольких экземпляров и при этом использовала бы lock-файл? Я просто не очень понимаю, зачем нужен lock-файл, в этом случае. Что он "лочит"?
Соответственно, я таких не видел. Разве что программы используют lock-файл "втихаря" и я просто этого не замечаю.
Пускай скрипят мои конечности.
Я - повелитель бесконечности...
Мой блог
Спасибо сказали:

Аватара пользователя
Bizdelnick
Модератор
Сообщения: 18483
Статус: grammatikführer
ОС: Debian GNU/Linux

Re: Как грамотно работать с lock-файлами?

Сообщение Bizdelnick »

Hephaestus писал:
18.05.2021 19:26
Вы можете привести пример программы
Я его привёл в первом сообщении.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:

Аватара пользователя
Hephaestus
Сообщения: 3729
Статус: Многоуважаемый джинн...
ОС: Slackware64-14.1/14.2

Re: Как грамотно работать с lock-файлами?

Сообщение Hephaestus »

Bizdelnick писал:
18.05.2021 20:02
Я его привёл в первом сообщении.
Ну, ясно же, что при таком раскладе lock-файл не удалится.
Поскольку там перенаправление в файл, последний вызов функции опять этот файл создаст.

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

В Вашем скрипте lock-файл создан шеллом. Шеллом и удаляйте. По большому счету, flock тут вообще сбоку. Он про lock-файл скорее всего и знать не знает, поскольку работает с дескриптором.
Пускай скрипят мои конечности.
Я - повелитель бесконечности...
Мой блог
Спасибо сказали:

Аватара пользователя
Bizdelnick
Модератор
Сообщения: 18483
Статус: grammatikführer
ОС: Debian GNU/Linux

Re: Как грамотно работать с lock-файлами?

Сообщение Bizdelnick »

Hephaestus, понимаете ли, какое дело. Если я задаю вопрос, я хотел бы получить ответ от кого-то, кто разбирается в теме по крайней мере не хуже меня. Поэтому у меня к Вам большая просьба: если Вам непонятно, о чём идёт речь в вопросе, лучше пройдите мимо.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:

Аватара пользователя
Bizdelnick
Модератор
Сообщения: 18483
Статус: grammatikführer
ОС: Debian GNU/Linux

Re: Как грамотно работать с lock-файлами?

Сообщение Bizdelnick »

Поизучал в качестве примера, как работает с lock-файлами apt. По сути, точно так же (за исключением того, что использует не flock, а fcntl, конечно). Файлы не удаляет.

Shell

% ls -l /var/cache/apt/archives/lock
-rw-r----- 1 root root 0 апр 27 2011 /var/cache/apt/archives/lock
%
В принципе, оно и логично. Если рассматривать lock-файл как «именованный мьютекс», мы же обычные мьютексы не пересоздаём по мере необходимости.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:

Аватара пользователя
olecya
Сообщения: 477
ОС: debian, fedora (i3-wm)

Re: Как грамотно работать с lock-файлами?

Сообщение olecya »

Немного поэкспериментировала и вот такую конструкцию составила:

Код: Выделить всё

#!/bin/sh

exec {myfd}> something.lock

do_something() {
        {
                flock --verbose $myfd
                echo started "$@"
                sleep 1
                echo finished "$@"
	} {myfd}>{myfd}
}

do_something 1 &
do_something 2 &
do_something 3 &

sleep 2

do_something 4 &

Shell

./mutex.sh
flock: getting lock took 0.000012 seconds
started 1
finished 1
flock: getting lock took 1.004240 seconds
started 2
finished 2
flock: getting lock took 2.008354 seconds
started 3
finished 3
flock: getting lock took 1.008141 seconds
started 4
finished 4
Хочу заметить, в выводе команды я ничего не перепутала!
Добавлено (13:05):
Уы, не сработало создается файл '{myfd}'
Добавлено (13:08):
olecya писала:
20.05.2021 13:00
} {myfd}>{myfd}
Только так

Код: Выделить всё

} {myfd}>something.lock
Что делает конструкцию бесполезной
Добавлено (13:20):
Ладно, вот безфайловый вариант:

Код: Выделить всё

#!/bin/sh

do_something() {
        {
                flock --verbose 3
                echo started "$@"
                sleep 1
                echo finished "$@"
        } 3>/dev/null
}

do_something 1 & 
do_something 2 & 
do_something 3 & 

sleep 2

do_something 4 & 

wait
Добавлено (13:29):
Вот вроде нормальный вариант, не надо задавать число

Код: Выделить всё

exec {myfd}> /dev/null

do_something() {
        {
                flock --verbose $myfd
                echo started "$@"
                sleep 1
                echo finished "$@"
        } {myfd}>/dev/null
}
Спасибо сказали:

Аватара пользователя
Bizdelnick
Модератор
Сообщения: 18483
Статус: grammatikführer
ОС: Debian GNU/Linux

Re: Как грамотно работать с lock-файлами?

Сообщение Bizdelnick »

olecya писала:
20.05.2021 13:00
Ладно, вот безфайловый вариант
/dev/null тоже файл. Да и какой смысл в бесфайловом варианте, если бы он и был возможен? Нужна ведь блокировка между процессами.
А вот за конструкцию {var}>file спасибо, не знал, что в баше так можно. Как раз пригодится в buildp.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:

Аватара пользователя
Bizdelnick
Модератор
Сообщения: 18483
Статус: grammatikführer
ОС: Debian GNU/Linux

Re: [Решено] Как грамотно работать с lock-файлами?

Сообщение Bizdelnick »

Bizdelnick писал:
20.05.2021 13:37
за конструкцию {var}>file спасибо, не знал, что в баше так можно
Можно, ага. Только он в таком случае файл не закрывает. Соответственно, дескрипторы текут и блокировка не отпускается. Смахивает на баг. Но это уже предмет для отдельной темы
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:

Аватара пользователя
olecya
Сообщения: 477
ОС: debian, fedora (i3-wm)

Re: [Решено] Как грамотно работать с lock-файлами?

Сообщение olecya »

Bizdelnick
У меня работает даже из разных файлов:
cat mutex1.sh:

Код: Выделить всё

#!/bin/bash

#exec {var}>&2
var=11

myfunc() {
	{
		flock ${var}
		echo hi
		echo $@
		sleep 5
		echo bye
	} {var}>/dev/null
}

myfunc 1 &
myfunc 2 &
myfunc 3 &

sleep 5
myfunc 4 &

wait

Shell

cp mutex1.sh mutex2.sh
Для чистоты эксперимента поставлю значение во втором скрипте var=12
Открываю второй терминал
В одном:

Shell

./mutex1.sh
Во втором:

Shell

./mutex2.sh
У меня отработало первые три функции из первого скрипта, потом 3 из второго и закончила 4 из первого и 4 из второго
значит все открывает и закрывает
Так же с отдельным файлом вместо /dev/null
P.S Извиняюсь, не сходила по ссылке
Спасибо сказали:

Аватара пользователя
Bizdelnick
Модератор
Сообщения: 18483
Статус: grammatikführer
ОС: Debian GNU/Linux

Re: [Решено] Как грамотно работать с lock-файлами?

Сообщение Bizdelnick »

olecya писала:
21.05.2021 16:50
У меня работает даже из разных файлов
Я получил граблями как раз наоборот из-за того, что в одном процессе дважды блокирую один и тот же файл для внесения изменений (раз, два). При замене на синтаксис {users_fd}<> "$LOWER.users" ни с того ни с сего вылез дедлок.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:

Аватара пользователя
olecya
Сообщения: 477
ОС: debian, fedora (i3-wm)

Re: [Решено] Как грамотно работать с lock-файлами?

Сообщение olecya »

Вот такое безобразие:

Код: Выделить всё

#!/bin/bash

do_something() {
        {
                flock $fd
                echo started "$@"
                sleep 1
                echo finished "$@"
        } {fd}>something.lock && exec {fd}>&-
}

do_something 1 & 
do_something 2 & 
do_something 3 & 

sleep 2

do_something 4 & 

wait
Спасибо сказали:

Аватара пользователя
Bizdelnick
Модератор
Сообщения: 18483
Статус: grammatikführer
ОС: Debian GNU/Linux

Re: [Решено] Как грамотно работать с lock-файлами?

Сообщение Bizdelnick »

olecya писала:
21.05.2021 20:16
Вот такое безобразие
Спасибо, но нет. Так файл не закроется при прерывании работы внутри блока. А мне ещё надо, чтобы отработал cleanup, в котором тоже блокировка. Хватит с меня дедлоков, идёт лесом этот удобный синтаксис из-за его кривой реализации в баше. Не могли даже поведение korn shell слизать, не выпендриваясь…
Добавлено (21:37):
Bizdelnick писал:
18.05.2021 18:44
вариант без блокировки файла мне в данном случае неинтересен. Да и как его можно было бы реализовать на чистом шелле, полностью избежав гонки, я всё равно не представляю.
Случайно нашёл ненужное мне решение — set -C. То есть как-то так:

Код: Выделить всё

set -C
if echo $$ >$"LOCKFILE"; then
    # делаем работу
    rm "$LOCKFILE"
else
    # ругаемся и ничего не делаем
fi
Не проверял. Просто записываю, чтобы не забыть, ну и вдруг ещё кому пригодится.
Можно и блокирующий режим придумать, наподобие

Код: Выделить всё

set -C
while ! echo $$ > "$LOCKFILE"; do sleep 1; done
set +C

trap "rm '$LOCKFILE'" EXIT

# делаем работу
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали: