需要检测任何具有特定分区标签的 USB 块设备

问题描述

我很惊讶这被证明很难找到。

我需要使用 python3 检测何时添加(插入)具有特定分区标签的 USB 块设备。

有没有办法使用 pyudev 提供 USB 块设备列表?我如何指定一个带有子系统 =“块”和子系统 =“usb”的过滤器,它们似乎是互斥的过滤器。

插入具有名为“XYZ”的分区的 USB 设备时,我需要运行脚本来挂载它并运行使用该分区上数据的程序。

解决方法

我尝试了太多的变体,从各种 udev 规则、systemd 单元、许多脚本及其组合,但直到我使用以下代码才取得任何成功。 它可以工作,但导致 100% CPU 负载。当我最后在 while 循环中添加睡眠时间时,它根本不再起作用,甚至还阻止了 PCmanFM 自动挂载。

问题出在 usbEvent.py 进程中。我可以从命令行运行它,它工作得很好。它做的第一件事是使用 Popen 调用“grep devName /proc/mounts”以等待自动挂载程序挂载分区。在循环中调用 Popen 并增加一些时间。sleep 消除了 CPU 负担,这是令人惊讶的,因为挂载点在几秒钟内出现。

systemd 运行下面的代码和它产生的 usbEvent.py 进程之间似乎存在一些我不完全理解的相互作用。它们是独立的进程,所以我认为它们应该相互独立。

usbEvent.py 处理程序可以工作,但识别挂载并继续需要更长的时间。当它运行时,它消耗大约 5% 的 CPU,当它完成时只消耗 0.3。为什么超时结束时它没有结束必须是由于 p.communicate,但是如果 p.poll 没有返回 None 过程应该完成并且不应该阻塞......但它确实如此!为什么?

该平台是具有 8GB RAM 和 2021 年 1 月 Raspbery Pi OS 版本的 Raspberry Pi4。

#!/usr/bin/env python3
import os
import time
import subprocess as sp

import pyudev

# This code is run on boot via systemd to detect when
# my custom USB storage device (USB stick,SSD etc) 
# is inserted or removed. It spawns a new process to 
# handle the event.

context = pyudev.Context()
monitor = pyudev.Monitor.from_netlink(context)
monitor.filter_by('block',device_type="partition")


def log_event(action,device):
    devName = device.get('DEVNAME')
    devLabel = device.get('ID_FS_LABEL')
    if devLabel == "MY_CUSTOM_USB":
        sp.Popen(["/home/user/bin/customUSB/usbEvent.py",action,devName,devLabel],stdin=sp.DEVNULL,stdout=sp.DEVNULL,stderr=sp.DEVNULL)


observer = pyudev.MonitorObserver(monitor,log_event)
observer.start()

while True:
#    pass
    time.sleep(0.1)

这是我更改以使其正常工作的 usbEvent.py 处理程序的部分:

# Waits for mount point of "dev" to appear and returns it. Communnicate
def getMountPoint(dev):
    out = ""
    interval = 0.1
    timeout = 5 / interval
    while timeout > 0:
        p = sp.Popen(["grep",dev,"/proc/mounts"],text=True,stdout=sp.PIPE,stderr=sp.PIPE)
        retCode = p.poll()
        if retCode is None:
            time.sleep(interval)
        else:
            out,err = p.communicate()  # This should not block but does!
            if retCode == 0 and len(out) > 0:
                out = out.split()[1]
                break
            else:
                lg.info(f"exit code: {retCode} Error: {err}")
                exit(1)
    if timeout == 0:
        p.terminate()
    return out

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...