目录
title: hotplug/mdev机制
date: 2019/1/9 19:35:14 toc: true ---hotplug/mdev机制
框架
我们以前创建设备的时候,使用class_create
来自动创建设备,是利用了mdev
根据我们的提供的信息来创建设备节点
kobject_uevent_env
我们使用drv_class_dev
来创建设备,具体的实例如下
drv_class = class_create(THIS_MODULE, "drv");drv_class_dev = class_device_create(drv_class, NULL, MKDEV(major, 0), NULL, "xyz%d", minor);
继续分析class_device_create
,可以看到最后调用kobject_uevent_env
来调用我们自己设置的辅助函数uevent_helper
,可以在内核加入打印函数打印这个变量
drv_class = class_create(THIS_MODULE, "drv");drv_class_dev = class_device_create(drv_class, NULL, MKDEV(major, 0), NULL, "xyz%d", minor); class_device_register(class_dev); class_device_initialize(class_dev); class_device_add(class_dev); //KOBJ_ADD = (__force kobject_action_t) 0x01, /* exclusive to core */ sysfs_create_link(&class_dev->kobj,&class_dev->dev->kobj, "device"); kobject_uevent(&class_dev->kobj, KOBJ_ADD); // 设置环境变量后调用辅助函数 kobject_uevent_env(kobj, action, NULL) { action_string = action_to_string(action); //return "add"; /* environment index */ envp = kzalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL); /* environment values */ buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL); /* complete object path */ devpath = kobject_get_path(kobj, GFP_KERNEL); /* event environemnt for helper process only */ envp[i++] = "HOME=/"; envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; // 先将指针scratch指向分配到的buffer // 再赋值到envp 环境变量中 /* default keys */ scratch = buffer; envp [i++] = scratch; scratch += sprintf(scratch, "ACTION=%s", action_string) + 1; envp [i++] = scratch; scratch += sprintf (scratch, "DEVPATH=%s", devpath) + 1; envp [i++] = scratch; scratch += sprintf(scratch, "SUBSYSTEM=%s", subsystem) + 1; for (j = 0; envp_ext && envp_ext[j]; j++) envp[i++] = envp_ext[j]; ... //我们自己在这里加入打印函数,查看下环境变量和uevent_helper printk("userhelp=%s\n",uevent_helper); for (i=0;envp[i];i++) { printk("envp[%d]=%s\n",i,envp[i]); } printk("argv[1]=%s\n",subsystem); // 辅助函数,也就是我们自动创建设备的mdev /* call uevent_helper, usually only enabled during early boot */ if (uevent_helper[0]) { char *argv [3]; argv [0] = uevent_helper; argv [1] = (char *)subsystem; argv [2] = NULL; call_usermodehelper (argv[0], argv, envp, 0); } }
启动后加载一个驱动,打印如下,起始启动内核的时候也有许多的打印,userhelp=/sbin/hotplug
// filename =dri_src.cmajor=register_chrdev(0, "drv_register", &drv_fops); // 注册, 告诉内核drv_class = class_create(THIS_MODULE, "drv_class");drv_class_dev = class_device_create(drv_class, NULL, MKDEV(major, 0), NULL, "xyz%d", minor);
可以看到个信息包含了主次设备号,文件路径,模块的名字等,这里注意一下,当你修改了一个模块 a.ko 为 b.ko ,加载b.ko 后使用lsmod
显示的依然是 a 模块,这里的信息也是 a 模块的
argv[1]
其实就是envp[4]
也就是类名 # insmod dri_src.kouserhelp=/sbin/mdevenvp[0]=HOME=/envp[1]=PATH=/sbin:/bin:/usr/sbin:/usr/binenvp[2]=ACTION=addenvp[3]=DEVPATH=/module/dri_src #模块名,一般就是文件名,但这个文件名指的是当时编译好的名字,也就是C文件的名字,生成ko后修改名字无效 envp[4]=SUBSYSTEM=moduleenvp[5]=SEQNUM=754argv[1]=moduleuserhelp=/sbin/mdevenvp[0]=HOME=/envp[1]=PATH=/sbin:/bin:/usr/sbin:/usr/binenvp[2]=ACTION=addenvp[3]=DEVPATH=/class/drv_class/xyz0 #设备文件的描述,最终会生成文件 /dev/xyz0 #输入子系统这里是 envp[3]=DEVPATH=/class/input/input1/event1envp[4]=SUBSYSTEM=drv_class #类的名字 class_create创建,如果是输入子系统这里是 # envp[4]=SUBSYSTEM=inputenvp[5]=SEQNUM=755envp[6]=MAJOR=252envp[7]=MINOR=0argv[1]=drv_class #就是类名# rmmod dri_src #卸载模块的时候使用ACTION=removeuserhelp=/sbin/mdevenvp[0]=HOME=/envp[1]=PATH=/sbin:/bin:/usr/sbin:/usr/binenvp[2]=ACTION=removeenvp[3]=DEVPATH=/class/drv_class/xyz0envp[4]=SUBSYSTEM=drv_classenvp[5]=SEQNUM=756envp[6]=MAJOR=252envp[7]=MINOR=0userhelp=/sbin/mdevenvp[0]=HOME=/envp[1]=PATH=/sbin:/bin:/usr/sbin:/usr/binenvp[2]=ACTION=removeenvp[3]=DEVPATH=/class/drv_classenvp[4]=SUBSYSTEM=classenvp[5]=SEQNUM=757userhelp=/sbin/mdevenvp[0]=HOME=/envp[1]=PATH=/sbin:/bin:/usr/sbin:/usr/binenvp[2]=ACTION=removeenvp[3]=DEVPATH=/module/dri_srcenvp[4]=SUBSYSTEM=moduleenvp[5]=SEQNUM=758
mdev_main
接下来就去执行函数/sbin/mdev
,也就是busybox的应用程序,定义在busybox-1.7.0\util-linux\mdev.c
make_device(temp, 0);
,getenv
是个库函数,我们之前在kobject_uevent_env
设置了参数 mdev_main(int argc, char **argv) //扫描模式,一般是上电用的 scan //我们自己实际用的 action = getenv("ACTION"); env_path = getenv("DEVPATH"); sprintf(temp, "/sys%s", env_path); if (!strcmp(action, "remove")) make_device(temp, 1); else if (!strcmp(action, "add")) { make_device(temp, 0);
make_device
kernel 传递的DEVPATH
是envp[3]=DEVPATH=/class/drv_class/xyz0
,也就是参数实际是/sys/class/drv_class/xyz0
- 先判断是否已经有设备文件被创建了,也就是去
/sys/class/drv_class/xyz0/dev
是否存在 这个文件包含了主次设备号
// 先判断是否设备文件已经存在,也就是判断 /sys/class/drv_class/xyz0/dev是否存在 这个文件包含了主次设备号//# cat /sys/class/drv_class/xyz0/dev//252:0// strcat 拼接字符串 if (!delete) { strcat(path, "/dev"); len = open_read_close(path, temp + 1, 64); *temp++ = 0; if (len < 1) return; }
- 然后判断设备类型,就去读取到
class
的c
字符,就判断是字符设备了
/// sys/class/drv_class/xyz0 device_name = bb_basename(path); //bb_basename 从前往后找到最后一个"/",这里就是找到文件名 type = path[5]=='c' ? S_IFCHR : S_IFBLK;
- 如果有配置文件,根据配置文件读取
if (ENABLE_FEATURE_MDEV_CONF) { fd = open("/etc/mdev.conf", O_RDONLY); }
- 创建实际的设备节点,如果有配置文件,还需要手动更改设备文件属性
if (!delete) { // 从设备文件的描述读取主次设备号 /sys/class/drv_class/xyz0/dev if (sscanf(temp, "%d:%d", &major, &minor) != 2) return; // mknod 创建设备文件 type=c 就是字符设备 makedev 包含了主次设备号 if (mknod(device_name, mode | type, makedev(major, minor)) && errno != EEXIST) if (major == root_major && minor == root_minor) symlink(device_name, "root"); // 如果有配置文件,还需要手动更改设备文件属性 if (ENABLE_FEATURE_MDEV_CONF) chown(device_name, uid, gid); }
- 所以我们如果需要使用配置文件自动创建设备文件,则需要使能
ENABLE_FEATURE_MDEV_CONF
,在make menuconfig
搜索mdev
,可以看到已经启用了
│ -> Linux System Utilities ││ -> mdev (MDEV [=y]) ││ -> Support /etc/mdev.conf (FEATURE_MDEV_CONF [=y])
mdev.conf
如何使用这个配置文件去自动创建设备文件? 我们搜索文档busybox-1.7.0/docs/mdev.txt
查看如下格式,也就是发现设备文件(前/后)来执行命令
The file has the format:: For example: hd[a-z][0-9]* 0:3 660 : [<@|$|*> ]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: owner gid: 组ID octal permissions:以八进制表示的属性 用户组 读写执行+读写执行+读写执行 110+110+000=660=crwcrw--- @:创建设备节点之后执行命令 $:删除设备节点之前执行命令 *: 创建设备节点之后 和 删除设备节点之前 执行命令 command:要执行的命令
配置文件学习
存在这样一个驱动,自动创建4个设备文件,代码仓库的4th
for (minor = 0; minor < 4; minor++){ firstdrv_class_dev[minor] = class_device_create(firstdrv_class, NULL, MKDEV(major, minor), NULL, "xyz%d", minor);}
自动创建设备如下
# ls /dev/xyz* -lcrw-rw---- 1 0 0 252, 0 Jan 4 07:13 /dev/xyz0crw-rw---- 1 0 0 252, 1 Jan 4 07:13 /dev/xyz1crw-rw---- 1 0 0 252, 2 Jan 4 07:13 /dev/xyz2crw-rw---- 1 0 0 252, 3 Jan 4 07:13 /dev/xyz3
更改属性
使用字符全匹配,更改权限为777,先卸载驱动
# vi /etc/mdev.confxyz0 0:0 777xyz1 0:0 777xyz2 0:0 777# ls /dev/xyz* -lcrwxrwxrwx 1 0 0 252, 0 Jan 4 07:21 /dev/xyz0crwxrwxrwx 1 0 0 252, 1 Jan 4 07:21 /dev/xyz1crwxrwxrwx 1 0 0 252, 2 Jan 4 07:21 /dev/xyz2crw-rw---- 1 0 0 252, 3 Jan 4 07:21 /dev/xyz3
使用正则匹配
# vi /etc/mdev.confxyz[0123]? 0:0 777# ls /dev/xyz* -lcrwxrwxrwx 1 0 0 252, 0 Jan 4 07:25 /dev/xyz0crwxrwxrwx 1 0 0 252, 1 Jan 4 07:25 /dev/xyz1crwxrwxrwx 1 0 0 252, 2 Jan 4 07:25 /dev/xyz2crwxrwxrwx 1 0 0 252, 3 Jan 4 07:25 /dev/xyz3
@:创建设备节点之后执行命令
这里使用@
表示在创建设备节点后执行命令
# vi /etc/mdev.confxyz[0123]? 0:0 777 @ echo create /dev/$MDEV > /dev/consoleinsmod dri.ko # create /dev/xyz2create /dev/xyz3create /dev/xyz0create /dev/xyz1
$:删除设备节点之前执行命令
# vi /etc/mdev.confxyz[0123]? 0:0 777 $ echo del /dev/$MDEV > /dev/consoleinsmod dri.ko # del /dev/xyz2del /dev/xyz3del /dev/xyz0del /dev/xyz1
*: 创建之后 和 删除之前
注意 在$ACTION
前有个空格,不能少
# cat /etc/mdev.confxyz[0123]? 0:0 777 * if [ $ACTION = "add" ];then echo create /dev/$MDEV > /dev/console; else echo del /dev/$MDEV > dev/console ;fi#insmod dri_4th.ko# create /dev/xyz2create /dev/xyz3create /dev/xyz0create /dev/xyz1#rmmod dridel /dev/xyz1del /dev/xyz0del /dev/xyz2del /dev/xyz3
使用脚本
创建一个脚本文件mdev.sh
,放到一个指定的目录,加入执行权限hmod +x /mnt/mdev.sh
#!/bin/shif [ $ACTION = "add" ]; then echo create /dev/$MDEV > /dev/console; else echo remove /dev/$MDEV > /dev/console; fi
修改配置文件
# chmod +x /mnt/mdev.sh# cat /etc/mdev.confxyz[0123]? 0:0 777 * /mnt/mdev.sh
然后会和上面同样的结果
U盘自动挂载
u盘插入
usb 1-1: new full speed USB device using s3c2410-ohci and address 3usb 1-1: configuration #1 chosen from 1 choicescsi1 : SCSI emulation for USB Mass Storage devicessd 1:0:0:0: [sda] 60555264 512-byte hardware sectors (31004 MB)sd 1:0:0:0: [sda] Write Protect is offsd 1:0:0:0: [sda] Assuming drive cache: write throughsd 1:0:0:0: [sda] 60555264 512-byte hardware sectors (31004 MB)sd 1:0:0:0: [sda] Write Protect is offsd 1:0:0:0: [sda] Assuming drive cache: write through sda: sda1 sda2
查看下U盘设备,这个u盘有两个分区,一个是PE,所以sda1
是第一个分区,sda2
是第二个分区
# ls -l /dev/sda*brw-rw---- 1 0 0 8, 0 Jan 4 06:57 /dev/sdabrw-rw---- 1 0 0 8, 1 Jan 4 06:57 /dev/sda1brw-rw---- 1 0 0 8, 2 Jan 4 06:57 /dev/sda2
手动挂载
mount /dev/sda1 /mnt
自动挂载
创建文件,加入配置文件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
- + 表示重复前面1次及以上 也就是表示1~无穷大的数字
0:0
uid,默认为0就好了660
权限*
,创建设备节点之后 和 删除设备节点之前 执行命令/dev/$MDEV
,表示要创建/注销的那个设备节点
或者使用脚本mdev.sh
,更好看
#!/bin/shif [ $ACTION = "add" ]; then mount /dev/$MDEV /mnt; else umount /mnt; fi
常用的命令
ifconfig eth0 192.168.95.200mount -t nfs -o nolock,vers=2 192.168.95.222:/home/book/stu /mnt使用nfs在uboot下下载内核 nfs 30000000 12.16.45.222:/uimage bootm 30000000
env
输入env
可以查看当前shell的环境变量
# envUSER=rootOLDPWD=/etcHOME=/TERM=vt102PATH=/sbin:/usr/sbin:/bin:/usr/binSHELL=/bin/shPWD=/mnt# envUSER=rootOLDPWD=/mntHOME=/TERM=vt102PATH=/sbin:/usr/sbin:/bin:/usr/binSHELL=/bin/shPWD=/
附录 U盘插入的打印信息
# usb 1-1: new full speed USB device using s3c2410-ohci and address 3userhelp=/sbin/mdevenvp[0]=HOME=/envp[1]=PATH=/sbin:/bin:/usr/sbin:/usr/binenvp[2]=ACTION=addenvp[3]=DEVPATH=/devices/platform/s3c2410-ohci/usb1/1-1envp[4]=SUBSYSTEM=usbenvp[5]=SEQNUM=761envp[6]=MAJOR=189envp[7]=MINOR=2envp[8]=DEVTYPE=usb_deviceenvp[9]=PHYSDEVBUS=usbenvp[10]=DEVICE=/proc/bus/usb/001/003envp[11]=PRODUCT=951/1666/100envp[12]=TYPE=0/0/0envp[13]=BUSNUM=001envp[14]=DEVNUM=003argv[1]=usbuserhelp=/sbin/mdevenvp[0]=HOME=/envp[1]=PATH=/sbin:/bin:/usr/sbin:/usr/binenvp[2]=ACTION=addenvp[3]=DEVPATH=/class/usb_endpoint/usbdev1.3_ep00envp[4]=SUBSYSTEM=usb_endpointenvp[5]=SEQNUM=762envp[6]=MAJOR=253envp[7]=MINOR=2envp[8]=PHYSDEVPATH=/devices/platform/s3c2410-ohci/usb1/1-1envp[9]=PHYSDEVBUS=usbenvp[10]=PHYSDEVDRIVER=usbargv[1]=usb_endpointusb 1-1: configuration #1 chosen from 1 choiceuserhelp=/sbin/mdevenvp[0]=HOME=/envp[1]=PATH=/sbin:/bin:/usr/sbin:/usr/binenvp[2]=ACTION=addenvp[3]=DEVPATH=/devices/platform/s3c2410-ohci/usb1/1-1/1-1:1.0envp[4]=SUBSYSTEM=usbenvp[5]=SEQNUM=763envp[6]=DEVTYPE=usb_interfaceenvp[7]=PHYSDEVBUS=usbenvp[8]=DEVICE=/proc/bus/usb/001/003envp[9]=PRODUCT=951/1666/100envp[10]=TYPE=0/0/0envp[11]=INTERFACE=8/6/80envp[12]=MODALIAS=usb:v0951p1666d0100dc00dsc00dp00ic08isc06ip50argv[1]=usbscsi1 : SCSI emulation for USB Mass Storage devicesuserhelp=/sbin/mdevenvp[0]=HOME=/envp[1]=PATH=/sbin:/bin:/usr/sbin:/usr/binenvp[2]=ACTION=addenvp[3]=DEVPATH=/class/scsi_host/host1envp[4]=SUBSYSTEM=scsi_hostenvp[5]=SEQNUM=764envp[6]=PHYSDEVPATH=/devices/platform/s3c2410-ohci/usb1/1-1/1-1:1.0/host1argv[1]=scsi_hostuserhelp=/sbin/mdevenvp[0]=HOME=/envp[1]=PATH=/sbin:/bin:/usr/sbin:/usr/binenvp[2]=ACTION=addenvp[3]=DEVPATH=/class/usb_endpoint/usbdev1.3_ep81envp[4]=SUBSYSTEM=usb_endpointenvp[5]=SEQNUM=765envp[6]=MAJOR=253envp[7]=MINOR=3envp[8]=PHYSDEVPATH=/devices/platform/s3c2410-ohci/usb1/1-1/1-1:1.0envp[9]=PHYSDEVBUS=usbenvp[10]=PHYSDEVDRIVER=usb-storageargv[1]=usb_endpointuserhelp=/sbin/mdevenvp[0]=HOME=/envp[1]=PATH=/sbin:/bin:/usr/sbin:/usr/binenvp[2]=ACTION=addenvp[3]=DEVPATH=/class/usb_endpoint/usbdev1.3_ep02envp[4]=SUBSYSTEM=usb_endpointenvp[5]=SEQNUM=766envp[6]=MAJOR=253envp[7]=MINOR=4envp[8]=PHYSDEVPATH=/devices/platform/s3c2410-ohci/usb1/1-1/1-1:1.0envp[9]=PHYSDEVBUS=usbenvp[10]=PHYSDEVDRIVER=usb-storageargv[1]=usb_endpointscsi 1:0:0:0: Direct-Access Kingston DataTraveler 3.0 PMAP PQ: 0 ANSI: 6userhelp=/sbin/mdevenvp[0]=HOME=/envp[1]=PATH=/sbin:/bin:/usr/sbin:/usr/binenvp[2]=ACTION=addenvp[3]=DEVPATH=/devices/platform/s3c2410-ohci/usb1/1-1/1-1:1.0/host1/target1:0:0/1:0:0:0envp[4]=SUBSYSTEM=scsienvp[5]=SEQNUM=767envp[6]=PHYSDEVBUS=scsienvp[7]=MODALIAS=scsi:t-0x00argv[1]=scsiuserhelp=/sbin/mdevenvp[0]=HOME=/envp[1]=PATH=/sbin:/bin:/usr/sbin:/usr/binenvp[2]=ACTION=addenvp[3]=DEVPATH=/class/scsi_disk/1:0:0:0envp[4]=SUBSYSTEM=scsi_diskenvp[5]=SEQNUM=768envp[6]=PHYSDEVPATH=/devices/platform/s3c2410-ohci/usb1/1-1/1-1:1.0/host1/target1:0:0/1:0:0:0envp[7]=PHYSDEVBUS=scsienvp[8]=PHYSDEVDRIVER=sdargv[1]=scsi_disksd 1:0:0:0: [sda] 60555264 512-byte hardware sectors (31004 MB)sd 1:0:0:0: [sda] Write Protect is offsd 1:0:0:0: [sda] Assuming drive cache: write throughsd 1:0:0:0: [sda] 60555264 512-byte hardware sectors (31004 MB)sd 1:0:0:0: [sda] Write Protect is offsd 1:0:0:0: [sda] Assuming drive cache: write through sda: sda1 sda2userhelp=/sbin/mdevenvp[0]=HOME=/envp[1]=PATH=/sbin:/bin:/usr/sbin:/usr/binenvp[2]=ACTION=addenvp[3]=DEVPATH=/block/sdaenvp[4]=SUBSYSTEM=blockenvp[5]=SEQNUM=769envp[6]=MINOR=0envp[7]=MAJOR=8envp[8]=PHYSDEVPATH=/devices/platform/s3c2410-ohci/usb1/1-1/1-1:1.0/host1/target1:0:0/1:0:0:0envp[9]=PHYSDEVBUS=scsienvp[10]=PHYSDEVDRIVER=sdargv[1]=blockuserhelp=/sbin/mdevenvp[0]=HOME=/envp[1]=PATH=/sbin:/bin:/usr/sbin:/usr/binenvp[2]=ACTION=addenvp[3]=DEVPATH=/block/sda/sda1envp[4]=SUBSYSTEM=blockenvp[5]=SEQNUM=770envp[6]=MINOR=1envp[7]=MAJOR=8envp[8]=PHYSDEVPATH=/devices/platform/s3c2410-ohci/usb1/1-1/1-1:1.0/host1/target1:0:0/1:0:0:0envp[9]=PHYSDEVBUS=scsienvp[10]=PHYSDEVDRIVER=sdargv[1]=blockuserhelp=/sbin/mdevenvp[0]=HOME=/envp[1]=PATH=/sbin:/bin:/usr/sbin:/usr/binenvp[2]=ACTION=addenvp[3]=DEVPATH=/block/sda/sda2envp[4]=SUBSYSTEM=blockenvp[5]=SEQNUM=771envp[6]=MINOR=2envp[7]=MAJOR=8envp[8]=PHYSDEVPATH=/devices/platform/s3c2410-ohci/usb1/1-1/1-1:1.0/host1/target1:0:0/1:0:0:0envp[9]=PHYSDEVBUS=scsienvp[10]=PHYSDEVDRIVER=sdargv[1]=blocksd 1:0:0:0: [sda] Attached SCSI removable diskuserhelp=/sbin/mdevenvp[0]=HOME=/envp[1]=PATH=/sbin:/bin:/usr/sbin:/usr/binenvp[2]=ACTION=addenvp[3]=DEVPATH=/class/scsi_device/1:0:0:0envp[4]=SUBSYSTEM=scsi_deviceenvp[5]=SEQNUM=772envp[6]=PHYSDEVPATH=/devices/platform/s3c2410-ohci/usb1/1-1/1-1:1.0/host1/target1:0:0/1:0:0:0envp[7]=PHYSDEVBUS=scsienvp[8]=PHYSDEVDRIVER=sdargv[1]=scsi_device