zoukankan      html  css  js  c++  java
  • 修改mdev的配置以支持U盘的自动挂载

    原文:https://www.cnblogs.com/lifexy/p/7891883.html

    1.当我们每次插入u盘后,都会自动创键U盘的设备节点/dev/sda%d

    这是因为里面调用了device_create()实现的, busybox的mdev机制就会根据主次设备号等信息,在/dev下创建设备节点,如下图所示:

     

    /dev/sda:表示整个U盘
    /dev/sda1:表示U盘的第一个分区

    而想使用上面的sda1设备节点,读写数据时,还需要使用mount /dev/sda1  /mnt,来挂载u盘才行,会显得非常麻烦,如下图所示:

     

    2.其实,可以在/etc/mdev.conf文件里加入一行语句就能实现自动装载u盘,也可以在里面干其它与设备节点相关的事

    2.1而/etc/mdev.conf又是什么?

    它是属于mdev的一个配置文件,而mdev之前就讲过了,它主要的功能是管理/dev目录底下的设备节点

    当系统中有自动注册设备节点的时候,mdev就会调用/etc/mdev.conf一次, 该文件可以实现与设备节点相关的事,比如自动装载usb,打印创建的设备节点信息等

    3.我们首先来分析device_create(),是如何来调用到/etc/mdev.conf的,后面再讲如何使用mdev.conf(也可以直接跳过,直接看下面第4小节,如何使用)

    (PS: 之前创建字符设备节点用的class_device_create(),其实是和device_create功能差不多)

    3.1 device_create()最终调用了:device_create()->device_register()->device_add():

    复制代码
    device_create()->device_register()->device_add()函数如下所示:
    
    int class_device_add(struct class_device *class_dev)
    {
           ... ...
           kobject_uevent(&class_dev->kobj, KOBJ_ADD);         // KOBJ_ADD是一个枚举值
                  //调用了kobject_uevent_env(kobj, action, NULL);              // action=KOBJ_ADD
    }
    复制代码

    3.2 device_create()->device_register()->device_add()->kobject_uevent_env()函数如下所示:

    复制代码
    int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,char *envp_ext[])
    {
           char **envp;
           char *buffer;
           char *scratch;
           int i = 0;
           ... ...
    
           /* 通过KOBJ_ADD获取字符串"add",所以action_string="add"  */
           action_string = action_to_string(action);              // action=KOBJ_ADD
    
                                                           
           /* environment index */
           envp = kzalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL);      //分配一个环境变量索引值
    
           /* environment values */
        buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);     //分配一个环境变量缓冲值      
    
    /* event environemnt for helper process only */
    /*设置环境变量*/
           envp[i++] = "HOME=/";
           envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
           scratch = buffer;
           envp [i++] = scratch;
           scratch += sprintf(scratch, "ACTION=%s", action_string) + 1;  //"ACTION= add"
           envp [i++] = scratch;
           scratch += sprintf (scratch, "DEVPATH=%s", devpath) + 1;
           envp [i++] = scratch;
           scratch += sprintf(scratch, "SUBSYSTEM=%s", subsystem) + 1;
           ... ...
           /*调用应用程序,比如mdev*/
           if (uevent_helper[0]) {
                char *argv [3];
                  argv [0] = uevent_helper;       // uevent_helper[]= "/sbin/hotplug";
                  argv [1] = (char *)subsystem;
                  argv [2] = NULL;
                  call_usermodehelper (argv[0], argv, envp, 0);        //调用应用程序,根据传入的环境变量参数来创建设备节点
           }
    }
    复制代码

    从上面的代码和注释来看,最终通过*argv[], *envp[]两个字符串数组里面存的环境变量参数来创建设备节点的

    3.2接下来便在kobject_uevent_env()函数里添加打印信息, 然后重新烧内核:

     

    3.3然后我们以注册一个按键驱动为例 

    输入 insmod key.ko,打印了以下语句:

    复制代码
    class_device: argv[0]=/sbin/mdev                 //调用mdev
    
    class_device: argv[1]=sixth_dev                      //类名
    
    class_device: envp[0]=HOME=/
    
    class_device: envp[1]=PATH=/sbin:/bin:/usr/sbin:/usr/bin
    
    class_device: envp[2]=ACTION=add             //add:表示添加设备节点,  若=remove:表示卸载设备节点
    
    class_device: envp[3]=DEVPATH=/class/sixth_dev/buttons   //设备的路径
    
    class_device: envp[4]=SUBSYSTEM=sixth_dev                //类名
    
    class_device: envp[5]=SEQNUM=745
    
    class_device: envp[6]=MAJOR=252                          //主设备号
    
    class_device: envp[7]=MINOR=0
    复制代码

    3.4最终这些参数根据/sbin/mdev就进入了busybox的mdev.c的mdev_main()函数里:

    复制代码
    int mdev_main(int argc, char **argv)
    {
    ... ...
    action = getenv("ACTION");           //获取传进来的执行参数,它等于“add”,则表示创建设备节点
    env_path = getenv("DEVPATH");      //获取设备的路径“/class/sixth_dev/buttons”
    sprintf(temp, "/sys%s", env_path);   //指定temp (真正设备路径)为“/sys/class/sixth_dev/buttons”
    
    if (!strcmp(action, "remove"))           //卸载设备节点
                        make_device(temp, 1);
    
    else if (!strcmp(action, "add")) {       //创建设备节点
                         make_device(temp, 0);
     ... ... 
    }
    复制代码

    3.5最终调用mdev_main ()->make_device()函数来创建/卸载设备节点,该函数如下所示:

    复制代码
    static void make_device(char *path, int delete) //delete=0:创建, delete=1:卸载
    {
           /*判断创建的设备节点是否是有效的设备*/
           if (!delete) {
                  strcat(path, "/dev");
                  len = open_read_close(path, temp + 1, 64);
                  *temp++ = 0;
                  if (len < 1) return;
           }
    
    device_name = bb_basename(path);    //通过设备路径,来获取要创建/卸载的设备节点名称
                          //例: path =“/sys /class/sixth_dev/buttons”,那么device_name=“buttons”
    
     
    
    type = path[5]=='c' ? S_IFCHR : S_IFBLK;     //判断如果是在/sys/class/目录下,那么就是字符设备
                                                  //因为块设备,是存在/sys/block/目录下的
    
    
    /* 如果配置了支持mdev.conf选项,那么就解析里边内容并执行   */
     if (ENABLE_FEATURE_MDEV_CONF) { 
           /* mmap the config file */
    fd = open("/etc/mdev.conf", O_RDONLY);     //调用/etc/mdev.conf配置文件
         
         ... ...
    //开始操作 mdev.conf配置文件 } if (!delete) { //如果是创建设备节点 if (sscanf(temp, "%d:%d", &major, &minor) != 2) return; //获取主次设备号

            /*调用mknod ()创建字符设备节点*/ if (mknod(device_name, mode | type, makedev(major, minor)) && errno != EEXIST) bb_perror_msg_and_die("mknod %s", device_name); if (major == root_major && minor == root_minor) symlink(device_name, "root"); /*若配置了支持mdev.conf选项,则调用chown命令来改变属主,默认uid和gid=0 */ if (ENABLE_FEATURE_MDEV_CONF) chown(device_name, uid, gid); } if (delete) unlink(device_name); //如果是卸载设备节点 }
    复制代码

    从上面的代码和注释分析到,要使用mdev.conf配置文件,还需要配置busybox的menuconfig, 使mdev支持mdev.conf选项才行

    如下图,进入busybox目录,然后输入make menuconfig,发现我们已经配置过了该选项了

     

    4.接下来,便来看看如何使用mdev.conf,  参考busybox-1.7.0/docs/mdev.txt文档

    使用方法如下所示:

    the format:

           <device regex> <uid>:<gid> <octal permissions> [<@|$|*> <command>]

    The special characters have the meaning:

    @ Run after creating the device.

    $ Run before removing the device.

    * Run both after creating and before removing the device.

    大概就是:

    配置文件格式:

    <device regex> <uid>:<gid> <octal permissions> [<@|$|*> <command>]  

    各个参数代表的含义如下:  

    device regex:

    设备的正则表达式,来表达哪一个设备 ,正则表达式讲解链接:https://deerchao.net/tutorials/regex/regex.htm

    插讲:掌握下面几个简单的规则基本上就可以了

    .  表示任意字符(换行符除外)
    * 表示重复0次或更多次
    +表示重复1次或更多次
    ?表示重复0次或1次
    [ ]表示这些字符里的某一个。例如[abc],就表示abc中的某一个。[1-9],就表示1到9中的某一个

    uid: 

    owner       (uid,gid:注册设备节点时,就会被chown命令调用,来改变设备的属主,默认都填0即可)

    gid: 

    组ID  

    octal permissions:

    以八进制表示的权限值,会被chmod命令调用,来更改设备的访问权限,默认填660即可

    @ : 创建设备节点之后执行命令  

    $  :  删除设备节点之前执行命令  

    *   : 创建设备节点之后 和 删除设备节点之前 执行命令  

    command : 要执行的命令 。

    以加载/卸载myleds为例,介绍上面的规则。

    //写mdev.conf
    //leds led1  led2   led3,如何用一个正则表达式来表示它们。
    //写法1:
    vim /ect/mdev.conf
    leds  0:0  777
    led2  0:0  777
    led3  0:0  777
    led4  0:0  777
    
    //执行insmod myleds.ko
    ls -l /dev/led*
    //可以看到
    crwxrwxrwx  1 0    0   231, ..... /dev/led1
    crwxrwxrwx  1 0    0   231, ..... /dev/led2
    crwxrwxrwx  1 0    0   231, ..... /dev/led3
    crwxrwxrwx  1 0    0   231, ..... /dev/leds
    //从上面可以看出,这么写没有问题,但是就是非常麻烦。最笨的一种方法。
    
    //写法2:利用规则来匹配这4个设备节点
    vi /etc/mdev.conf
    leds?[123]?  0:0 777
    //执行insmod myleds.ko
    ls -l /dev/led*
    //可以看到
    crwxrwxrwx  1 0    0   231, ..... /dev/led1
    crwxrwxrwx  1 0    0   231, ..... /dev/led2
    crwxrwxrwx  1 0    0   231, ..... /dev/led3
    crwxrwxrwx  1 0    0   231, ..... /dev/leds
    
    //3下面继续改进:
    leds?[123]? 0:0  777 @ echo create 某个设备,(//到底是哪个设备呢?可以用环境变量MDEV来表示是哪个设备节点)
    //即mdev.conf文件的内容如下:
    
    leds?[123]? 0:0  777 @ echo create /dev/$MDEV > /dev/console
    
     
    //当执行insmod myleds.ko时,将会打印下面的内容:
    create       /dev/leds
    create  /dev/led1
    create  /dev/led2
    create  /dev/led3
    
    //4.继续改进,加载的时候打印create,卸载的时候打印remove
    vi /ect/mdev.conf
    
    leds?[123]? 0:0  777 * if [$ACTION = "add" ]; then echo create /dev/$MDEV > /dev/console; else echo remove /dev/$MDEV > /dev/console; fi
    
    insmod myleds.ko
    create  /dev/leds
    create  /dev/led1
    create  /dev/led2
    create  /dev/led3
    
    rmmod myleds
    remove  /dev/leds
    remove  /dev/led1
    remove  /dev/led2
    remove  /dev/led3
    
    //5.把命令写入一个脚本
    add_remove_led.sh
    
    #!/bin/sh
    if [$ACTION = "add" ]; 
    then 
        echo create /dev/$MDEV > /dev/console; 
    else 
        echo remove /dev/$MDEV > /dev/console;
    fi
    
    chmod +x /bin/add_remove_led.sh
    
    //将add_remove_led.sh脚本放入/bin目录下
    vi /etc/mdev.conf
    
    leds?[123]? 0:0  777 * /bin/add_remove_led.sh

    5.接下来便来使用mdev.conf,实现u盘自动装载

    vi /etc/mdev.conf

    添加以下一句:

    sda[1-9]+ 0:0 660 * if [ $ACTION = "add" ]; then mount /dev/$MDEV /mnt; else umount /mnt; fi


    [1-9] : 匹配1~9的数字,

    +   :  重复匹配一次或更多次

    $ACTION=="add"   :表示注册设备节点,否则就是注销设备节点

    /dev/$MDEV      :表示要创建/注销的那个设备节点

    所以当我们插上u盘,自动创建了/dev/sda1时,mdev便会进入/etc/mdev.conf配置文件,然后执行mount /dev/ 命令,即可自动装载U盘,如下图所示:

     

    可以利用cat /proc/mounts来进行查看

    输入ls /dev/sda1  -l,可以看到都是通过mdev.conf里配置信息来创建的设备节点,如下图所示:

    上面那种写法不直观,我们还是用一个脚本对命令来进行封装。

    vi  /bin/add_remove_udisk.sh

    if [ $ACTION = "add" ];
    then
       mount /dev/$MDEV /mnt;
    else
       umount /mnt;
    fi

    chmod +x /bin/add_remove_udisk.sh

    vi /etc/mdev.conf

    sda[1-9]+ 0:0 660 * /bin/add_remove_udisk.sh

    而取出u盘时,同样自动umount  /mnt来卸载

     

     
  • 相关阅读:
    [DB] 数据库的连接
    JS leetcode 翻转字符串里的单词 题解分析
    JS leetcode 拥有最多糖果的孩子 题解分析,六一快乐。
    JS leetcode 搜索插入位置 题解分析
    JS leetcode 杨辉三角Ⅱ 题解分析
    JS leetcode 寻找数组的中心索引 题解分析
    JS leetcode 移除元素 题解分析
    JS leetcode 最大连续1的个数 题解分析
    JS leetcode 两数之和 II
    JS leetcode 反转字符串 题解分析
  • 原文地址:https://www.cnblogs.com/-glb/p/12589926.html
Copyright © 2011-2022 走看看