历史上的今天
返回首页

历史上的今天

今天是:2025年07月21日(星期一)

正在发生

2021年07月21日 | 25.Linux-实现U盘自动挂载

2021-07-21 来源:eefocus

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

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

在这里插入图片描述

  而想使用上面的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:


        : [<@|$|*> ]


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:


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


  uid:


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


  gid:


  组ID


  octal permissions:


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


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


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


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


  command : 要执行的命令


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

vi /etc/mdev.conf

1

  添加以下一句:


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

1

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


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


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


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


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

在这里插入图片描述

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

推荐阅读

史海拾趣

Globaltech Semiconductor Co Ltd公司的发展小趣事

随着公司业务的不断发展壮大,Globaltech开始着手进行全球化布局。公司在全球多个地区设立了研发中心和销售中心,以便更好地服务全球客户。同时,公司还注重可持续发展和社会责任,积极参与公益事业和环保活动。这些举措不仅提升了公司的社会形象和品牌价值,也为公司的长期发展奠定了坚实的基础。

以上五个故事展示了Globaltech Semiconductor Co., Ltd.在电子行业中的发展历程和关键里程碑。通过技术创新、市场拓展、研发实力提升、战略调整和全球化布局等措施,公司不断壮大自身实力,并在全球半导体行业中占据了一席之地。

Compact公司的发展小趣事

近年来,电子行业经历了多次技术变革和市场洗牌。面对这些挑战,Compact公司积极调整战略,加大研发投入,以适应市场变化。同时,公司还通过优化生产流程、降低成本等方式提高竞争力。在行业变革中,Compact公司不仅成功应对了挑战,还抓住了机遇,实现了跨越式发展。

维峰电子(WCON)公司的发展小趣事

质量是企业的生命线。维峰电子始终将质量管理作为公司发展的重中之重。公司建立了完善的质量管理体系,从原材料采购、生产过程到产品检验等各个环节都进行严格把控。同时,公司还引入了先进的检测设备和技术,确保产品的质量和性能达到最高标准。这些措施的实施不仅提升了产品的竞争力,也赢得了客户的信任和好评。

Electro Technik Industries公司的发展小趣事

为了扩大市场份额,ETI开始积极寻求国际合作。公司先后与多家国际知名企业建立了战略合作关系,共同研发新产品、开拓市场。同时,ETI还积极参加国际电子展览会,展示公司的技术实力和产品优势,吸引了众多客户的关注。通过不断努力,ETI的产品逐渐打入国际市场,成为了一家具有全球影响力的电子企业。

Axiomtek公司的发展小趣事

为了扩大市场份额,ETI开始积极寻求国际合作。公司先后与多家国际知名企业建立了战略合作关系,共同研发新产品、开拓市场。同时,ETI还积极参加国际电子展览会,展示公司的技术实力和产品优势,吸引了众多客户的关注。通过不断努力,ETI的产品逐渐打入国际市场,成为了一家具有全球影响力的电子企业。

Alps Alpine Co Ltd公司的发展小趣事

随着公司规模的扩大和产品线的增加,ETI开始注重品质管理。公司建立了一套完善的品质管理体系,从原材料采购到生产过程再到成品检验都严格把关。同时,ETI还积极推行精益生产、六西格玛等先进管理方法,提高了生产效率和产品质量。这些措施使ETI的产品在市场上获得了良好的口碑和信誉。

问答坊 | AI 解惑

嵌入式C编程与Atmel AVR

嵌入式C编程与Atmel AVR.pdf…

查看全部问答>

LED驱动如何布线合理(简易)--推荐

刚看了一款LED驱动IC的布线:采用A704    85~265输入 Vin 18W (15S16P) LED日光灯 Layout 及组件选择注意事项 大致如下– 1¨ A704 IC 下方除了A704GND 外, 不要有其它走线 2¨ HV220 和 LED+ , LED- 走线尽量不在上下层走线重疉或 ...…

查看全部问答>

初学ARM,牛哥牛姐们有什么宝贵经验分享一下呗!!

初学ARM,牛哥牛姐们有什么宝贵经验分享一下呗!!…

查看全部问答>

net平台下wince开发语言的选择?

以前我用c#来做wince程序开发,觉得好方便。 现在公司用c++来做,我新建了几个项目(c++)发现和c#建立的有很大的差别, c#是基于net compack framework的,c++是基于mfc和win32的。 请问大家,是这样的吗?我第一次做,不太懂。 另外,wince现 ...…

查看全部问答>

s3c2410 EINT17 中断注册的问题

大家好 我这边用S3C2410 的EINT11   EINT17两个中断,注册使用的是  s3c_irqext_type(),low_level 触发。 测试发现,只有EINT11是好用的。查文档(manual:9-17)看到EINT17比EINT11多一些解释。 1)5v Tolerant input 2)EX ...…

查看全部问答>

问个BSP下调用PB下的lib的初学者问题

那个C:\\WINCE500\\PLATFORM\\COMMON\\SRC\\COMMON\\INTR\\BASE\\map.c编译生成了一个oal_intr.lib,在C:\\WINCE500\\PLATFORM\\SMDK2440A\\Src\\Inc\\oal_intr.h这个东西声明了map.c里面的函数,是不是表示包含了oal_intr.h这个文件就能调用map.c ...…

查看全部问答>

关于2440 bootloader的体系

请教高手:    2440 bootloader究竟是怎样运行的?总共十几个程序究竟先加载哪个?后加载哪个?我发现2440mon.c里面语句怎么不是按顺序运行的啊?…

查看全部问答>

ip 包如何直接提交协议栈?

如何将获得的ip包直接提交协议栈呢? 又如何从协议栈出口获取发送下来的ip包呢?…

查看全部问答>

关于ISP烧录

100pin和48pin的stm32f103都能够反复烧录正常. 唯独64pin的stm32f103fv8只能烧录一次,第二次就不能进入boot. 试验了2片都一样.是不是这个批号的产品有问题. 激光打字如下: arm stm32f103 r8t6 2205v 93 mlt 22 925…

查看全部问答>

[求解]IAR中如何将数字转为字符串

我利用430的uart和电脑通信想通过字符串方式传递数据遇到一个问题查了很多头文件都没找到 我想将 数字转为字符串  就像  12345 ---> \"12345\"又如  12.56--->\"12.56\" 但是没找到这个函数 希望大侠指点一下 (p ...…

查看全部问答>