我正在尝试卸载一个繁忙的文件系统,该文件系统上的多线程程序正在发生连续的 I/O 读取和写入,因此 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
但根据定影命令
fusionr -k 或 -K 可能无法检测并终止程序开始运行后立即创建的新进程。
这就是我的情况,因为某些线程可能同时发出 IO 请求。当我再次卸载文件系统时,它再次说它正忙,这变成了一个循环。
我的问题是,如何确保没有新进程能够对文件系统进行一次读/写操作
/sbin/fuser -m /mount/v1 -k
发出命令以便可以正常卸载文件系统。
答案1
这里有两种方法,如果您不信任您的应用程序,可以直接转到第二种方法。
1.umount --lazy
如果那些干扰进程没有chdir()
)进入安装点(例如使用绝对路径)并且您知道它们最终会放弃,那么在 Linux 上您可以使用umount --lazy
:内核将从目录树状结构中取消文件系统的映射,使其无法访问新的访问并在最后一个引用消失时自动卸载它。引用可以是一个打开的 fd、一个 cwd、一个内部挂载点(最后一个可能会很糟糕)...
但这不会影响进程/线程将引用保留在“内部”,例如仍然打开文件描述符或具有西德里面。那么这将变得更加困难,因为现在文件系统不再可访问,并且跟踪违规进程/线程变得更加困难(例如:fuser -m
将不再找到它们)。因此,您需要进程才能正确执行此方法。
2.冷冻机组
应该适用的替代解决方案行为不好进程:将所有这些进程(和线程)分组到同一个冷冻机 cgroup 中。将它们全部冻结,防止它们将资源使用添加到挂载点(通过分叉/克隆/打开新的 fd)。即使您在第一次循环中错过了一些,您最终也会在后续迭代中捕获所有新内容。由于它们现在已被冻结,因此不会发生新的活动:您现在可以将它们全部杀死。对于 cgroups v1,已经由操作系统初始化(例如:通过系统或者CG经理,否则你必须弄清楚如何安装 freeze 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,则必须对此进行调整。