udev机制一之udev原理

问题背景:用户插入U盘后需要自动打开U盘目录,但是发现U盘每次挂载点都不一样,测试两个U盘的挂载点分别是/media/sda1和/media/sda4,所以就需要了解一下linux的udev机制了

  这应该是对udev和mdev的一个理解,我们下次可以再深入一些.

dev 和mdev 是两个使用uevent 机制处理热插拔问题的用户空间程序,两者的实现机理不同。udev 是基于netlink 机制的,它在系统启动时运行了一个deamon 程序udevd,通过监听内核发送的uevent 来执行相应的热拔插动作,包括创建/删除设备节点,加载/卸载驱动模块等等。mdev 是基于uevent_helper 机制的,它在系统启动时修改了内核中的uevnet_helper 变量(通过写/proc/sys/kernel/hotplug),值为“/sbin/mdev”。这样内核产生uevent 时会调用uevent_helper 所指的用户级程序,也就是mdev,来执行相应的热拔插动作。udev 使用的netlink 机制在有大量uevent 的场合效率高,适合用在PC 机上;而mdev 使用的uevent_helper 机制实现简单,适合用在嵌入式系统中。另外要说明的一点是,uevent_helper 的初始值在内核编译时时可配置的,默认值为/sbin/hotplug。如果想修改它的值,写/proc/sys/kernel/hotplug 文件就可以了,例如:

echo “/sbin/mdev” > /proc/sys/kernel/hotplug

我们知道,每个设备注册后会自动产生设备节点,以字符设备为例,不管是misc_register还是配合cdev_add接口注册驱动,都需要调用device_create来创建设备节点,就从这个函数入手,看其在3.2.0内核版本的注册流程

device_create

device_create_vargs

device_register(struct device *dev)

device_add(dev);

kobject_uevent(&dev->kobj, KOBJ_ADD);

kobject_uevent_env(kobj, action, NULL);

char *action_string = kobject_actions[action]; //action_string = "add"

struct kobj_uevent_env *env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);

add_uevent_var(env, "ACTION=%s", action_string); //env[0] = "ACTION=add"

add_uevent_var(env, "DEVPATH=%s", devpath); //这两个先不关注

add_uevent_var(env, "SUBSYSTEM=%s", subsystem);

argv [0] = uevent_helper;

argv [1] = (char *)subsystem;

argv [2] = NULL;

add_uevent_var(env, "HOME=/"); //设置其他环境变量

add_uevent_var(env, "PATH=/sbin:/bin:/usr/sbin:/usr/bin");

//调用argv[0]指向的应用程序来根据环境变量env->envp一些参数来创建设备节点

call_usermodehelper(argv[0], argv, env->envp, UMH_WAIT_EXEC);

到这里,我们基本知道,内核在启动时候已经设置了一些环境变量,可以在终端用命令env看一下

TSLIB_TSDEVICE=/dev/input/event0

QTDIR=/usr/gui/qt

PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin

HOME=/home/root

我的板子由于需要QT,所以一些环境变量已经改过或者添加了新的

那么上面提到用户空间的argv[0]即uevent_helper到底是什么呢?

从内核源码看uevent_helper初始化地方看是全部设置为"/sbin/hotplug"

可是我在板子上执行 ls /sbin/hotplug 命令并没有发现这个程序,简单的办法就是打印一下了。

在调用call_usermodehelper之前加上下面打印

 

printk("uevent_helper = %s\n", uevent_helper);

for(i=0; env->envp[i]; i++)

{

printk("envp[%d] = %s\n", i,env->envp[i]);

}

重新编译内核然后启动截取部分打印如下(每注册一个驱动都会出现这一串打印):

看门狗注册

uevent_helper = /sbin/hotplug

envp[0] = ACTION=add

envp[1] = DEVPATH=/devices/platform/omap/omap_wdt/misc/watchdog

envp[2] = SUBSYSTEM=misc

envp[3] = MAJOR=10

envp[4] = MINOR=130

envp[5] = DEVNAME=watchdog

envp[6] = SEQNUM=520

envp[7] = HOME=/

envp[8] = PATH=/sbin:/bin:/usr/sbin:/usr/bin

按键注册

uevent_helper = /sbin/hotplug

envp[0] = ACTION=add

envp[1] = DEVPATH=/devices/platform/gpio-keys/input/input2

envp[2] = SUBSYSTEM=input

envp[3] = PRODUCT=19/1/1/100

envp[4] = NAME="gpio-keys" envp[5] = PHYS="gpio-keys/input0" envp[6] = PROP=0 envp[7] = EV=3 envp[8] = KEY=14001 40000000 envp[9] = MODALIAS=input:b0019v0001p0001e0100-e0,1,kramlsfw envp[10] = SEQNUM=572 envp[11] = HOME=/ envp[12] = PATH=/sbin:/bin:/usr/sbin:/usr/bin

我插入的2GB U盘 uevent_helper = /sbin/hotplug envp[0] = ACTION=add envp[1] = DEVPATH=/bus/hid/drivers/generic-usb envp[2] = SUBSYSTEM=drivers envp[3] = SEQNUM=551 envp[4] = HOME=/ envp[5] = PATH=/sbin:/bin:/usr/sbin:/usr/bin sd 0:0:0:0: [sda] 3919872 512-byte logical blocks: (2.00 GB/1.86 GiB)

uevent_helper仍然是hotplug,查了一些资料,貌似是说2.6以后udev机制替换了hotplug,而嵌入式中的udev即mdev,所以要去看下文件系统busybox的mdev源码 相关资料链接:

http://blog.chinaunix.net/uid-14753126-id-2978523.html

http://www.cnblogs.com/hnrainll/archive/2011/06/23/2088250.html

mdev.c mdev_main action = getenv("ACTION"); //插入u盘就是"add" env_path = getenv("DEVPATH"); sprintf(temp, "/sys%s", env_path); //u盘的 temp = "/sys/bus/hid/drivers/generic-usb" make_device(temp, 0); device_name = bb_basename(path); //根据上面temp取出设备名字 if (ENABLE_FEATURE_MDEV_CONF) { //如果配置了支持mdev.conf选项,那么就解析里边内容并执行 .... } type = path[5]=='c' ? S_IFCHR : S_IFBLK; //判断是字符还是块设备 sscanf(temp, "%d:%d", &major, &minor) //取出主次设备号 mknod(device_name, mode | type, makedev(major, minor)) //创建设备节点

 

这就是设备从内核到用户空间创建设备节点的整个过程,而如果我们要对设备节点做手脚,就需要从mdev下手了 busybox源码有个帮助文档mdev.txt讲了应该如何去操作mdev.conf,从而实现一些高级功能,比如U盘自动挂载

 

文档很短,其中重要就是下面这段内容: ---------------------------------------------------------------------------- ------------- MDEV Config (/etc/mdev.conf) ------------- Mdev has an optional config file for controlling ownership/permissions of device nodes if your system needs something more than the default root/root 660 permissions. The file has the format: : For example: hd[a-z][0-9]* 0:3 660 The config file parsing stops at the first matching line. If no line is matched, then the default of 0:0 660 is used. To set your own default, simply create your own total match like so: .* 1:1 777 If you also enable support for executing your own commands, then the file has 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. The command is executed via the system() function (which means you're giving a command to the shell), so make sure you have a shell installed at /bin/sh. For your convenience, the shell env var $MDEV is set to the device name. So if the device 'hdc' was matched, MDEV would be set to "hdc". ----------------------------------------------------------------------------

大体意思就是: 配置文件格式: : [<@|$|*>   
  
 
 
] 各个参数代表的含义如下: device regex:正则表达式,表示哪一个设备 uid: owner gid: 组ID octal permissions:以八进制表示的属性 @:创建设备节点之后执行命令 $:删除设备节点之前执行命令 *: 创建设备节点之后 和 删除设备节点之前 执行命令 command:要执行的命令 所以如果要想让U盘自动挂载怎么办呢?就需要在/etc下建立mdev.conf文件,并编辑内容如下: sda[1-9]+ 0:3 660 * if [ $ACTION = "add" ]; then mount /dev/$MDEV /mnt; else umount /mnt; fi 其中sda[1-9]+是一个正则表达式,表示sda1...sda9(U盘默认设备节点名),0:3 660就是 : 这部分内容,剩下的一大串shell命令就是挂载命令了 搞懂了这些再去操作U盘相关修改就很容易了。

 

---------------------------------------------------------------------------- 公司fs的配置非常麻烦,不符合上述流程,后是通过搜"mount"关键字找到其实是/etc/udev/scripts/mount.sh操控这个流程 那么需要做如下内容修改: 第23-25行: ! test -d "/media/$name" && mkdir -p "/media/$name" if ! $MOUNT -t auto $DEVNAME "/media/$name" 改为: ! test -d "/upan" && mkdir -p "/upan" if ! $MOUNT -t auto $DEVNAME "/upan" 这样无论哪个u盘都会挂载到/upan目录下了

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值