hotplug就是热拔插,在linux里面,这个功能是通过class_device_create这个函数来实现的,那么我们来分析下这个函数:
class_device_create(cls, NULL, MKDEV(major, 0), NULL, "dma"); /* /dev/dma */
class_device_register(class_dev);
class_device_add(class_dev);
kobject_uevent(&class_dev->kobj, KOBJ_ADD);
kobject_uevent_env(kobj, action, NULL);
action_string = action_to_string(action);
/* 分配保存环境变量的内存 */
envp = kzalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL);
/* 设置环境变量 */
envp[i++] = "HOME=/";
scratch = buffer;
envp [i++] = scratch;
.................................
/* 可以加打印语句将下面的变量打印出来,详见注释1 */
char *argv [3];
argv [0] = uevent_helper;
argv [1] = (char *)subsystem;
argv [2] = NULL;
call_usermodehelper (argv[0], argv, envp, 0);
/* 开启一个用户模式下的应用程序,其中path为应用程序的路径 */
call_usermodehelper_keys(path, argv, envp, NULL, wait);
注释1:
if (uevent_helper[0]) {
char *argv [3];
/* 蓝色字体是我们加进去的打印语句 */
printk("100ask:uevent_helper=%s\n",uevent_helper);
for(i=0;envp[i];i++)
{
printk("envp[%d]=%s\n",i,envp[i]);
}
argv [0] = uevent_helper;
argv [1] = (char *)subsystem;
argv [2] = NULL;
call_usermodehelper (argv[0], argv, envp, 0);
}
在内核中加入如上的打印语句之后,重新编译内核:make uImage
然后用新内核启动,会看到很多打印环境变量的信息。
我们选择一个包含函数:class_device_create的驱动程序,加载后打印如下信息:
100ask:uevent_helper=/sbin/mdev //我们看到uevent_helper是mdev
envp[0]=HOME=/
envp[1]=PATH=/sbin:/bin:/usr/sbin:/usr/bin
envp[2]=ACTION=add //ACTION=add,记住它
envp[3]=DEVPATH=/module/third_drv
envp[4]=SUBSYSTEM=module
envp[5]=SEQNUM=716
100ask:uevent_helper=/sbin/mdev
envp[0]=HOME=/
envp[1]=PATH=/sbin:/bin:/usr/sbin:/usr/bin
envp[2]=ACTION=add
envp[3]=DEVPATH=/class/third_drv/buttons
envp[4]=SUBSYSTEM=third_drv
envp[5]=SEQNUM=717
envp[6]=MAJOR=252 //主设备号
envp[7]=MINOR=0 //次设备号
于是我们知道call_usermodehelper 将会开启应用程序mdev,那么我们猜测mdev就根据主次设备号等环境变量来创建设备节点!
其实uevent_helper=mdev是在/etc/init.d/rcS里面的设置的,因为有这么一句:
echo /sbin/mdev > /proc/sys/kernel/hotplug
那么mdev到底做了什么呢?我们来看一下:busybox mdev.c 这个文件:
sprintf(temp, "/sys%s", env_path);//temp=/sys/class/third_drv/buttons,详见注释2
make_device(temp, 0);
strcat(path, "/dev");//temp=/sys/class/third_drv/buttons/dev,这个dev文件里面放的就是主次设备号
/* 确定设备名,类型,主次设备号 */
device_name = bb_basename(path); //确定设备名
type = path[5]=='c' ? S_IFCHR : S_IFBLK; //获得类型
sscanf(temp, "%d:%d", &major, &minor); //扫描主次设备号
mknod(device_name, mode | type, makedev(major, minor)) //根据设备名,主次设备号以及类型来创建设备节点
注释2:
DEVPATH=/class/third_drv/buttons
env_path = getenv("DEVPATH");
sprintf(temp, "/sys%s", env_path);
于是乎:temp=/sys/class/third_drv/buttons