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两个文件,如下:
3.2 数据结构描述kobject.h定义了uevent相关的常量和数据结构,如下:
1: /* include/linux/kobject.h, line 50 */ 2: enum kobject_action { 3: KOBJ_ADD,
4: KOBJ_REMOVE,
5: KOBJ_CHANGE,
6: KOBJ_MOVE,
7: KOBJ_ONLINE,
8: KOBJ_OFFLINE,
9: KOBJ_MAX
10: };
kobject_action定义了event的类型,包括:
1: /* include/linux/kobject.h, line 31 */ 2: #define UEVENT_NUM_ENVP 32 /* number of env pointers */ 3: #define UEVENT_BUFFER_SIZE 2048 /* buffer for the variables */ 4:
5: /* include/linux/kobject.h, line 116 */ 6: struct kobj_uevent_env { 7: char *envp[UEVENT_NUM_ENVP]; 8: int envp_idx; 9: char buf[UEVENT_BUFFER_SIZE]; 10: int buflen; 11: };
前面有提到过,在利用Kmod向用户空间上报event事件时,会直接执行用户空间的可执行文件。而在Linux系统,可执行文件的执行,依赖于环境变量,因此kobj_uevent_env用于组织此次事件上报时的环境变量。
1: /* include/linux/kobject.h, line 123 */ 2: struct kset_uevent_ops { 3: int (* const filter)(struct kset *kset, struct kobject *kobj); 4: const char *(* const name)(struct kset *kset, struct kobject *kobj); 5: int (* const uevent)(struct kset *kset, struct kobject *kobj, 6: struct kobj_uevent_env *env); 7: };
kset_uevent_ops是为kset量身订做的一个数据结构,里面包含filter和uevent两个回调函数,用处如下:
3.3 内部动作通过kobject.h,uevent模块提供了如下的API(这些API的实现是在"lib/kobject_uevent.c”文件中): 1: /* include/linux/kobject.h, line 206 */ 2: int kobject_uevent(struct kobject *kobj, enum kobject_action action); 3: int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, 4: char *envp[]); 5:
6: __printf(2, 3)
7: int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...); 8:
9: int kobject_action_type(const char *buf, size_t count, 10: enum kobject_action *type);
原创文章,转发请注明出处。蜗窝科技,www.。 标签: Linux Kernel 内核 设备模型 Uevent 评论:
慢慢想
2016-09-12 13:36 wowo您好,想请教一个问题。
在android启动的时候init进程会进行coldboot操作,往所有uevent文件里写入add,如下: fd = openat(dfd, "uevent", O_WRONLY); write(fd, "add\n", 4); 但是在对某个uevent写入add的时候花费了很久的时间,导致系统起不来。(每次启动都是同一个uevent耗费时间) 想请教一下write(fd, "add\n", 4);操作之后相应的driver都做了啥?下面该如何加log分析,我在write前后加了log,可以确定时间全部耗费在write这个函数上了。 非常感谢!
wowo
2016-09-12 20:02 @慢慢想:对Android的uvent机制不是很熟悉,我猜测这里的write,是init进程通过uevent向另外一个进程传递了一个信息(add),另一个进程收到该信息后,做出相应的处理。
你要查为什么这么耗时,就要查这个uevent最终是谁处理的。 write uvent的过程大致如下: init进程 write uvent(ADD) drivers/base/core.c static struct device_attribute uevent_attr store_uevent kobject_action_type KOBJ_ADD kobject_uevent kobject_uevent_env ... 其它进程,收到ADD event,进行处理(find out it)
慢慢想
2016-09-22 10:30 @wowo:现在又能回复了。。。
您好,“其他进程”应该还是本进程,处理函数为handle_device_fd();不过这个函数没有耗费时间,耗费时间的只是write(fd, "add\n", 4);系统调用。我猜测问题还是出现在kernel space在上报netlink之前,请问这样理解对么?是不是注册这个uevent的driver有问题? 940 fd = openat(dfd, "uevent", O_WRONLY); 941 if(fd >= 0) { 942 write(fd, "add\n", 4); 943 close(fd); 944 handle_device_fd(); 945 }
大头元
2016-07-07 14:48 博主您好!之前在板子上做usb驱动实验。1、如果usb驱动编译进内核,插入u盘可以正常识别。2、把之前的usb驱动编译成模块,然后安装到/lib/modules下,插入u盘不能识别。有以下疑问:
1、把驱动编译成模块并安装后,系统启动后会主动加载驱动模块吗?还是必须自己主动的去modprobe或写一些脚本来加载驱动模块? 2、系统热插拔只是起到帮助创建设备文件、挂载的作用。驱动模块的加载跟热插拔有关系吗?
maze
2016-07-07 11:30 wowo您好,我有个疑问啊,Uevent上报用户空间的方法是netlink与kmod?
为什么说是kmod?这里不太理解,kmod是指通过call_usermodehelper这个函数实现吗? 谢谢您提供这个学习平台
gzz
2015-08-04 12:40 遇到一个疑惑就是linux下建立framebuffer帧设备节点时是/dev/fb0,而Android下的linux内核启动后建立的是/dev/graphics/fb0.
目前我看了devtmpfs里面假设使用了:貌似创建节点的nodename是根据dev的name来建立的,所以很奇怪这个为何不也是建立一个/dev/fb0. 但有看到uevent是建立一个/dev/graphics/fb0的。 所以疑问就是为何这两个设备节点最终只存在/dev/graphics/fb0啦。 此外要说明的graphics_class类是不带其他命名方式的,不像input_class可以进行class->input_devnode后将设备节点相对路径变为input/xxx路径。 很是疑惑,求解答。
wowo
2015-08-04 13:29 @gzz:一般情况下,Android的kernel是没有使能devtmpfs的,因此,谁帮忙创建“/dev/fb0”呢?所以没有创建。
至于"/dev/graphics/fb0",是Android的init进程,根据uevent自行创建的,”graphics“取自framebuffer所在的class(subsystem) name。具体可参考代码: system/core/init/devices.c ... } else if (!strncmp(uevent->subsystem, "graphics", 8)) { base = "/dev/graphics/"; make_dir(base, 0755); ...
gzz
2015-07-29 16:44 请教几个问题:
1.device_add里面有devtmpfs_create_node与kobject_uevent两种接口。 向问下有什么区别两者 2.在处理的device的dev_t没有指定时,udev是根据什么不去创建设备节点的。?因为看起来kobject_uevent都是执行了的。
linuxer
2015-07-30 09:08 @gzz:针对第一个问题的回答:
我们都知道,在引入linux device model之后,设备节点的创建是通过udev这个userspace的daemon进程捕获内核发送的各种uevent来进行的,这种方法虽然灵活,但是比较耗时,我们曾经进行过嵌入式系统启动过程速度的优化,纵观整个启动过程udev花费的时间不是一个小的数目,因此我们果断干掉udev,使用固定在rootfs中预先建立设备节点的方法 devtmpfs是为了提高启动速度而引入到kernel中,在内核中,各个设备的初始化过程中就已经建立了一个文件系统,在userspace只要进行mount的动作,所有的设备节点就ready了,不需要udev参与了 针对第二个问题的回答: device在add的时候会发送userspace event,包括其在sys目录下的位置,叫做dev path,udev获得这样的信息可以通过那个dev path,那个目录下有关于主次设备节点号的信息(在dev属性文件中),uevent可以通过这样的信息创建,当然,如果支持devtmpfs,那么udev不需要执行这一步,mount devtmpfs之后,所有的设备节点就在那了 |
|
来自: pravite_lib > 《设备模型》