有关如何在 Linux 中编写基本 udev 规则的教程
客观的
了解 udev 背后的基本概念,并学习如何编写简单的规则
要求
根权限
困难
中等的
惯例
# – 需要以root权限执行给定的linux命令
直接以 root 用户身份或使用 sudo 命令$ – 要求以常规非特权用户身份执行给定的 Linux 命令
介绍
在 GNU/Linux 系统中,虽然设备低级支持是在内核级别处理的,但与它们相关的事件的管理是由 udev
在用户空间中管理的,更准确地说是由 udevd
守护进程。学习如何编写应用于这些事件发生的规则对于修改系统的行为并使其适应我们的需求非常有用。
规则是如何组织的
Udev 规则定义在扩展名为 .rules
的文件中。这些文件可以放置在两个主要位置: /usr/lib/udev/rules.d
它是用于系统安装规则的目录,/etc/udev/rules。 d/
保留用于定制规则。
定义规则的文件通常以数字作为前缀命名(例如50-udev-default.rules
),并按词法顺序进行处理,与它们所在的目录无关。安装的文件但是,/etc/udev/rules.d
会覆盖系统默认路径中安装的同名规则。
规则语法
一旦理解了 udev 规则背后的逻辑,它的语法就不是很复杂。规则由两个主要部分组成:“匹配”部分,其中我们使用一系列以逗号分隔的键来定义要应用的规则的条件,以及“操作”部分,我们在其中执行一些操作当条件满足时采取的行动。
一个测试用例
有什么比配置实际规则更好的方式来解释可能的选项呢?例如,我们将定义一条规则,以在连接鼠标时禁用触摸板。显然,规则定义中提供的属性将反映我的硬件。
我们将借助我们最喜欢的文本编辑器在 /etc/udev/rules.d/99-togglemouse.rules
文件中编写规则。规则定义可以跨越多行,但如果是这种情况,则必须在换行符之前使用反斜杠作为行延续,就像在 shell 脚本中一样。这是我们的规则:
ACTION=="add" \
, ATTRS{idProduct}=="c52f" \
, ATTRS{idVendor}=="046d" \
, ENV{DISPLAY}=":0" \
, ENV{XAUTHORITY}="/run/user/1000/gdm/Xauthority" \
, RUN+="/usr/bin/xinput --disable 16"
我们来分析一下。
运营商
首先,对使用的和可能的运算符进行解释:
== 和 != 运算符
==
是相等运算符,!=
是不等运算符。通过使用它们,我们确定要应用的规则,定义的键必须分别匹配或不匹配定义的值。
赋值运算符:= 和 :=
=
赋值运算符用于将值分配给接受值的键。相反,当我们想要分配一个值并且希望确保它不会被其他规则覆盖时,我们使用 :=
运算符:事实上,用该运算符分配的值不能被其他规则覆盖。改变了。
+= 和 -= 运算符
+=
和 -=
运算符分别用于在为特定键定义的值列表中添加或删除值。
我们使用的按键
现在让我们分析一下规则中使用的键。首先,我们有 ACTION
键:通过使用它,我们指定当设备发生特定事件时应用我们的规则。有效值为添加
、删除
和更改
然后,我们使用 ATTRS 关键字来指定要匹配的属性。我们可以使用 udevadm info 命令列出设备属性,并提供其名称或 sysfs 路径:
udevadm info -ap /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.1/0003:046D:C52F.0010/input/input39
Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.
looking at device '/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.1/0003:046D:C52F.0010/input/input39':
KERNEL=="input39"
SUBSYSTEM=="input"
DRIVER==""
ATTR{name}=="Logitech USB Receiver"
ATTR{phys}=="usb-0000:00:1d.0-1.2/input1"
ATTR{properties}=="0"
ATTR{uniq}==""
looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.1/0003:046D:C52F.0010':
KERNELS=="0003:046D:C52F.0010"
SUBSYSTEMS=="hid"
DRIVERS=="hid-generic"
ATTRS{country}=="00"
looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.1':
KERNELS=="2-1.2:1.1"
SUBSYSTEMS=="usb"
DRIVERS=="usbhid"
ATTRS{authorized}=="1"
ATTRS{bAlternateSetting}==" 0"
ATTRS{bInterfaceClass}=="03"
ATTRS{bInterfaceNumber}=="01"
ATTRS{bInterfaceProtocol}=="00"
ATTRS{bInterfaceSubClass}=="00"
ATTRS{bNumEndpoints}=="01"
ATTRS{supports_autosuspend}=="1"
looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2':
KERNELS=="2-1.2"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{authorized}=="1"
ATTRS{avoid_reset_quirk}=="0"
ATTRS{bConfigurationValue}=="1"
ATTRS{bDeviceClass}=="00"
ATTRS{bDeviceProtocol}=="00"
ATTRS{bDeviceSubClass}=="00"
ATTRS{bMaxPacketSize0}=="8"
ATTRS{bMaxPower}=="98mA"
ATTRS{bNumConfigurations}=="1"
ATTRS{bNumInterfaces}==" 2"
ATTRS{bcdDevice}=="3000"
ATTRS{bmAttributes}=="a0"
ATTRS{busnum}=="2"
ATTRS{configuration}=="RQR30.00_B0009"
ATTRS{devnum}=="12"
ATTRS{devpath}=="1.2"
ATTRS{idProduct}=="c52f"
ATTRS{idVendor}=="046d"
ATTRS{ltm_capable}=="no"
ATTRS{manufacturer}=="Logitech"
ATTRS{maxchild}=="0"
ATTRS{product}=="USB Receiver"
ATTRS{quirks}=="0x0"
ATTRS{removable}=="removable"
ATTRS{speed}=="12"
ATTRS{urbnum}=="1401"
ATTRS{version}==" 2.00"
[...]
以上是运行命令后收到的截断输出。您可以从输出本身中读取它,udevadm
从我们提供的指定路径开始,并向我们提供有关所有父设备的信息。请注意,设备的属性以单数形式报告(例如 KERNEL
),而父属性以复数形式报告(例如 KERNELS
)。父信息可以是规则的一部分,但一次只能引用其中一个父设备:混合不同父设备的属性将不起作用。在我们上面定义的规则中,我们使用了一个父设备的属性:idProduct
和 idVendor
。
我们在规则中所做的下一件事是使用 ENV
关键字:它可用于设置或尝试匹配环境变量。我们为 DISPLAY
和 XAUTHORITY
分配了一个值。当以编程方式与 X 服务器交互时,这些变量是必不可少的,以设置一些所需的信息:使用 DISPLAY 变量,我们指定服务器在哪台机器上运行、我们引用的显示器和屏幕,以及通过 XAUTHORITY
,我们提供包含 Xorg 身份验证和授权信息的文件的路径。该文件通常位于用户的“home”目录中。
最后我们使用了RUN
关键字:这用于运行外部程序。非常重要:这不会立即执行,而是在解析完所有规则后执行各种操作。在本例中,我们使用 xinput
实用程序来更改触摸板的状态。我不会在这里解释 xinput 的语法,这会断章取义,只需注意 16
是触摸板的 id。
设置规则后,我们可以使用 udevadm test 命令进行调试。这对于调试很有用,但它并不真正运行使用 RUN 键指定的命令:
$ udevadm test --action="add" /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.1/0003:046D:C52F.0010/input/input39
我们向命令提供的是要模拟的操作(使用 --action
选项)和设备的 sysfs 路径。如果没有报告错误,我们的规则应该可以正常运行。要在现实世界中运行它,我们必须重新加载规则:
# udevadm control --reload
此命令将重新加载规则文件,但是仅对新生成的事件有效。
我们已经了解了用于创建 udev 规则的基本概念和逻辑,但是我们只触及了许多选项和可能设置的表面。 udev 联机帮助页提供了详尽的列表:请参阅它以获取更深入的知识。