uevent, user space event. 内核与用户空间的一种通信机制,基于netlink机制,主要用于设备驱动模型,例如热插拔。
1、调用/sbin/mdev的流程分析
在驱动程序中经常出现class_device_create这个函数,它用来在/dev目录下创建各个设备的设备节点,那么,这个是怎么实现的呢?下面就来分析这个过程的实现,直接进到class_device_create函数中层层分析它。
class_device_create class_device_register class_device_add kobject_uevent(&class_dev->kobj, KOBJ_ADD); kobject_uevent_env(kobj, action, NULL); // action_string = "add"; action_string = action_to_string(action); /* 分配、保存环境变量的内存 */ /* environment values */ buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL); /* 设置环境变量 */ nvp [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; /* 调用应用程序:比如mdev */ /* 在/etc/init.d/rcS 中的echo /sbin/mdev > /proc/sys/kernel/hotplug指定了应用程序*/ argv [0] = uevent_helper; // = "/sbin/mdev" argv [1] = (char *)subsystem; argv [2] = NULL; call_usermodehelper (argv[0], argv, envp, 0);
从上面的调用关系可以看出,最终内核会调用用户空间的/sbin/mdev程序,并且使用该程序的环境变量。
2、mdev源码分析
接下来看到busy_box下的mdev机制的实现,直接看到mdev.c文件,列出调用关系:
mdev_main temp = "/sys/class/buttons/buttons" make_device(temp, 0); /* 确定设备文件名,类型,主设备号,次设备号 */ path = "/sys/class/buttons/buttons" device_name = bb_basename(path); = "buttons" type = path[5]=='c' ? S_IFCHR : S_IFBLK; // ='c'是字符设备节点 根据 "/sys/class/buttons/buttons/dev"的内容确定主次设备号 mknod(device_name, mode | type, makedev(major, minor)
从上面的调用关系可以看出mdev最终通过mknod生成了设备节点
3、利用mdev机制实现U盘的挂接
分析mdev源码可以知道,它还可以根据/etc/mdev.conf这个配置文件来执行某个命令,这个文件的格式如下:
mdev.conf的格式: <device regex> <uid>:<gid> <octal permissions> [<@|$|*> <command>]
每个字段的含义如下:
device regex:正则表达式,表示哪一个设备 uid:owner gid:组id octal permissions:以八进制表示的属性 @ :创建设备节点之后执行命令 $: 删除设备节点之前执行命令 * :创建设备节点之后执行命令 和 删除设备节点之前执行命令 command:要执行的命令
正则表达式的介绍如下:
正则表达式: 1、在电脑上查文件,*.c(*表示通配符,表示任意字符) 2、用正则表达式 .表示任意字符(换行符除外) *表示重复0次或更多次 +重复1次或更多次 ?重复0次或1次 [...]表示中括号里面的字符的某一个
假设需要自动挂载U盘,它的/etc/mdev.conf可以这么写:
sda[1-9]+ 0:0 777 * if [ $ACTION = "add" ]; then mount /dev/$MDEV /mnt;else umount /mnt; fi
其中sda[1-9]+表示sda1-sda9中的一个或多个。后面跟着shell命令,如果是有U盘加载的话,那么挂接到/mnt上,否则的话,就卸载。
当然也可以把shell命令写成脚本的形式,脚本存放在/bin/add_remove_udisk.sh
sda[1-9]+ 0:0 777 * /bin/add_remove_udisk.sh /bin/add_remove_udisk.sh #!/bin/sh if [ $ACTION = "add" ]; then mount /dev/$MDEV /mnt; else umount /mnt; fi