Linux设备模型(3)_uevent

1.uevent的功能

uevent是Kobject的一部分,用于在Kobject状态发生改变时,例如增加、移除等,通知用户空间程序。用户空间程序收到这样的事件后,会做相应的处理。

该机制通常是用来支持热拔插设备的,例如U盘插入后,USB相关的驱动软件会动态创建用于表示该U盘的device结构(相应的也包括其中的kobject),并告知用户空间程序,为该U盘动态的创建/dev/目录下的设备节点,更进一步,可以通知其它的应用程序,将该U盘设备mount到系统中,从而动态的支持该设备。

2.uevent在kernel中的位置

下面图片描述了uevent模块在内核中的位置:


由此可知,uevent的机制是比较简单的,设备模型中任何设备有事件需要上报时,会触发uevent提供的接口。uevent模块准备好上报事件的格式后,可以通过两个途径把事件上报到用户空间:一种是通过kmod模块,直接调用用户空间的可执行文件;另一种是通过netlink通信机制,将事件从内核空间传递给用户空间。

注1:有关kmod和netlink,会在其它文章中描述,因此本文就不再详细说明了。

3.uevent的内部逻辑解析
3.1.source code位置

uevent的代码比较简单,主要涉及kobject.h和kobject_uevent.c两个文件,如下:
include/linux/kobject.h
lib/kobject_uevent.c
3.2.数据结构描述
kobject.h定义了uevent相关的常量和数据结构,如下:

/* include/linux/kobject.h, line 50 */
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,并携带相应的参数。

kobj_uevent_env定义:

/* include/linux/kobject.h, line 31 */
#define UEVENT_NUM_ENVP         32 /* number of env pointers */
#define UEVENT_BUFFER_SIZE      2048 /* buffer for the variables */
/* include/linux/kobject.h, line 116 */
struct kobj_uevent_env {
    char *envp[UEVENT_NUM_ENVP];
    int envp_idx;
    char buf[UEVENT_BUFFER_SIZE];
    int buflen;
};

前面有提到过,在利用kmod向用户空间上报event事件时,会直接执行用户空间的可执行文件。而在Linux系统,可执行文件的执行,依赖于环境变量,因此kobj_uevent_env用于组织此次事件上报时的环境变量。

envp:指针数组,用于保存每个环境变量的地址,最多可支持的环境变量数量为UEVENT_NUM_ENVP。
envp_idx:用于访问环境变量指针数组的index。
buf:保存环境变量的buffer,最大为UEVENT_BUFFER_SIZE。
buflen:访问buf的变量。

kset_uevent_ops定义:

/* include/linux/kobject.h, line 123 */
struct kset_uevent_ops {
    int (* const filter)(struct kset *kset, struct kobject *kobj);
    const char *(* const name)(struct kset *kset, struct kobject *kobj);
    int (* const uevent)(struct kset *kset, struct kobject *kobj,struct kobj_uevent_env *env);                
};

kset_uevent_ops是为kset量身订做的一个数据结构,里面包含filter和uevent两个回调函数,用处如下:

filter:当任何Kobject需要上报uevent时,它所属的kset可以通过该接口过滤,阻止不希望上报的event,从而达到从整体上管理的目的。
name:该接口可以返回kset的名称。如果一个kset没有合法的名称,则其下的所有Kobject将不允许上报uvent。

uevent:当任何Kobject需要上报uevent时,它所属的kset可以通过该接口统一为这些event添加环境变量。因为很多时候上报uevent时的环境变量都是相同的,因此可以由kset统一处理,就不需要让每个Kobject独自添加了。

3.3内部动作
通过kobject.h,uevent模块提供了如下的API(这些API的实现是在"lib/kobject_uevent.c"文件中):
/* include/linux/kobject.h, line 206 */
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_env:以envp为环境变量,上报一个指定action的uevent。环境变量的作用是为执行用户空间程序指定运行环境。
kobject_uevent:和kobject_uevent_env功能一样,只是没有指定任何的环境变量。
add_uevent_var:以格式化字符的形式(类似printf、printk等),将环境变量copy到env指针中。
kobject_action_type:将enum kobject_action类型的Action,转换为字符串。

说明:怎么指定处理uevent的用户空间程序(简称uevent helper)? 
上面介绍kobject_uevent_env的内部动作时,有提到,uevent模块通过Kmod上报uevent时,会通过call_usermodehelper函数,调用用户空间的可执行文件(或者脚本,简称uevent helper)处理该event。而该uevent helper的路径保存在uevent_helper数组中。 
可以在编译内核时,通过CONFIG_UEVENT_HELPER_PATH配置项,静态指定uevent helper。但这种方式会为每个event fork一个进程,随着内核支持的设备数量的增多,这种方式在系统启动时将会是致命的(可以导致内存溢出等)。因此只有在早期的内核版本中会使用这种方式,现在内核不再推荐使用该方式。因此内核编译时,需要把该配置项留空。 

在系统启动后,大部分的设备已经ready,可以根据需要,重新指定一个uevent helper,以便检测系统运行过程中的热拔插事件。这可以通过把helper的路径写入到"/sys/kernel/uevent_helper”文件中实现。实际上,内核通过sysfs文件系统的形式,将uevent_helper数组开放到用户空间,供用户空间程序修改访问,具体可参考"./kernel/ksysfs.c”中相应的代码,这里不再详细描述。

本文转自:点击打开链接

猜你喜欢

转载自blog.csdn.net/caihaitao2000/article/details/80634067
今日推荐