嵌入式linux之mdev机制分析

时间:2021-09-24 20:24:16

一、mdev分析

1、作用

首先我们先说明它的作用是什么,然后分析为什么能产生这种作用。
在系统启动和热插拔或动态加载驱动程序时,自动产生驱动程序所需的设备节点即/dev/xxx。同时可以设置一些脚本,并执行,脚本可以设置一下设备节点的读写权限,或者进行挂载等。 mdev是udev的简化版本,它也是通过读取内核信息来创建设备文件

以前的按键驱动程序,入口函数,为什么创建类呢,为什么在类下创建设备节点呢?是为了让mdev根据这些信息来创建设备节点。
按键驱动程序中我们这么做:

sixthdrv_class = class_create(THIS_MODULE, "sixth_drv");
sixthdrv_class_dev = device_create(sixthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons");
最后产生 /dev/buttons设备节点。

2、函数调用流程

device_create—>device_create_vargs—>device_register—>device_add—>kobject_uevent (&dev->kobj, KOBJ_ADD) —>kobject_uevent_env (kobj, action, NULL) —>

// action_string = "add";
const char *action_string = kobject_actions[action];
/* environment buffer */
env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
/* 设置环境变量 */
retval = add_uevent_var(env, "ACTION=%s", action_string);
retval = add_uevent_var(env, "DEVPATH=%s", devpath);
retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);

/* 调用应用程序: 比如mdev,启动脚本 echo /sbin/mdev > /proc/sys/kernel/hotplug 设置了uevent_helper为“/sbin/mdev“*/

argv [0] = uevent_helper;
argv [1] = (char *)subsystem;
argv [2] = NULL;
retval = call_usermodehelper(argv[0], argv,env->envp, UMH_WAIT_EXEC);

调用了busybox文件系统中的 mdev.c  
修改内核,添加参数打印:

嵌入式linux之mdev机制分析
uevent_helper = /sbin/mdev
envp[0] = HOME=/
envp[1] = PATH=/sbin:/bin:/usr/sbin:/usr/bin
envp[2] = ACTION=add
envp[3] = DEVPATH=/class/sixth_drv/buttons
envp[4] = SUBSYSTEM=sixth_drv
envp[5] = SEQNUM=720
envp[6] = MAJOR=252
envp[7] = MINOR=0

3、分析busybox mdev.c

mdev_main
temp = /sys/class/sixth_drv/buttons
make_device(temp, 0);
/* 确定设备文件名,类型,主次设备号 */
device_name = bb_basename(path); = "buttons"
'c' == > 字符设备节点
根据"/sys/class/sixth_drv/buttons/dev"的内容确定主次设备号
mknod(device_name, mode | type, makedev(major, minor)
最后mdev调用了mknod创建设备节点,从而在/dev下产生/dev/buttons设备节点。到此知道了原来/dev下的设备节点是这么创建的!


如何在设备创建的时候执行一些脚本呢?比如修改设备节点权限,自动挂接U盘等。

需要添加/etc/mdev.conf配置文件。mdev.c会解析此配置文件,一旦匹配到设备,则可以执行一些脚本命令。

代码分析:

int mdev_main(int argc UNUSED_PARAM, char **argv)
{
//mdev -s:mdev -s is to be run during boot to scan /sys and populate /dev.
if (argv[1] && strcmp(argv[1], "-s") == 0){}
snprintf(temp, PATH_MAX, "/sys%s", env_path);
//static void make_device(char *path, int delete)
make_device(temp, /*delete:*/ 0);
{
/* Determine device name, type, major and minor */
device_name = (char*) bb_basename(path);
mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST
}
}

3.1 mdev.conf的格式:

<device regex> <uid>:<gid> <octal permissions> [<@|$|*> <command>]
device regex:正则表达式,表示哪一个设备
uid: owner
gid: 组ID
octal permissions:以八进制表示的属性
@:创建设备节点之后执行命令
$:删除设备节点之前执行命令
*: 创建设备节点之后 和 删除设备节点之前 执行命令
command:要执行的命令


3.2 写mdev.conf

1.
leds 0:0 777
led1 0:0 777
led2 0:0 777
led3 0:0 777

2.
leds?[123]? 0:0 777


3.
leds?[123]? 0:0 777 @ echo create /dev/$MDEV > /dev/console


4.
leds?[123]? 0:0 777 * if [ $ACTION = "add" ]; then echo create /dev/$MDEV > /dev/console; else echo remove /dev/$MDEV > /dev/console; fi


5.
leds?[123]? 0:0 777 * /bin/add_remove_led.sh


把命令写入一个脚本:
add_remove_led.sh


#!/bin/sh
if [ $ACTION = "add" ];
then
echo create /dev/$MDEV > /dev/console;
else
echo remove /dev/$MDEV > /dev/console;
fi


6. U盘自动加载
sda[1-9]+ 0:0 777 * if [ $ACTION = "add" ]; then mount /dev/$MDEV /mnt; else umount /mnt; fi


7.
sda[1-9]+ 0:0 777 * /bin/add_remove_udisk.sh


add_remove_udisk.sh
#!/bin/sh
if [ $ACTION = "add" ];
then
mount /dev/$MDEV /mnt;
else
umount /mnt;
fi

二、扩展etc配置分析

1、busybox文件目录中/doc/mdev.txt中写到

Here's a typical code snippet from the initscript:
[0] mount -t proc proc /proc
[1] mount -t sysfs sysfs /sys
[2] echo /sbin/mdev >/proc/sys/kernel/hotplug
[3] mdev -s
Of course, a more "full" setup wouldentail executing this before the previous
code snippet:
[4] mount -t tmpfs -o size=64k,mode=0755 tmpfs/dev
[5] mkdir /dev/pts
[6] mount -t devpts devpts /dev/pts


所以在/etc/init.d/rcS配置如下

#!/bin/sh
ifconfig eth0 192.168.1.17
mount -a
mkdir /dev/pts #使用内存文件系统,减少对flash的读写
mount -t devpts devpts /devpts #/dev/pts用来支持外部网络链接(telnet:远程访问摄像头)的虚拟终端
echo /sbin/mdev > /proc/sys/kernel/hotplug #设置内核,当有设备插拔时调用/bin/mdev程序
mdev -s #在/dev目录下生成内核支持的所有设备的节点

2、在busybox文件目录中/doc/mdev.txt还有如下关键说明

The simple explanation here is that
[1] youneed to have /sys mounted before executing mdev.
[4]make sure /dev is a tmpfs filesystem (assuming you're running out of flash).
Thenyou want to [5] create the /dev/pts mount point and finally [6] mount thedevpts filesystem on it

 所以要修改/etc/fstab来自动挂载文件系统,修改etc/init.d/rcS加入要自动运行的命令。

  #device mount-point type options dump fsck order
proc /proc proc defaults 0 0
tmpfs/tmp tmpfs defaults 0 0

sysfs /sys sysfs defaults 0 0
tmpfs/dev tmpfs defaults 0 0

3.linux虚拟文件系统

   1、linux有几种虚拟的文件系统类型(只存在于内核,在物理内存不存在,不过我们可以用挂载的方式将它在物理内存上进行映射,如mount-t proc proc /proc;将cpu,mem等信息挂载在硬盘中),比如proc,sysfs,tmpfs,root.
  2、proc是内核用的文件系统,用于显示内核信息及改变内核参数;
  3、sysfs文件系统用来管理和显示各种设备的运行参数及设备的层次结构。//能减少对flash的对写
  4、tmpfs:能解决临时的设备(/dev)或文件(/tmp)访问速度慢的缺点。


4./etc/fstab 的用途

/etc/fstab 是设定分割区分 mount目录相关的档案,开机时会依这个档案的內容 mount 档案系統,mount档案系統的命令为:
   linux# mount -av
会依 /etc/fstab的內容 mount 进"除了 root 以外的所有档案系統"。
root 是在开机时由核心所 mount 进來的。root 先由核心mount 成 read-only,然后由使用者手动remount 成 read-write。


5. /etc/inittab

init根据/etc/inittab配置文件来执行相应的脚本进行系统初始化,如设置键盘、字体,装载模块,设置网络;
 init 是在核心 mount 进 root 后,第一個执行的程式 (第一个 process)


开机后执行上述文件的过程:

开机==>mount -t root root /root (此句内核自动执行)==> /etc/fstab ==>/etc/inittab ==> 执行/etc/rc.d/目录下的脚本(包括/etc/init.d/rcS)