linux uevent 机制中的udev 和mdev

一、Uevent机制

1.前提摘要

(1)Sysfs文件系统 

        内核设备模型主要的模块和用户之间能看到的相关部分就是sysfs文件系统了。内核在启动的时候会注册sysfs文件系统,并且在启动系统的初期。通过mount命令挂载sysfs文件系统到/sys挂载点。

       Mount -t sysfs sysfs /sys

           那么sysfs文件系统的作用是什么呢。概括的说有三点:

                    1)、建立系统中总线、驱动、设备三者之间的桥梁

                    2)、像用户空间展示内核中各种设备的拓扑图

                    3)、提供给用户空间对设备获取信息和操作的接口,部分取代ioctl功能。

2)Kobject:Sysfs文件系统中最基本的结构就是kobject,kobject可以代表一个设备,一条总线等。在sys目录下直观的以一个目录表示出来。

(3)Uevent机制

     上面的分析其实只是对Linux设备模型做了一些基础性的了解。也就是一个穿针引线的作用,如果要细致了解,需要仔细阅读代码。有了上面对于sysfs的基础。接下来我们来比较详细的了解一下uevent机制。

         什么是uevent机制。这个得从热插拔设备开始说起。最简单的一个例子就是U盘了。当我们在计算机上插上一个U盘的时候,系统的USB hub会检测到U盘设备接入,并且完成设备枚举过程(从设备上读出相应的设备信息),并在内核中创建相应的设备结构体。但是,usb设备千奇百态,内核不可能预先将所有usb设备驱动都增加到内存中来。也就是当插入U盘设备的时候,内核中不一定存在对应这个设备的usb驱动。这个时候USB驱动也许以模块的形式保存在硬盘上。载入驱动必然只能从用户态来进行,那这时候应该怎么办呢?

          看到这里的时候,有人一定会想,人工敲入命令载入驱动,呵呵。这必然是一种方法,但是是一种很古老的方法。Linux对类似的情况设计了一种uevent的机制。当有新的设备加入的时候,将设备的信息发送消息到用户态。而用户态有一个udev的进程监听这个信息。当收到信息后做一定的解析,根据解析到的结果和用户程序的配置做一些处理,也包括加载驱动程序。

2.具体介绍(Linux设备模型(3)_Uevent

          Uevent是Kobject的一部分,用于在Kobject状态发生改变时,例如增加、移除等,通知用户空间程序。

用户空间程序收到这样的事件后,会做相应的处理。

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

更进一步,可以通知其它的应用程序,将该U盘设备mount到系统中,从而动态的支持该设备。

Uevent在kernel中的位置

 

由此可知,Uevent的机制是比较简单的,设备模型中任何设备有事件需要上报时,会触发Uevent提供的接口。Uevent模块准备好上报事件的格式后。

        可以通过两个途径把事件上报到用户空间:一种是通过kmod模块,直接调用用户空间的可执行文件;

另一种是通过netlink通信机制,将事件从内核空间传递给用户空间。

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

Uevent的内部逻辑解析

        Uevent的代码比较简单,主要涉及kobject.h和kobject_uevent.c两个文件,如下:

  • include/linux/kobject.h
  • lib/kobject_uevent.c

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

         说明:怎么指定处理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”中相应的代码,这里不再详细描述。

3.实例分析(Uevent 上报event事件给上层的详细讲解_sunweizhong1024的专栏-CSDN博客_add_uevent_varheadphone_event 上报事件的分析

     本文章讲解插入headphone的时候,向上层上报event函数的整个过程

#ifdef CONFIG_I_LOVE_PBJ30

void headphone_event(int state)

{

       switch_set_state(&wired_switch_dev, state);

}

EXPORT_SYMBOL_GPL(headphone_event);

#endif

 

headphone_event 函数会调用switch_set_state函数进行上报事件

接下来会调用kobject_uevent_env函数进行上报事件。

最终调用add_uevent_var()将用户空间需要的参数添加到环境变量中去,如

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;

4.实例分析2

     Uevent 是内核通知Android有状态变化的一种方法,比如USB线插入、拔出,电池电量变化等等。其本质是内核发送(可以通过socket)一个字符串,应用层(android)接收并解释该字符串,获取相应信息。

 (一)、Kernel侧:UEVENT的发起在Kernel端,主要是通过函数

             intkobject_uevent_env(struct kobject*kobj, enum kobject_action action,char*envp_ext[])

该函数的主要功能是根据参数组合一个字符串并发送。一个典型的字符串如下:change@/devices/platform/msm-battery/power_supply/usb纮ACTION=change纮DEVPATH=/devices/platform/msm-battery/power_supply/usb纮SUBSYSTEM=power_supply纮POWER_SUPPLY_NAME=usb纮POWER_SUPPLY_ONLINE=0纮SEQNUM=1486纮

上面这块来自网上,这段内容是否有问题,待考究。

下面看这个函数: int kobject_uevent_env(structkobject *kobj, enum kobject_action action,char *envp_ext[])

static const char *kobject_actions[] ={

[KOBJ_ADD] = "add",

[KOBJ_REMOVE] = "remove",

[KOBJ_CHANGE] = "change",

[KOBJ_MOVE] = "move",

[KOBJ_ONLINE] = "online",

[KOBJ_OFFLINE] = "offline",

};

//以上为kobject标准的动作,调用时需要传入相应的enum值

///以下是获取subsystem信息

if (uevent_ops&&uevent_ops->name)

subsystem =uevent_ops->name(kset, kobj);

else

subsystem =kobject_name(&kset->kobj);

if (!subsystem) {

pr_debug("kobject: '%s' (%p):%s: unset subsystem caused the "

"event to drop!\n",kobject_name(kobj), kobj,

__func__);

return 0;

}

//下面准备要传递的信息数据

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;

//envp_ext[i]是传进来的参数,为该event时携带的一些自定义的信息

if (envp_ext) {

for (i = 0; envp_ext[i]; i++){

retval = add_uevent_var(env, "%s", envp_ext[i]);

if (retval)

goto exit;

//下面通过网络socket将数据发送出去

mutex_lock(&uevent_sock_mutex);

list_for_each_entry(ue_sk,&uevent_sock_list, list) {

struct sock *uevent_sock =ue_sk->sk;

struct sk_buff*skb;

size_t len;

NETLINK_CB(skb).dst_group =1;//下面开始发送数据

retval =netlink_broadcast_filtered(uevent_sock, skb,

0, 1, GFP_KERNEL,

kobj_bcast_filter,

kobj);

}

Linux--内核Uevent事件机制 与 Input子系统【转】 - sky-heaven - 博客园

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值