sysfs

sysfs 是一个内存文件系统,用于将内核的数据结构暴露出来。

任何 kobject 在系统中注册,就会有一个目录在 sysfs 中被创建出来。这个目录是作为 kobject 的父对象所在的目录的子目录创建的。每个 kobject 的属性,会以目录中的普通文件的形式出现。

kobject

Kobject 是一个类型为 struct kobject 的对象,它包含 name, ref count, parent pointer 等,使得 kobject 可以被组织成层次结构,并且通过 sysfs 暴露出来。Kobject 本身没有包含其他具体的功能,它主要通过被嵌入到其他的结构体中来使用。

uevent

Sysfs 的 device 目录下有个 *uevent 文件。uevent 的是 udev event 的缩写。

uevent 这个文件可读可写,当你读这个文件的时候,会返回这个设备最后一次被 kernel 发送的 udev 事件(这里就是这个设备被 add event,remove 的不会存在,因为 remove 发生后,整个设备目录都没了)。

[root@centos8 block]# cat nvme0n1/uevent
MAJOR=259
MINOR=0
DEVNAME=nvme0n1
DEVTYPE=disk
DISKSEQ=1
[root@centos8 block]# cat sdc/uevent
MAJOR=8
MINOR=32
DEVNAME=sdc
DEVTYPE=disk
DISKSEQ=4

uevent 本身是一个 kernel 的机制,用于通知用户态的 udev 程序,关于设备的相关事件。在新的系统中,用户态的程序是在 systemd 中实现的,即 systemd-udevd

[root@centos8 block]# systemctl cat systemd-udevd
# /usr/lib/systemd/system/systemd-udevd.service
#  SPDX-License-Identifier: LGPL-2.1+
#
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=udev Kernel Device Manager
Documentation=man:systemd-udevd.service(8) man:udev(7)
DefaultDependencies=no
After=systemd-sysusers.service systemd-hwdb-update.service
Before=sysinit.target
ConditionPathIsReadWrite=/sys

[Service]
Type=notify
OOMScoreAdjust=-1000
Sockets=systemd-udevd-control.socket systemd-udevd-kernel.socket
Restart=always
RestartSec=0
ExecStart=/usr/lib/systemd/systemd-udevd
KillMode=mixed
WatchdogSec=3min
TasksMax=infinity
PrivateMounts=yes
MemoryDenyWriteExecute=yes
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
RestrictRealtime=yes
RestrictSUIDSGID=yes
SystemCallFilter=@system-service @module @raw-io
SystemCallErrorNumber=EPERM
SystemCallArchitectures=native
LockPersonality=yes

这个程序并不是通过逐个读取 sysfs 的 uevent 文件的内容来获取信息的,而是通过 uevent netlink 来获得内核发送的 uevent:

[root@centos8 block]# ps -ef | grep systemd-udevd
root        1830       1  0 Feb18 ?        00:00:01 /usr/lib/systemd/systemd-udevd
root      807799 3741741  0 23:03 pts/93   00:00:00 grep --color=auto systemd-udevd
[root@centos8 block]# lsof -p 1830 | grep -i uevent
systemd-u 1830 root    3u  netlink                         0t0     92397 KOBJECT_UEVENT

include/uapi/linux/netlink.h 文件中定义了这个 netlink 类型


#define NETLINK_KOBJECT_UEVENT        15        /* Kernel messages to userspace */

既然 udev 是通过 NETLINK_KOBJECT_UEVENT 来接收事件的,为什么还需要 uevent 文件呢?

这个是用于解决开机启动以及 udevd 重启的情况。当 udevd 启动时,它会扫描 sysfs 中所有设备的 uevent 文件,然后针对每个文件写入一个 ADD 内容,这样就可以触发 kernel 再一次通知和该设备有关的事件,然后 udevd 就可以收到该事件,并进行 udev rule 的处理。

Ref


知识共享许可协议本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。