Я пытаюсь размонтировать загруженную файловую систему, в которой многопоточная программа непрерывно выполняет операции ввода-вывода, чтения и записи, из-за чего команда umount завершается ошибкой.
root@ubuntu:~ # umount /mount/v1
umount: /mount/v1: target is busy.
(In some cases useful info about processes that use
the device is found by lsof(8) or fuser(1))
Теперь я попытался завершить все процессы с помощью
/sbin/fuser -m /mount/v1 -k
Но согласноКоманда фьюзера
fuser -k или -K может оказаться неспособным обнаружить и завершить новые процессы, созданные сразу после запуска программы.
что и происходит в моем случае, так как некоторые потоки могли выдать запрос ввода-вывода в одно и то же время. Когда я снова пытаюсь размонтировать файловую систему, она снова говорит, что занята, и это становится циклом.
Мой вопрос в том, как гарантировать, что ни один новый процесс не сможет выполнять чтение/запись в файловую систему после
/sbin/fuser -m /mount/v1 -k
выдается команда, позволяющая корректно размонтировать файловую систему.
решение1
Вот два метода. Если вы не доверяете своим приложениям, вы можете сразу перейти ко второму методу.
1.umount --lazy
Если эти мешающие процессы не chdir()
) в точке монтирования (например, используйте абсолютные пути) и вы знаете, что они в конечном итоге сдадутся, то в Linux вы можете использовать umount --lazy
: ядро отключит отображение файловой системы от древовидной структуры каталогов, сделав ее недоступной для новых обращений, и автоматически размонтирует ее, когда исчезнет последняя ссылка. Ссылка может быть открытым fd, cwd, точкой монтирования внутри (последнее будет плохим)...
Но это не повлияет на процессы/потоки, сохраняющие ссылку «внутри», например, на все еще открытые файловые дескрипторы или имеющиеcwdвнутри. Тогда это будет еще сложнее, так как теперь файловая система больше недоступна, и становится еще сложнее отслеживать нарушающие процессы/потоки (например: fuser -m
больше не будет их находить). Так что каким-то образом вам нужно, чтобы процессы вели себя правильно для этого метода.
2.морозильник cgroup
Альтернативное решение, которое должно подойтиплохо себя ведетпроцессы: сгруппируйте все эти процессы (и потоки) в одну и ту же замороженную cgroup. Заморозьте их все, не давая им добавлять использование ресурсов к точке монтирования (путем разветвления/клонирования/открытия новых fds). Даже если вы пропустите несколько в первом цикле, вы в конечном итоге поймаете все новые в последующих итерациях. Поскольку они теперь заморожены, никакой новой активности не произойдет: теперь вы можете убить их все. Для cgroups v1, уже инициализированных ОС (например:системдилиcgmanager, иначе вам придется придумать, каксмонтировать подсистему cgroup морозильника), что-то вроде этого должно работать. Обратите внимание, что, по-видимому, запись в cgroup.procs
должна включать все потоки, которые должны появляться в, tasks
но были сообщения о ненадежном поведении, поэтому на всякий случай я также перебираю потоки, даже если это, вероятно, не нужно (т. е.: tasks
вероятно, уже правильно заполнен потоками, поэтому for t
цикл избыточен).
mkdir -p /sys/fs/cgroup/freezer/prepareumount
echo FROZEN > /sys/fs/cgroup/freezer/prepareumount/freezer.state
for i in $(seq 1 10); do
for p in $(fuser -m /mount/v1 2>/dev/null); do
echo $p > /sys/fs/cgroup/freezer/prepareumount/cgroup.procs
for t in $(ps -L -o tid= -p $p); do
echo $t > /sys/fs/cgroup/freezer/prepareumount/tasks
done
done
done
#give time to an overloaded kernel to freeze everything (FREEZING->FROZEN)
while ! cat /sys/fs/cgroup/freezer/prepareumount/freezer.state | grep -q FROZEN; do
sleep 0.1
done
# kills, delayed
for i in $(cat /sys/fs/cgroup/freezer/prepareumount/cgroup.procs); do
kill -KILL $i
done
# actual kills happen now, at once
echo THAWED > /sys/fs/cgroup/freezer/prepareumount/freezer.state
sleep 1
umount /mount/v1
Это необходимо будет адаптировать при использовании cgroups v2.