第一节:强制卸载"device/target is busy"的linux文件系统/设备分区
本节翻译自:Forcing Linux to Unmount a Filesystem Reporting “device is busy”
一、引言:
当运行sudo umount /dev/sdxx
命令时可能会报"device is busy",这个合理的报错可以防止正在使用的设备上的数据丢失。如1)使用者自己清楚确实发生了错误 或 2)使用者不在乎数据丢失 或 3)例如NFS这样的服务dead,那么这个问题怎么处理?
二、原因分析:
总概: 程序或内核线程访问了分区设备/文件系统
举例:1)终端占用: 卸载设备时某个终端正在占用该设备的挂载路径
2)文件访问: 卸载设备时设备上的某个文件被记事本或者其他程序正在访问
三、解决方案:
方案0: 如果你的目的是重新挂载文件系统,请使用mount命令的remount参数
sudo mount -o remount /dev/sdc2 //例如重新挂载sdc2分区设备
方案1: 使用umount命令的 -f 选项强制卸载
sudo umount -f /dev/sdc2 //-f选项会立即卸载分区
//也即:如果分区上的某个文件用文件编辑器打开时卸载掉该分区,则卸载后保存该文件时会提示路径找不到
方案2: kill掉正在使用该分区设备的应用程序,然后再卸载设备
- 法1: 先
sudo lsof /mnt/data
查找进程,再kill或kill -9
杀掉进程 (假设/mnt/data是挂载点) - 法2: 先
fuser /mnt/data
查找进程,再kill进程 (fuser的 -k 参数可以在查找进程的同时杀掉该进程)
四、总结:
如果上述方法无法解决问题,可以采用重启系统的方法。
第二节:“device/target is busy” 是怎么来的?
一、问题复现:
插入U盘 -> 用终端或者其他其他软件访问U盘的挂载目录或者U盘内的文件 -> 终端执行dbus-monitor
命令 -> 使用umount
命令或者图形工具卸载/弹出U盘 -> 在报错的同时查看dbus-monitor的输出。(截取其中一段输出如下:)
signal time=1611215781.457437 sender=:1.25 -> destination=:1.42 serial=375 path=/org/gtk/Private/RemoteVolumeMonitor; interface=org.gtk.Private.RemoteVolumeMonitor; member=MountOpShowProcesses
string "org.gtk.vfs.UDisks2VolumeMonitor"
string "2001610:19"
string "卷被占用
一个或更多应用程序正在占用卷。"
array [
int32 2032244
]
array [
string "无论如何弹出"
string "取消"
]
二、问题分析:
1、根据上述"org.gtk.vfs.UDisks2VolumeMonitor"字符串查询提供此输出的二进制软件包为gvfs-daemons
,对应的源码包为gvfs
dpkg -S /usr/share/dbus-1/services/org.gtk.vfs.UDisks2VolumeMonitor.service //deb系
rpm -qf /usr/share/dbus-1/services/org.gtk.vfs.UDisks2VolumeMonitor.service //rpm系
2、在gvfs源码monitor/udisks2/gvfsudisks2mount.c
中跟踪"一个或更多应用程序正在占用卷。" 和 “无论如何弹出” 字符串可找到如下代码
//函数名: lsof_command_cb
choices[0] = _("Eject Anyway");
}
else
{
choices[0] = _("Unmount Anyway");
}
choices[1] = _("Cancel");
message = _("Volume is busy\n"
"One or more applications are keeping the volume busy.");
g_signal_emit_by_name (data->mount_operation, //glib2 发射信号
"show-processes",
message,
processes,
choices);
继续跟踪函数lsof_command_cb
可以在同一个C文件中找下如下代码
escaped_mount_point = g_strescape (mount_point, NULL); //对字符串进行处理
gvfs_udisks2_utils_spawn (10, /* timeout in seconds */
g_task_get_cancellable (task),
lsof_command_cb, //传递lsof_command_cb作为内部的回调函数
g_object_ref (task),
"lsof -t \"%s\"", //调用lsof命令
escaped_mount_point); //传递U盘的挂载目录作为lsof命令的参数
3、由上述可知(结论): lsof -t U盘挂载目录
命令的输出不为空则表示U盘设备处于busy
状态
第三节: lsof命令的不足与解决
- 终端占用挂载目录、vim访问挂载目录内的文件 时是可以被lsof命令检测到的
- 使用pluma文本编辑器程序、eom图片查看器等访问挂载目录下的文件 时lsof命令无法检测
检测命令为: lsof U盘挂载目录
解决:既然底层库也使用的是命令处理的,那么为了解决lsof的不足,可以在代码中使用如下命令进行判断
ps aux|grep U盘挂载目录|grep -v grep //经过验证这个也不能100%解决问题
//<div style=visibility: none">