Linux设备模型(四) uevent
2018-02-28 15:03
162 查看
热插拔事件:在linux系统中,当系统配置发生变化时,如添加kset到系统或移动kobject,一个通知会从内核空间发送到用户空间,这就是热插拔事件。
热插拔事件的产生通常是由在总线驱动程序层的逻辑所控制。
热插拔事件会导致用户空间中的处理程序(如udev,mdev)被调用,这些处理程序会通过加载驱动程序,创建设备节点等来响应热插拔事件。
比如,当U盘通过USB线缆插入到系统时。热插拔事件会导致对/sbin/hotplug程序的调用,该程序通过加载驱动程序,创建设备节点,挂装分区,或者其他正确的动作来响应。
uevent事件是kobject的一部分,用于在kobject状态发生改变时,例如增加、移除等,通知用户空间程序。用户空间程序收到这样的事件后,会做相应的处理。
在上边kset结构体中就有通知事件uevent,设备模型中任何设备状态发生改变时需要上报,会触发uevent提供的接口。
而在Linux系统,可执行文件的执行,依赖于环境变量,环境变量的作用是为执行用户空间程序设置运行环境。
因此
环境变量参数的结构体
当内核中打开
代码参考内核源码 lib/kobject_event.c
显然 uevent 的机制,就是设置环境变量,然后调用用户空间程序 mdev 进行更新设备。
uevent模块准备好上报事件的格式后,在
一种是通过kmod模块,直接调用用户空间的可执行文件;
一种是通过netlink通信机制(需要内核使能
这部分源码就在
从源码分析来看,在利用Kmod向用户空间上报event事件时,会调用内核的接口
uevent_helper为指定的执行文件路径
总结:
kobject_uevent的工作
它的作用会在上层容器,device和driver的kset对象中体现比较重要,也正是这个函数接口,会调用mdev自动创建设备节点
对于热插拔事件的调用:
热插拔事件的产生通常是由在总线驱动程序层的逻辑所控制。
热插拔事件会导致用户空间中的处理程序(如udev,mdev)被调用,这些处理程序会通过加载驱动程序,创建设备节点等来响应热插拔事件。
比如,当U盘通过USB线缆插入到系统时。热插拔事件会导致对/sbin/hotplug程序的调用,该程序通过加载驱动程序,创建设备节点,挂装分区,或者其他正确的动作来响应。
uevent事件是kobject的一部分,用于在kobject状态发生改变时,例如增加、移除等,通知用户空间程序。用户空间程序收到这样的事件后,会做相应的处理。
在上边kset结构体中就有通知事件uevent,设备模型中任何设备状态发生改变时需要上报,会触发uevent提供的接口。
而在Linux系统,可执行文件的执行,依赖于环境变量,环境变量的作用是为执行用户空间程序设置运行环境。
因此
kobj_uevent_env用于组织此次事件上报时的环境变量,
kset_uevent_ops->uevent就是设置这个环境变量到用户空间
struct kset_uevent_ops { int (*filter)(struct kset *kset, struct kobject *kobj);//事件过滤 const char *(*name)(struct kset *kset, struct kobject *kobj);//返回字符串给用户空间的热插拔程序 int (*uevent)(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env);//传递热插拔程序的环境变量 }; filter() 函数让 kset 代码决定是否将事件传递给用户空间。用于过滤掉不需要导出到用户空间的事件 name 该接口可以返回kset的名称。如果一个kset没有合法的名称,则其下的所有Kobject将不允许上报uvent uevent() 函数用于设置用户热插拔处理程序的环境变量
环境变量参数的结构体
#define UEVENT_NUM_ENVP 32 /* number of env pointers */ #define UEVENT_BUFFER_SIZE 2048 /* buffer for the variables */ struct kobj_uevent_env { char *envp[UEVENT_NUM_ENVP]; int envp_idx; char buf[UEVENT_BUFFER_SIZE]; int buflen; };
当内核中打开
CONFIG_HOTPLUG这个宏,也就是支持热插拔后,会有对应的这几个函数被定义
kobject_uevent在后边上层容器(设备、驱动、总线)中会被调用到,其中在
kset_register中就有调用
/* include/linux/kobject.h lib/kobject_event.c */ int kobject_uevent(struct kobject *kobj, enum kobject_action action); int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, char *envp[]); int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...) int kobject_action_type(const char *buf, size_t count, enum kobject_action *type); kobject_uevent 不添加环境变量的上报 kobject_uevent_env 以envp为环境变量,上报一个指定action的uevent。 add_uevent_var 以格式化字符的形式(类似printf、printk等),将环境变量copy到env指针中。 kobject_action_type 将enum kobject_action类型的Action,转换为字符串。 enum kobject_action { KOBJ_ADD, KOBJ_REMOVE, KOBJ_CHANGE, KOBJ_MOVE, KOBJ_ONLINE, KOBJ_OFFLINE, KOBJ_MAX }; ADD/REMOVE kobject(或上层数据结构)的添加/移除事件。 ONLINE/OFFLINE kobject(或上层数据结构)的上线/下线事件,其实是是否使能。 CHANGE kobject(或上层数据结构)的状态或者内容发生改变。 MOVE kobject(或上层数据结构)更改名称或者更改Parent(意味着在sysfs中更改了目录结构)。 CHANGE 如果设备驱动需要上报的事件不再上面事件的范围内,或者是自定义的事件,可以使用该event,并携带相应的参数。
代码参考内核源码 lib/kobject_event.c
int kobject_uevent(struct kobject *kobj, enum kobject_action action) { return kobject_uevent_env(kobj, action, NULL); } int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, char *envp_ext[]) { ... /* default keys */ retval = add_uevent_var(env, "ACTION=%s", action_string); if (retval) goto exit; retval = add_uevent_var(env, "DEVPATH=%s", devpath); if (retval) goto exit; retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem); if (retval) goto exit; ... /* * Mark "add" and "remove" events in the object to ensure proper * events to userspace during automatic cleanup. If the object did * send an "add" event, "remove" will automatically generated by * the core, if not already done by the caller. */ if (action == KOBJ_ADD) kobj->state_add_uevent_sent = 1; else if (action == KOBJ_REMOVE) kobj->state_remove_uevent_sent = 1; /* we will send an event, so request a new sequence number */ spin_lock(&sequence_lock); seq = ++uevent_seqnum; spin_unlock(&sequence_lock); retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq); if (retval) goto exit; #if defined(CONFIG_NET) /* send netlink message */ ... #endif ... /* call uevent_helper, usually only enabled during early boot */ if (uevent_helper[0] && !kobj_usermode_filter(kobj)) { char *argv [3]; argv [0] = uevent_helper; argv [1] = (char *)subsystem; argv [2] = NULL; retval = add_uevent_var(env, "HOME=/"); if (retval) goto exit; retval = add_uevent_var(env, "PATH=/sbin:/bin:/usr/sbin:/usr/bin"); if (retval) goto exit; retval = call_usermodehelper(argv[0], argv, env->envp, UMH_WAIT_EXEC); } exit: kfree(devpath); kfree(env); return retval; }
显然 uevent 的机制,就是设置环境变量,然后调用用户空间程序 mdev 进行更新设备。
uevent模块准备好上报事件的格式后,在
kobject_uevent中有两种方法把事件上报到用户空间:
一种是通过kmod模块,直接调用用户空间的可执行文件;
一种是通过netlink通信机制(需要内核使能
CONFIG_NET),将事件从内核空间传递给用户空间。
这部分源码就在
kobject_uevent函数里
从源码分析来看,在利用Kmod向用户空间上报event事件时,会调用内核的接口
call_usermodehelper,配置好环境变量,然后直接执行用户空间的可执行程序 mdev 。
uevent_helper为指定的执行文件路径
总结:
kobject_uevent的工作
1、将 device 的 kobject 的PATH 、name、主次设备号等等设置到环境变量里 2、调用用户空间 mdev,自动创建设备节点
它的作用会在上层容器,device和driver的kset对象中体现比较重要,也正是这个函数接口,会调用mdev自动创建设备节点
对于热插拔事件的调用:
当具体对象有事件发生时,相应的操作函数中(如device_add()),会调用事件消息接口kobject_uevent()。 在该接口中,首先会添加一些共性的消息(路径、子系统名等),然后会回调kset -> uevent_ops -> uevent(kset, kobj, env)来添加该对象特有的事件消息(如device对象的设备号、设备名、驱动名、DT信息) 一切准备完毕,就会通过两种可能的方法向用户空间发送消息并执行相关程序:1.netlink广播;2. uevent_helper程序。
相关文章推荐
- Linux设备模型(3)_Uevent
- Linux设备模型(3)_Uevent【转】
- Linux设备模型(3)_Uevent
- Linux设备模型(3)_Uevent
- Linux设备模型(3)_Uevent
- Linux设备模型(3)_Uevent
- Linux设备模型(3)_Uevent
- Linux设备模型(3)_Uevent
- Linux设备模型(3)_Uevent
- Linux设备模型(3)_Uevent
- Linux设备模型(3)_Uevent
- Linux设备模型(3)_Uevent【转】
- 三、Linux设备模型(3)_Uevent
- Linux设备模型(3)_Uevent
- Linux设备模型之tty驱动架构分析
- Linux设备模型之tty驱动架构分析
- Linux设备模型 -- 总线、设备、驱动程序和类(机制理解)
- linux 设备模型 补充
- Linux设备模型中三个很重要的概念: 总线,设备,驱动.即bus,device,driver
- Linux设备模型之input子系统详解