转自:http://blog.chinaunix.net/uid-29401328-id-5019678.html

一、概述

mdev是busybox提供的一个工具,用在嵌入式系统中,相当于简化版的udev,作用是在系统启动和热插拔或动态加载驱动程序时,
自动创建设备节点。文件系统中的/dev目录下的设备节点都是由mdev创建的。

在加载驱动过程中,根据驱动程序,在/dev下自动创建设备节点。

以下内容摘自busybox-1.23.1的mdev.txt文件:
Mdev has two primary uses: initial population and dynamic updates.  Both 
require sysfs support in the kernel and have it mounted at /sys.  For dynamic 
updates, you also need to have hotplugging enabled in your kernel.

Here's a typical code snippet from the init script: 
[0] mount -t proc proc /proc 
[1] mount -t sysfs sysfs /sys 
[2] echo /sbin/mdev > /proc/sys/kernel/hotplug 
[3] mdev -s

Alternatively, without procfs the above becomes: 
[1] mount -t sysfs sysfs /sys 
[2] sysctl -w kernel.hotplug=/sbin/mdev 
[3] mdev -s

Of course, a more "full" setup would entail executing this before the previous 
code snippet: 
[4] mount -t tmpfs -o size=64k,mode=0755 tmpfs /dev 
[5] mkdir /dev/pts 
[6] mount -t devpts devpts /dev/pts
The simple explanation here is that [1] you need to have /sys mounted before 
executing mdev.  Then you [2] instruct the kernel to execute /sbin/mdev whenever 
a device is added or removed so that the device node can be created or destroyed.  
Then you [3] seed /dev with all the device nodes that were created while the system 
was booting.

For the "full" setup, you want to [4] make sure /dev is a tmpfs filesystem 
(assuming you're running out of flash).  Then you want to [5] create the 
/dev/pts mount point and finally [6] mount the devpts filesystem on it.

二、用法

这篇博文所涉及到的用法很简单:

1、在/etc/init.d/rcS脚本里有“mdev -s”
解释:在系统启动时,通过执行“mdev -s”扫描/sys/class和/sys/block,在目录中查找dev文件。例如:/sys/class/tty/tty0/dev,
它的内容为"4:0",即主设备号是4,次设备号是0,dev的上一级目录为设备名,这里是tty0。/sys/class/下的每个文件夹都代表
着一个子系统。

2、在/etc/init.d/rcS脚本里有“echo /sbin/mdev > /proc/sys/kernel/hotplug”,即是把/sbin/mdev写到/proc/sys/kernel/hotplug文件里
解释:当有热插拔事件产生时,内核会调用/proc/sys/kernel/hotplug文件里指定的应用程序来处理热插拔事件

根据mdev.txt的说明可知在使用mdev之前要满足下面的条件:
mount -t proc proc /proc 
mount -t sysfs sysfs /sys
mount -t tmpfs -o size=64k,mode=0755 tmpfs /dev 
mkdir /dev/pts 
mount -t devpts devpts /dev/pts

说明:所有的设备都可以在/sys/class下找到,这个文件夹下的每一个文件夹下代表了一类设备,表示类设备的文件夹下也有
文件夹,这些文件夹代表设备。如:/sys/class/test/test_dev/ ,test代表类,如net、tty、sound;test_dev代表某个
设备,它的名字和/dev下的设备节点名字是一样的

三、内核源码分析

分析一下相关内核源码,看上面提到的功能是如何实现的。
平时我们添加驱动时,如果想自动创建设备节点调用的函数是class_create和device_create。class_create是创建类设备,
就是在/sys/class/创建一个文件夹,这个文件夹代表一类设备,这个文件夹里会包含device_create创建的设备,也是一个
文件夹。
下面就从device_create入手,看是怎么实现自动创建设备节点的。源码基于linux-2.6.30.4内核

struct device *device_create(struct class *class, struct device *parent,
    dev_t devt, void *drvdata, const char *fmt, ...)
    dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
        retval = device_register(dev);
            return device_add(dev);
                kobject_uevent(&dev->kobj, KOBJ_ADD);
                    return kobject_uevent_env(kobj, action, NULL);   // action = KOBJ_ADD
                        const char *action_string = kobject_actions[action];    // action_string = "add"
                        ……
                        //把相关信息存到环境变量里,ACTION代表操作类型,DEVPATH为设备在class下存在的路径,SUBSYSTEM为class_create创建的设备类
                        //ACTION=add , DEVPATH=/class/test/test_dev , SUBSYSTEM=test
                        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;
                        ……
                        if (uevent_helper[0]){
                            argv [0] = uevent_helper;
                            argv [1] = (char *)subsystem;
                            argv [2] = NULL;
                        
                            //内核空间调用用户空间程序,调用的程序由argv [0] = uevent_helper指定
                            retval = call_usermodehelper(argv[0], argv,
                                  env->envp, UMH_WAIT_EXEC);

下面看看uevent_helper是谁
定义如下:
char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;

去.config中查看:
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
但是去/sbin目录下查看,并没有hotplug这个文件,所以肯定不是这个文件起作用,于是在上面的if (uevent_helper[0])
里加了一句调试信息,打印uevent_helper,内核启动相关打印信息如下:
uevent_helper is /sbin/hotplug 
uevent_helper is /sbin/hotplug 
s3c2410-rtc s3c2410-rtc: setting system clock to 2015-04-30 08:12:15 UTC (1430381535)
yaffs: dev is 32505858 name is "mtdblock2"
yaffs: passed flags ""
yaffs: Attempting MTD mount on 31.2, "mtdblock2"
yaffs: auto selecting yaffs2
block 646 is bad
yaffs_read_super: isCheckpointed 0
VFS: Mounted root (yaffs filesystem) on device 31:2.
Freeing init memory: 240K
Start Qtopia-2.2.0
uevent_helper is /sbin/mdev 
uevent_helper is /sbin/mdev

看到没,刚开始确实是/sbin/hotplug,但后来就变成了/sbin/mdev。很据上面信息,我们知道是在文件系统启动的过程中
发生改变的。文件系统启动过程中,改变mdev的只有“echo /sbin/mdev > /proc/sys/kernel/hotplug”,也确实是这个
导致了uevent_helper的改变。

涉及到的数据在/kernel/sysctl.c下
#if defined(CONFIG_HOTPLUG) && defined(CONFIG_NET)
{
.ctl_name = KERN_HOTPLUG,
.procname = "hotplug",
.data = &uevent_helper,
.maxlen = UEVENT_HELPER_PATH_LEN,
.mode = 0644,
.proc_handler = &proc_dostring,
.strategy = &sysctl_string,
},
#endif
至于为什么“echo /sbin/mdev > /proc/sys/kernel/hotplug”能改变uevent_helper就是proc虚拟文件系统的内容了,
这里不讨论。

其实设置mdev有三种方法,总结如下:
1、编译内核的时候直接配置CONFIG_UEVENT_HELPER_PATH,并且在之后的启动中不去修改uevent_helper,那么
uevent_helper代表的程序就是CONFIG_UEVENT_HELPER_PATH指定的程序

2、不管CONFIG_UEVENT_HELPER_PATH配置与否或如何设置,通过echo /sbin/mdev > /sys/kernel/uevent_helper
修改uevent_helper的内容,这个指令将会调用内核函数uevent_helper_store。过程涉及sysfs虚拟文件系统的
内容,这里不讨论。改变之后,/proc/sys/kernel/hotplug里的内容也会立即发生改变

3、不管CONFIG_UEVENT_HELPER_PATH配置与否或如何设置,通过echo /sbin/mdev > /proc/sys/kernel/hotplug
修改uevent_helper的内容.它的修改也会导致/sys/kernel/uevent_helper里的内容立即改变

对于上述的2、3两种方法,都是通过用户层的接口直接uevent_helper,所以谁在后面谁起作用

三、busybox源码分析

内核源码的最后是调用uevent_helper指定的用户程序,这个用户程序通常是mdev,那么mdev如何做的呢,来看一下
busybox的源码。源码基于busybox-1.23.1

int mdev_main(int argc UNUSED_PARAM, char **argv)
    xchdir("/dev");  // 先把目录改变到/dev下
    
    if (argv[1] && strcmp(argv[1], "-s") == 0) {  // 在文件系统启动的时候会调用 mdev -s,创建所有驱动设备节点
        putenv((char*)"ACTION=add"); // mdev -s 的动作是创建设备节点,所以为add
    
        if (access("/sys/class/block", F_OK) != 0) { // 当/sys/class/block目录不存在时,才扫描/sys/block
     /* Scan obsolete /sys/block only if /sys/class/block
      * doesn't exist. Otherwise we'll have dupes.
      * Also, do not complain if it doesn't exist.
      * Some people configure kernel to have no blockdevs.
      */
     recursive_action("/sys/block",
     ACTION_RECURSE | ACTION_FOLLOWLINKS | ACTION_QUIET,
     fileAction, dirAction, temp, 0);
   }
   
   /* 
    * 这个函数是递归函数,它会扫描/sys/class目录下的所有文件,如果发现dev文件,将按照
    * /etc/mdev.conf文件进行相应的配置。如果没有配置文件,那么直接创建设备节点 
    * 最终调用的创建函数是 make_device
    */
   recursive_action("/sys/class",    
   ACTION_RECURSE | ACTION_FOLLOWLINKS,
   fileAction, dirAction, temp, 0);
    
    }
    
    else{
        // 获得环境变量,环境变量是内核在调用mdev之前设置的
        env_devname = getenv("DEVNAME"); /* can be NULL */
   G.subsystem = getenv("SUBSYSTEM");
   action = getenv("ACTION");
   env_devpath = getenv("DEVPATH");
    
        snprintf(temp, PATH_MAX, "/sys%s", env_devpath);
    
        make_device(env_devname, temp, op);
    }
    
由以上代码分析可知,无论对于何种操作,最后都是调用make_device来创建节点,看一下这个函数

static void make_device(char *device_name, char *path, int operation)
    int major, minor, type, len;
    char *path_end = path + strlen(path);  //path_end指定path结尾处

major = -1;
    if (operation == OP_add) {
        strcpy(path_end, "/dev");  // 往path结尾处拷贝“/dev”,这时path=/sys/class/test/test_dev/dev
        len = open_read_close(path, path_end + 1, SCRATCH_SIZE - 1); // 打开并读取/sys/class/test/test_dev/dev
        *path_end = '\0';
        if (len < 1) {
            if (!ENABLE_FEATURE_MDEV_EXEC)
                return;
        } else if (sscanf(path_end + 1, "%u:%u", &major, &minor) == 2) { //从/sys/class/test/test_dev/dev获得主次设备号
            dbg1("dev %u,%u", major, minor);
        } else {
            major = -1;
        }
    }
    
    if (operation == OP_add && major >= 0) // 如果是add,即创建节点
        mknod(node_name, rule->mode | type, makedev(major, minor)) // 最终用mknod函数在/dev下创建设备节点
        
    if (operation == OP_remove && major >= -1)  // 如果是remove,即删除节点
        unlink(node_name);

创建节点最后无非还是调用mknod,当然在class_create和device_create自动创建设备节点时,也会在/sys/class下自动创建
和删除相关设备类和设备,这是sysfs的驱动内容,这里不讲

mdev详解【转】的更多相关文章

  1. CentOS网络配置详解

    转载于CentOS中文站:http://www.centoscn.com/CentOS/2015/0507/5376.html一.配置文件详解 在RHEL或者CentOS等Redhat系的Linux系 ...

  2. 转:CentOS---网络配置详解

    一.配置文件详解在RHEL或者CentOS等Redhat系的Linux系统里,跟网络有关的主要设置文件如下: /etc/host.conf         配置域名服务客户端的控制文件/etc/hos ...

  3. SDN 网络系统之 Mininet 与 API 详解

    SDN 网络系统之 Mininet 与 API 详解 Mininet 是轻量级的软件定义网络系统平台,同时提供了对 OpenFlow 协议的支持.本文主要介绍了 Mininet 的相关概念与特性,并列 ...

  4. Linux常用命令详解(week1_day1_2)--技术流ken

    本节内容 aliasunaliasunamesuhostnamehistorywhichwcwwhowhoamipingkillseqdudffreedate 命令详解 1. alias 设置.’查看 ...

  5. CentOS---网络配置详解 (转)

    一.配置文件详解在RHEL或者CentOS等Redhat系的Linux系统里,跟网络有关的主要设置文件如下: /etc/host.conf         配置域名服务客户端的控制文件/etc/hos ...

  6. Linux就业技术指导(五):Linux运维核心管理命令详解

    一,Linux核心进程管理命令 1.1 ps:查看进程 1.1.1 命令解释 功能说明 ps命令用于列出执行ps命令的那个时刻的进程快照,就像用手机给进程照了一张照片.如果想要动态地显示进程,就需要使 ...

  7. CentOS---网络配置详解

    一.配置文件详解 在RHEL或者CentOS等Redhat系的Linux系统里,跟网络有关的主要设置文件如下: /etc/host.conf         配置域名服务客户端的控制文件 /etc/h ...

  8. CentOS 6.9下KVM虚拟机网络Bridge(网桥)方式与NAT方式详解(转)

    摘要:KVM虚拟机网络配置的两种方式:NAT方式和Bridge方式.Bridge方式的配置原理和步骤.Bridge方式适用于服务器主机的虚拟化.NAT方式适用于桌面主机的虚拟化. NAT的网络结构图: ...

  9. [转] CentOS---网络配置详解

    原文地址: http://blog.chinaunix.net/uid-26495963-id-3230810.html 一.配置文件详解在RHEL或者CentOS等Redhat系的Linux系统里, ...

随机推荐

  1. 【解决】ERROR in xxx.js from UglifyJs

    当我们运行打包脚本npm run build或者打包iosweexpack build ios有可能会遇到以下报错 ERROR in index.js from UglifyJs ![](https: ...

  2. tp5 使用paginate分页获取数据对象之后 如何对对象进行数据添加

    tp5 使用paginate分页获取数据对象之后 如何对对象进行数据添加 大家都知道,在使用tp5的paginate获取分页数据之后,得到的是一个数据对象,但有时会碰到要对数据对象进行二次加工的情况, ...

  3. [NOIP2015]运输计划(树上差分+LCA+二分)

    Description 公元 2044 年,人类进入了宇宙纪元. L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所有星球. 小 P 掌管 ...

  4. Hi3518EV300编译U-Boot和内核报错:loadlocale.c:130: _nl_intern_locale_data: Assertion `cnt < (sizeof (_nl_value_type_LC_TIME) / sizeof (_nl_value_type_LC_TIME[0]))' failed. Aborted (core dumped)

    下载Hi3518EV300的SDK后编译内核和U-boot,发现爆出如下错误: scripts/kconfig/conf --silentoldconfig Kconfig Aborted (core ...

  5. 快速排序,对于相同元素的优化,c++

    #include<iostream>using namespace std; void middl(int &p,int &q,int &r)//找枢轴,然后把枢轴 ...

  6. vrpie在Visio Studio 中无法调试的问题

    最近这这几天一直在研究vrpie,之前不能调试,一调试就出问题,打开那个生成的htm文件是没问题的,最初的解决方法是不通过调试来打可那个htm页面,但是这样比较麻烦,因为经常需要和服务器交互,就只能用 ...

  7. laravel5.2总结--门面(facades)

    Facades 为应用程序的服务容器中可用的类提供了一个「静态」接口.   Laravel 本身附带许多的 facades,甚至你可能在不知情的状况下已经在使用他们!   xpower的静态接口(门面 ...

  8. Three Steps to Migrate Group Policy Between Active Directory Domains or Forests Using PowerShell

    Three Steps Ahead Have you ever wished that you had three legs? Imagine how much faster you could ru ...

  9. Android TV 开发(2)

    本文来自网易云社区 作者:孙有军 首先来看看拨号界面的配置代码: <LinearLayout xmlns:android="http://schemas.android.com/apk ...

  10. Asp.net自定义控件开发任我行(7)-注册自定义事件

    摘要 前面我们已经把嵌入资源讲完了,不知道大家有没有得到收益,本章主要讲自定义事件,也就是给TextBox注册一个点击事件. 引言 不知道道上的朋友有没有注意到TextBox控件没有点击事件,就连网上 ...