前言
此文始于 2019-04-02,这是在简书的第一篇文章,目的是为了练习 markdown语法。先选择一篇文章进行 copy to write,结合目前正在看的内容 android ueventd,选一篇对 uevent 的描述来进行练习。
进展
Linux 设备模型之 Uevent
1. Uevent的功能
uevent 是 Kobject 的一部分,用于在 Kobject 状态发生改变时,例如增加、移除等,通知用户空间程序,用户空间程序接收到这样的时间后,会做出相应的处理。该机制通常是用来支持热插拔设备的,例如U盘插入后,USB相关 的驱动软件会动态创建用于表示该 U盘的 device 结构(相应的也包括其中的 kobject),并告知用户空间程序,为该 U盘动态的创建/dev/目录下的设备节点,更进一步,可以通知其他应用程序,将该 U盘 mount 到系统中,从而动态的支持该设备。
2. sysfs 概述
设备节点是为设备驱动所创建的,而设备device和驱动driver都是以链表的形式连接在总线bus上的,而设备——驱动——总线的更上一层就是sysfs层。
sysfs是一个内存文件系统,它把连接在系统上的设备和总线组织成为一个分级的文件,用户空间的程序同样可以利用这些信息,以实现和内核的交互。sysfs文件系统是当前系统上实际设备树的一个直观反映,用mount命令查看可以得知其挂载在“/sys”下 sysfs on /sys type sysfs (rw,seclabel,relatime))。当一个kobject被创建的时候,对应的sys文件和目录也就被创建了;其主要文件目录如下:
- /sys/block 存放块设备,提供以设备名(如sda)到/sys/devices的符号链接
- /sys/bus 按总线类型分类,在某个总线目录之下可以找到连接该总线的设备的符号链接,指向/sys/devices。某个总线目录之下的 drivers 目录包含了该总线所需的所有驱动的符号链接对应kernel中的 struct bus_type
- /sys/class 按设备功能分类,如输入设备在 /sys/class/input 之下,图形设备在 /sys/class/graphics 之下,是指向 /sys/devices 目录下对应设备的符号链接对应kernel中的 struct class
- /sys/dev 按设备驱动程序分层(字符设备/块设备),提供以major:minor为名到 /sys/devices 的符号链接对应kernel中的 struct device_driver
- /sys/devices 包含所有被发现的注册在各种总线上的各种物理设备。
所有的物理设备都按其在总线上的拓扑结构来显示,除了 platform devices 和 system devices 。platform devices一般是挂在芯片内部高速或者低速总线上的各种控制器和外设,能被CPU直接寻址。system devices不是外设,他是芯片内部的核心结构,比如CPU,timer等,他们一般没有相关的driver,但是会有一些体系结构相关的代码来配置他们对应kernel中的 struct device
上面展现了在sys目录下总线,设备,驱动和类所对应的文件,而他们的区别为: - device用于描述各种设备,其保存了所有的设备信息
- driver 用于驱动 device ,其保存了所有能够被它所驱动的设备链表。
- bus 是连接 CPU 和 device 的桥梁,其保存了所有挂载在它上面的设备链表和驱动这些设备的驱动链表。
- class 用于描述一类 device ,其保存了所有该类 device 的设备链表。
创建设备节点文件的过程
下图描述了设备节点文件的创建过程:
由此可知,设备模型中任何设备有事件需要上报时,会触发 uevent提供的接口,uevent模块准备好上报事件的格式后,可以通过两个途径上报到用户空间:
- 通过 kmod 模块,直接调动用户空间的可执行文件
- 通过 netlink 通信机制,将事件从内核空间传递给用户空间
PS: 目前多采用 netlink 通信机制,ueventd 也是采用 netlink机制创建 socket 与 kernel 进行通信。
netlink基本概念
用户空间程序接收到上报的uevent之后就可以根据其event类型进行相应操作了。
uevent的数据结构描述
kobject.h定义了uevent相关的常量和数据结构,如下:
kernel/lib/kobject_uevent.c
/* the strings here must match the enum in include/linux/kobject.h */
static const char *kobject_actions[] = {
[KOBJ_ADD] = "add",
[KOBJ_REMOVE] = "remove",
[KOBJ_CHANGE] = "change",
[KOBJ_MOVE] = "move", //暂未看到ueventd有具体处理
[KOBJ_ONLINE] = "online",
[KOBJ_OFFLINE] = "offline",//暂未看到ueventd有具体处理
};
/*
* The actions here must match the index to the string array
* in lib/kobject_uevent.c
*
* Do not add new actions here without checking with the driver-core
* maintainers. Action strings are not meant to express subsystem
* or device specific properties. In most cases you want to send a
* kobject_uevent_env(kobj, KOBJ_CHANGE, env) with additional event
* specific variables added to the event environment.
*/
kernel/include/linux/kobject.h
enum kobject_action {
KOBJ_ADD,
KOBJ_REMOVE,
KOBJ_CHANGE,
KOBJ_MOVE,
KOBJ_ONLINE,
KOBJ_OFFLINE,
KOBJ_MAX
};
kobject_action定义了event的类型,包括:
- ADD/REMOVE,Kobject(或上层数据结构)的添加/移除事件。
- ONLINE/OFFLINE,Kobject(或上层数据结构)的上线/下线事件,其实是是否使能。
- CHANGE,Kobject(或上层数据结构)的状态或者内容发生改变。
- MOVE,Kobject(或上层数据结构)更改名称或者更改Parent(意味着在sysfs中更改了目录结构)。
- CHANGE,如果设备驱动需要上报的事件不再上面事件的范围内,或者是自定义的事件,可以使用该event,并携带相应的参数。
小结
以上就是 uevent 的基础知识,作为了解 ueventd 前的准备。
期望
在后续日子可以完全使用 markdown 语言誊写文档