使用程序来监测USB存储设备热插拔检测, 主要原理是先熟练使用各系统自带的管理工具及命令使用方法, 先在各平台系统中通过命令获取执行结果, 分析命令返回的结果是否是我们需要的, 在这个过程中可能需要反复尝试和验证。然后通过程序解析命令返回的结果, 在此基础之上再逐步加入自己的功能。
要实现在Windows平台检测USB存储设备的插入, 需要先了解Windows平台的一款工具: WIndows Management Instrumentation Command-line,简称:WMIC, 该工具主要用于检索计算机上逻辑磁盘驱动器的信息, 而满足我们需求的命令如下:
wmic logicaldisk where dirvetype=2 get deviceid
下面解释一下这条命令的参数含义:
综合来讲, 该命令的目的是检索计算机上所有硬盘驱动器的设备标识符,在执行命令后, 我们会得到一个列表, 其中包含了所有硬盘驱动器的设备标识符。例如C:、D:等。
下面这个是我的电脑, 上面已经接入一个U盘外存储设备, 如图:
从图中可以看到, 电脑本身的驱动器盘符为C:和D: , U盘外存储设备的盘符为F:。
以Windows10为例, 打开系统自带的Windows PowerShell, 尝试执行以下命令:
wmic logicaldisk get deviceid
注意, 该命令我没有加上筛选条件:where dirvetype=2, 看看返回什么结果,如图:
从图中可以看到, wmic命令给我们返回了与上面一样的三个盘符: C:、D:和F:
现在我们再加上筛选条件:where drivetype=2 看下结果:
从图中可以看到, 只返回了U盘的盘符F: , 因此, 利用Go语言实现Windows平台的USB存储设备的方法总结如下:
实现上述步骤的参考代码如下:
func DetectWinDevice() error { driveMap := make(map[string]bool) cmd := "wmic" args := []string{"logicaldisk", "where", "drivetype=2", "get", "deviceid"} out, err := exec.Command(cmd, args...).Output() if err != nil { return err } s := bufio.NewScanner(bytes.NewReader(out)) for s.Scan() { line := s.Text() if strings.Contains(line, ":") { rootPath := strings.TrimSpace(line) + string(os.PathSeparator) driveMap[rootPath] = true } } for k := range driveMap { err = filepath.Walk(k, visit) if err != nil { return err } } return nil}
在上面的代码中, 利用Go语言执行命令:wmic logical disk where drivetype=2 get device, 之后按行读取命令返回的结果, 并判断每一行中是否包含冒号, 如果找到则将盘符保存, 该盘符便是USB存储设备的盘符。
将获取的盘符作为根目录,开始进行文件编译, 以下是文件遍历的函数:
func visit(path string, info os.FileInfo, err error) error { if err != nil { return nil } if info.IsDir() { fmt.Printf("Directory: %s\n", path) } else { fmt.Printf("文件路径: %s\n", path) } return nil}
将程序编译运行一下, 成功打印出我的U盘里的所有文件全路径,如图:
在Linux平台中,可以首先使用系统自带的"df"命令查看一下当前所有的文件系统对应的挂载点,。这里我在电脑上安装了虚拟机,在虚拟机中安装了ubuntu系统, 通过虚拟机设置添加USB设备, 在ubuntu系统中通过df命令可以看到加载的U盘设备,如图:
从图中可以看到, U盘存储设备对应的文件系统路径为:/dev/sdb1。那么对于Linux系统中所有的设备类型, 我们怎么知道是USB存储设备呢, 这里需要使用到Linux系统自带的一个名为udevadm的工具, 完整命令如下:
udevadm info -q property -n device
下面详细了解一下udevadm的原理和使用方法。
udev是Linux系统中用于管理设备的守护程序, 它通过监听内核事件并在设备插入、删除或其它事件发生时触发相应的规则来执行操作。udevadm工具用于与udev守护程序进行交互, 提供了一种方便的方式来查询和管理设备信息。
info参数实际是udevadm工具的一个子命令, 用于显示有关指定设备的信息。
-q property这部分参数指定了要查询的信息的类型。在这种情况下, property表示要获取设备的属性信息。
-n device这里的device就是上面的设备路径, 例如:/dev/sdb1, 这部分参数指定要查询信息的设备节点, 在后面的程序实现中,一般都是遍历所有的设备节点, 然后对命令返回结果进行过滤。
在Linux中输入以上命令, 看看命令返回结果:
从上图中可以看到, 有一个关键条目内容为: ID_USB_DRIVER=usb-storage,该条内容可作为识别USB存储器的关键特征。如果感兴趣, 可以自行选择其它设备查看下结果。
到这里, 在Linux系统上检测USB存储设备插入的方法逐渐清晰:
按照以上思路, 检测Linux系统USB存储设备插入的参考代码如下:
func Detect() error { driveMap := make(map[string]bool) dfPattern := regexp.MustCompile("^(\/[^ ]+)[^%]+%[ ]+(.+)$") cmd := "df" out, err := exec.Command(cmd).Output() if err != nil { log.Printf("Error calling df: %s", err) } s := bufio.NewScanner(bytes.NewReader(out)) for s.Scan() { line := s.Text() if dfPattern.MatchString(line) { device := dfPattern.FindStringSubmatch(line)[1] rootPath := dfPattern.FindStringSubmatch(line)[2] if ok := isUSBStorage(device); ok { driveMap[rootPath] = true } } } for k := range driveMap { err = filepath.Walk(k, visit) if err != nil { fmt.Printf("error walking the path %v: %v\n", k, err) } } return nil}
判断是否为USB设备的关键函数代码如下:
func isUSBStorage(device string) bool { deviceVerifier := "ID_USB_DRIVER=usb-storage" cmd := "udevadm" args := []string{"info", "-q", "property", "-n", device} out, err := exec.Command(cmd, args...).Output() if err != nil { log.Printf("Error checking device %s: %s", device, err) return false } if strings.Contains(string(out), deviceVerifier) { return true } return false}
在上面的检测代码中, 我们使用了正则来匹配df命令返回的每行结果, 从结果中取出根路径和设备名, 利用udevadm的ID_USB_DRIVER=usb-storage属性判断其是否为USB设备。
由于Linux系统是一个虚拟机镜像,因此要将代码编译成Linux平台的二进制程序并运行, 当Linux系统能够识别USB设备后, 运行程序, 可以看到能够成功获取U盘上所有文件的全路径,如图:
在MacOS平台中, 有一个名为system_profiler的命令行工具, 该工具可用于获取系统硬件和软件信息的详细报告。以下是system_profiler工具常用的参数和对应的信息类型:
这里我们主要用到获取USB设备信息, 执行以下命令先看下结果:
system_profiler SPUSBDataType
在插入U盘存储设备的情况下,返回信息如下:
这里可以通过匹配Mount Point行内容来作为移动存储设备插入规则, 参考代码如下:
func Detect() error { driveMap := make(map[string]bool) macOSPattern := regexp.MustCompile("^.*Mount Point: (.+)$") cmd := "system_profiler" args := []string{"SPUSBDataType"} out, err := exec.Command(cmd, args...).Output() if err != nil { return err } s := bufio.NewScanner(bytes.NewReader(out)) for s.Scan() { line := s.Text() if macOSPattern.MatchString(line) { d := macOSPattern.FindStringSubmatch(line)[1] driveMap[d] = true } } for k := range driveMap { err = filepath.Walk(k, visit) if err != nil { fmt.Printf("error walking the path %v: %v\n", k, err) } } return nil}
将该代码编译成在macos平台上执行, 结果如下:
另外还有一个命令也能列出磁盘信息,输入:diskutil list 命令,返回结果如下:
从上图可以看到, U盘对应的设备路径为:/dev/disk4, 接着使用命令:diskutil info /dev/disk4, 返回结果如下:
在本文中, 我们分别在Windows、Linux和macOS平台上探索了USB存储设备插入检测的原理及使用Go语言实现自动化检测的方法, 其中,MacOS平台具备多种检测方法, 如何稳定的检测多种USB存储设备还有待进一步测试。虽然初步达到了我们想要的结果, 但因为时间和实验条件有限, 没有对更多的移动存储设备和不同版本的系统做兼容性测试, 以后有机会会顺便做测试, 遇到需要兼容的情况再做整体总结和更新。