驱动-热插拔-内核发送事件到用户空间-uevent

Linux 驱动开篇,内核是如何发送事件到用户空间


前言:

驱动热插拔事件初识,了解内核如何把事件发送给用户空间。

基础知识储备-参考资料

前面了解过数据模型,热插拔事件本身就基于数据模型 kObject/kset。 这里我们最好重温一下kObject/keyset 基础知识。
设备模型基本框架-kobject-kset
迅为-驱动-热插拔
内核发送事件到用户空间的方法

udevadm命令详解

udev简述
udevadm命令详解

一、发送事件API函数-kobject_uevent

基础知识梳理

核心概念

     kobject_uevent 是 Linux 内核设备模型(Device Model)的“信使”。它的唯一职责就是向用户空间(Userspace)广播一个事件,通知它内核中某个对象(通常代表一个设备)的状态发生了改变,例如被添加、移除或状态发生变化。

    这套机制是 热插拔(Hotplug) 事件的基石。用户空间的守护进程(如 udev mdev)监听这些事件,并据此动态地管理设备节点、加载驱动、设置权限等。

函数原型

// 最常用的函数,发送一个基本事件
int kobject_uevent(struct kobject *kobj, enum kobject_action action);

// 更强大的版本,允许附加自定义环境变量
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, char *envp[]);

参数解释

*kobject kobj

指向事件源的内核对象指针。这个 kobject 至关重要,因为它提供了事件的上下文:

  • 它的kobj->name和它在 sysfs 中的路径 (kobj->kobj_path) 用于标识是哪个对象发出了事件。

  • 它所属的 kset 决定了事件的“子系统”(Subsystem),例如 action 参数是一个枚举值,定义了事件的具体类型:

enum kobject_action action:

事件的具体类型。这是一个枚举值,定义了事件的具体类型:

  • KOBJ_ADD: 对象被创建并添加到系统中。(最常用)
  • KOBJ_REMOVE: 对象从系统中被移除。(最常用)
  • KOBJ_CHANGE: 对象的状态或属性发生了改变(例如,电池电量变化、网线插拔)。
  • KOBJ_MOVE: 对象被重命名或移动。
  • KOBJ_ONLINE: 对象上线。
  • KOBJ_OFFLINE: 对象下线。

*char envp[] (仅用于 kobject_uevent_env):

  • 一个以 NULL 结尾的字符串数组,用于向用户空间传递额外的、自定义的信息。每个字符串都是一个 “KEY=VALUE” 格式的环境变量。这极大地增强了事件的灵活性。

  • 例如,你可以传递 “PROFILE=performance” 或 “TRIGGER=low_battery”。

测试程序

源码程序如下:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/configfs.h>
#include <linux/kernel.h>
#include <linux/kobject.h>

// 定义kobject结构体指针,用于表示第一个自定义内核对象
struct kobject *mykobject01;

// 定义kset结构体指针,用于表示自定义内核对象的集合
struct kset *mykset;
// 定义kobj_type结构体,用于定义自定义内核对象的类型
struct kobj_type mytype;

// 模块的初始化函数
static int mykobj_init(void)
{
   
   
    int ret;

    // 创建并添加kset,名称为"mykset",父kobject为NULL,属性为NULL
    mykset = kset_create_and_add("mykset", NULL, NULL);

    // 为mykobject01分配内存空间,大小为struct kobject的大小,标志为GFP_KERNEL
    mykobject01 = kzalloc(sizeof(struct kobject), GFP_KERNEL);
    // 将mykset设置为mykobject01的kset属性
    mykobject01->kset = mykset;
    // 初始化并添加mykobject01,类型为mytype,父kobject为NULL,格式化字符串为"mykobject01"
    ret = kobject_init_and_add(mykobject01, &mytype, NULL, "%s", "mykobject01");

    // 触发一个uevent事件,表示kobject的属性发生了变化
    ret = kobject_uevent(mykobject01, KOBJ_CHANGE);

    return 0;
}

// 模块退出函数
static void mykobj_exit(void)
{
   
   
    // 释放mykobject01的引用计数
    kobject_put(mykobject01);
    kset_unregister(mykset);
}

module_init(mykobj_init); // 指定模块的初始化函数
module_exit(mykobj_exit); // 指定模块的退出函数

MODULE_LICENSE("GPL");     // 模块使用的许可证
MODULE_AUTHOR("fangchen"); // 模块的作者

这里结合kset 、kobject,然后 模拟发送一个uevent 事件出去: kobject_init_and_add

我们其实就是模拟一次内核发送uevent 事件出去 来 理解内核发送uevent 的机制。

udevadm 监控命令

这里可以参考上面的资料:
udev简述
udevadm命令详解

我们用这个命令目的就是监听系统uevent 事件,查看上面源码程序测试现象和效果。
这里掌握一个命令:udevadm monitor & ,监听和显示当前系统uevent 事件。

核心命令如下:

  • udevadm info: 用于获取设备的详细信息, 包括设备路径、 属性、 驱动程序等。
  • udevadm monitor: 用于监视和显示当前系统中的 uevent 事件。 它会实时显示设备的插入、拔出以及其他相关事件。
  • udevadm trigger: 用于手动触发设备的 uevent 事件。 可以使用该命令模拟设备的插入、 拔出等操作, 以便触发相应的事件处理。
  • udevadm settle: 用于等待 udev 处理所有已排队的 uevent 事件。 它会阻塞直到 udev 完成当前所有的设备处理操作。
  • udevadm control: 用于与 udev 守护进程进行交互, 控制其行为。 例如, 可以使用该命令重新加载 udev 规则、 设置日志级别等。
  • udevadm test: 用于测试 udev 规则的匹配和执行过程。 可以通过该命令测试特定设备是否能够正确触发相应的规则。

udevadm info - 查询设备信息

这是最常用的命令之一,用于查看 udev 数据库中的设备属性,这些属性是编写 udev 规则的关键。
常用选项:

  • -q all:查询所有信息。
  • -q path/-q name:查询设备路径或节点名。
  • -a:显示设备的所有父设备的属性(非常重要,用于找规则匹配项)。
  • -n:指定设备节点名。
  • –attribute-walk:类似于 -a,但显示更详细的层次结构
# 最基本的方式:查看 /dev/sdb1 的所有 udev 属性
udevadm info -n /dev/sdb1

# 更强大的方式:显示 /dev/sdb1 及其所有父级设备(USB控制器、总线等)的属性
# 这能帮你找到最唯一、最稳定的属性来编写规则
udevadm info -a -n /dev/sdb1

udevadm monitor - 监视设备事件

这个命令用于实时监听内核发出的设备事件(如插拔),是调试规则的第一步。

常用选项:

  • –kernel:只显示内核发出的事件(默认)。
  • –udev:只显示 udev 处理完成后的事件。
  • –property:显示事件的详细属性。
  • –subsystem-match=:只监听特定子系统(如 usb, input)的事件

监听 USB 设备的插拔
打开一个终端,开始监听所有事件:

udevadm monitor --kernel --udev --property

插入你的 U 盘或鼠标。终端会立即打印出类似下面的信息:

 KERNEL
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

野火少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值