第一节:强制卸载"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">
在这里插入图片描述

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐