Udev 内核机制(kobject_uevent) 性能优化

 

前言

这两天遇到一些 udev 的问题, 顺便阅读了一下文档 ( 见参考文档 ) , 基本了解了 udev 的机制。 嵌入式系统性能很重要,于是对内核这一块进行了 性能方面的 研究。

内核大概 20 多处会调用了 kobject_uevent 函数发送 KOBJ_ADD / KOBJ_REMOVE 等事件。其中我们最关心的就是在 device_add/device_del 中向用户空间发送设备的添加、删除信息。

kobject_uevent 直接调用 kobject_uevent_env 函数 完成功能, 我们先分析一下该函数的流程

kobject_uevent_env 调用流程 (内核版本 2.6.21

  1. kobject_uevent_env 首先搜索 kobject 所属的 kset, 获得 kset ->uevent_ops ,类型为 kset_uevent_ops (系统一共有 block_uevent_ops   memory_uevent_ops   device_uevent_ops   class_uevent_ops   module_uevent_ops )这几大类 kset_uevent_ops, 我们这里主要看 device_uev ent_ops
  2. 如果 uevent_ops filter 成员 函数, 则调用该函数, 如果 filter 函数返回非0, 则失败,结束流程。 device_uevent_ops filter 函数为 dev_uevent_filter, 它的过滤条件为该设备的 总线 ( bus ) 或者 ( class ) 非空。
  3. envp 分配一个大小为  32 的数组 , 用于存储环境变量指针,为 buffer 分配一个大小为 2048 的数组,用于存储环境变量内容。
  4. 初始化环境变量, 包括 HOME PATH ACTION DEVPATH SUBSYSTEM
  5. 如果 uevent_ops uevent 函数,则调用该函数, 如果 uevent 函数返回非0, 则失败,结束流程。 device_uevent_ops uevent 函数为 dev_uevent ,它的流程为:
    1. 添加主次设备号 环境变量
    2. 添加 driver 的名称 环境变量
    3. 如果设备所属总线 (dev->bus) uevent 函数, 则调用它。 失败,结束流程
    4. 如果设备所属类 (dev->class) uevent 函数, 则调用它。 失败,结束流程。 (象 函数 _ request_firmware () , 有两次调用 kobject_uevent 函数,其中第一次在 class uevent 函数 firmware_uevent 中,测试 FW_STATUS_READY 位失败而返回
    5. 如果设备所属类型 (dev->type) uevent 函数, 则调用它。 失败,结束流程 . ( 搜索了一下, 这个 field 在整个内核里基本没有使用)
    6. 注意每个 uevent 函数都有机会添加环境变量以及出错返回,结束 uevent 传递。
  6. 递增 uevent 序列号 uevent_seqnum (可以通过 cat /sys/kernel/uevent_seqnum 查看)。
  7. 如果定义了 CONFIG_NET 通过 netlink 将事件以及环境变量发送出去。
  8. 如果 uevent_helper 非空, 则调用该函数。 uevent_helper 的缺省值为 : "/sbin/hotplug" 应该在用户层调用: echo "" > /sys/kernel/uevent_helper 来取消 该此调用。

性能数据统计

我们通过高精度定时器 ( 一个 3.25M timer) 来统计 kobject_uevent 的时间花费 :

  1. 还没有进入用户空间时,内核调用了 kobject_uevent 244 次, 平均每次 289us, 总共 70ms (由于每次需要调用并不存在的 /sbin/hotplug, 时间比较长。
  2. 进入用户空间后,调用  udevtrigger, 开始 15 kobject_uevent 调用平均每次 6249 us, 总共 93 ms ( 很奇怪,需要深入研究一下 )
  3. 然后 181 kobject_uevent 调用平均每次 47us, 总共 8 ms

优化方案 :

考虑到:

  1. 还没有进入用户空间时发出的 uevent 看似丢失了!可以不发出
  2. /sbin/hotplug 在使用 netlink 时永远没有必要调用!可以删除

我们可以:

  1. 删除调用 /sbin/hotplug 的代码
  2. uevent_helper 非空时不真正发出 uevent

这样,在进入用户空间之前,我们的 uevent 机制不会做任何事情,只有用户空间调用

echo "" > /sys/kernel/uevent_helper

后, uevent 才会被真正发送到 netlink.

 

实践证明系统运行良好。

udev 参考文档

Writing udev rules

http://reactivated.net/writing_udev_rules.html

udev 文件系统的使用和基本工作原理分析

http://blog.csdn.net/colorant/archive/2008/01/09/2031721.aspx

udev 的实现原理

http://blog.csdn.net/absurd/archive/2007/04/27/1587938.aspx

你可能感兴趣的:(timer,filter,性能优化,存储,Class,文档)