转自: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. 用jq给img添加error事件

    <img src="xxxx.jpg" alt="" /> <script> $(document).ready(function(){ ...

  2. mysql索引详细描述与应用场景

    索引的数据结构: (1)一般是B+tree:MySql使用最频繁的一个索引数据结构,数据结构以平衡树的形式来组织,因为是树型结构,所以更适合用来处理排序,范围查找等功能. (2)Hash:Hsah索引 ...

  3. 利用python生成图形验证码

    validCode.py import random from io import BytesIO from PIL import Image, ImageDraw, ImageFont def ge ...

  4. 1022 D进制的A+B (20)(20 分)

    1022 D进制的A+B (20)(20 分) 输入两个非负10进制整数A和B(<=\(2^{30}-1\)),输出A+B的D (1 < D <= 10)进制数. 输入格式: 输入在 ...

  5. Leetcode 96. 不同的二叉搜索树

    题目链接 https://leetcode.com/problems/unique-binary-search-trees/description/ 题目描述 给定一个整数 n,求以 1 ... n ...

  6. 笔记-网络-抓包-wireshark

    笔记-网络-抓包-wireshark 1.      开始 环境:win8笔记本,无线网 1.1.    无线网卡设置 因为需抓捕无线网卡上的数据包,需要进行一项设置,如捕获有线网卡,无需设置. 打开 ...

  7. 矩阵乘法在hadoop的实现

    先随机生成一个矩阵,矩阵的行数与列数由用户输入: #!/bin/bashfor i in `seq 1 $1`do for j in `seq 1 $2` do s=$((RANDOM%100)) e ...

  8. Python 频繁请求问题: [Errno 104] Connection reset by peer

    Table of Contents 1. 记遇到的一个问题:[Errno 104] Connection reset by peer 记遇到的一个问题:[Errno 104] Connection r ...

  9. Apache Compress-使用

    Apache Compress 是什么? Apache  提供的文件压缩工具. 运行环境 jdk 1.7 commons-compress 1.15 测试代码 package com.m.basic; ...

  10. 使用 Anime 类在 XNA 中创建小动画(十一)

    平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...