linux mdev -s没有运行,mdev详解
一、概述
mdev是busybox提供的一个工具,用在嵌入式系统中,相当于简化版的udev,作用是在系统启动和热插拔或动态加载驱动程序时,
自动创建设备节点。文件系统中的/dev目录下的设备节点都是由mdev创建的。
在加载驱动过程中,根据驱动程序,在/dev下自动创建设备节点。
以下内容摘自busybox-1.23.1的mdev.txt文件:
Mdev has two primary uses: initial population and dynamic updates. Both
require sysfs support in the kernel and have it mounted at /sys. For dynamic
updates, you also need to have hotplugging enabled in your kernel.
Here's a typical code snippet from the init script:
[0] mount -t proc proc /proc
[1] mount -t sysfs sysfs /sys
[2] echo /sbin/mdev > /proc/sys/kernel/hotplug
[3] mdev -s
Alternatively, without procfs the above becomes:
[1] mount -t sysfs sysfs /sys
[2] sysctl -w kernel.hotplug=/sbin/mdev
[3] mdev -s
Of course, a more "full" setup would entail 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
The simple explanation here is that [1] you need to have /sys mounted before
executing mdev. Then you [2] instruct the kernel to execute /sbin/mdev whenever
a device is added or removed so that the device node can be created or destroyed.
Then you [3] seed /dev with all the device nodes that were created while the system
was booting.
For the "full" setup, you want to [4] make sure /dev is a tmpfs filesystem
(assuming you're running out of flash). Then you want to [5] create the
/dev/pts mount point and finally [6] mount the devpts filesystem on it.
二、用法
这篇博文所涉及到的用法很简单:
1、在/etc/init.d/rcS脚本里有“mdev -s”
解释:在系统启动时,通过执行“mdev -s”扫描/sys/class和/sys/block,在目录中查找dev文件。例如:/sys/class/tty/tty0/dev,
它的内容为"4:0",即主设备号是4,次设备号是0,dev的上一级目录为设备名,这里是tty0。/sys/class/下的每个文件夹都代表
着一个子系统。
2、在/etc/init.d/rcS脚本里有“echo /sbin/mdev > /proc/sys/kernel/hotplug”,即是把/sbin/mdev写到/proc/sys/kernel/hotplug文件里
解释:当有热插拔事件产生时,内核会调用/proc/sys/kernel/hotplug文件里指定的应用程序来处理热插拔事件
根据mdev.txt的说明可知在使用mdev之前要满足下面的条件:
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t tmpfs -o size=64k,mode=0755 tmpfs /dev
mkdir /dev/pts
mount -t devpts devpts /dev/pts
说明:所有的设备都可以在/sys/class下找到,这个文件夹下的每一个文件夹下代表了一类设备,表示类设备的文件夹下也有
文件夹,这些文件夹代表设备。如:/sys/class/test/test_dev/ ,test代表类,如net、tty、sound;test_dev代表某个
设备,它的名字和/dev下的设备节点名字是一样的
三、内核源码分析
分析一下相关内核源码,看上面提到的功能是如何实现的。
平时我们添加驱动时,如果想自动创建设备节点调用的函数是class_create和device_create。class_create是创建类设备,
就是在/sys/class/创建一个文件夹,这个文件夹代表一类设备,这个文件夹里会包含device_create创建的设备,也是一个
文件夹。
下面就从device_create入手,看是怎么实现自动创建设备节点的。源码基于linux-2.6.30.4内核
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
retval = device_register(dev);
return device_add(dev);
kobject_uevent(&dev->kobj, KOBJ_ADD);
return kobject_uevent_env(kobj, action, NULL); // action = KOBJ_ADD
const char *action_string = kobject_actions[action]; // action_string = "add"
……
//把相关信息存到环境变量里,ACTION代表操作类型,DEVPATH为设备在class下存在的路径,SUBSYSTEM为class_create创建的设备类
//ACTION=add , DEVPATH=/class/test/test_dev , SUBSYSTEM=test
retval = add_uevent_var(env, "ACTION=%s", action_string);
if (retval)
goto exit;
retval = add_uevent_var(env, "DEVPATH=%s", devpath);
if (retval)
goto exit;
retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
if (retval)
goto exit;
……
if (uevent_helper[0]){
argv [0] = uevent_helper;
argv [1] = (char *)subsystem;
argv [2] = NULL;
//内核空间调用用户空间程序,调用的程序由argv [0] = uevent_helper指定
retval = call_usermodehelper(argv[0], argv,
env->envp, UMH_WAIT_EXEC);
下面看看uevent_helper是谁
定义如下:
char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
去.config中查看:
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
但是去/sbin目录下查看,并没有hotplug这个文件,所以肯定不是这个文件起作用,于是在上面的if (uevent_helper[0])
里加了一句调试信息,打印uevent_helper,内核启动相关打印信息如下:
uevent_helper is /sbin/hotplug
uevent_helper is /sbin/hotplug
s3c2410-rtc s3c2410-rtc: setting system clock to 2015-04-30 08:12:15 UTC (1430381535)
yaffs: dev is 32505858 name is "mtdblock2"
yaffs: passed flags ""
yaffs: Attempting MTD mount on 31.2, "mtdblock2"
yaffs: auto selecting yaffs2
block 646 is bad
yaffs_read_super: isCheckpointed 0
VFS: Mounted root (yaffs filesystem) on device 31:2.
Freeing init memory: 240K
Start Qtopia-2.2.0
uevent_helper is /sbin/mdev
uevent_helper is /sbin/mdev
看到没,刚开始确实是/sbin/hotplug,但后来就变成了/sbin/mdev。很据上面信息,我们知道是在文件系统启动的过程中
发生改变的。文件系统启动过程中,改变mdev的只有“echo /sbin/mdev > /proc/sys/kernel/hotplug”,也确实是这个
导致了uevent_helper的改变。
涉及到的数据在/kernel/sysctl.c下
#if defined(CONFIG_HOTPLUG) && defined(CONFIG_NET)
{
.ctl_name= KERN_HOTPLUG,
.procname= "hotplug",
.data= &uevent_helper,
.maxlen= UEVENT_HELPER_PATH_LEN,
.mode= 0644,
.proc_handler= &proc_dostring,
.strategy= &sysctl_string,
},
#endif
至于为什么“echo /sbin/mdev > /proc/sys/kernel/hotplug”能改变uevent_helper就是proc虚拟文件系统的内容了,
这里不讨论。
其实设置mdev有三种方法,总结如下:
1、编译内核的时候直接配置CONFIG_UEVENT_HELPER_PATH,并且在之后的启动中不去修改uevent_helper,那么
uevent_helper代表的程序就是CONFIG_UEVENT_HELPER_PATH指定的程序
2、不管CONFIG_UEVENT_HELPER_PATH配置与否或如何设置,通过echo /sbin/mdev > /sys/kernel/uevent_helper
修改uevent_helper的内容,这个指令将会调用内核函数uevent_helper_store。过程涉及sysfs虚拟文件系统的
内容,这里不讨论。改变之后,/proc/sys/kernel/hotplug里的内容也会立即发生改变
3、不管CONFIG_UEVENT_HELPER_PATH配置与否或如何设置,通过echo /sbin/mdev > /proc/sys/kernel/hotplug
修改uevent_helper的内容.它的修改也会导致/sys/kernel/uevent_helper里的内容立即改变
对于上述的2、3两种方法,都是通过用户层的接口直接uevent_helper,所以谁在后面谁起作用
三、busybox源码分析
内核源码的最后是调用uevent_helper指定的用户程序,这个用户程序通常是mdev,那么mdev如何做的呢,来看一下
busybox的源码。源码基于busybox-1.23.1
int mdev_main(int argc UNUSED_PARAM, char **argv)
xchdir("/dev"); // 先把目录改变到/dev下
if (argv[1] && strcmp(argv[1], "-s") == 0) { // 在文件系统启动的时候会调用 mdev -s,创建所有驱动设备节点
putenv((char*)"ACTION=add"); // mdev -s 的动作是创建设备节点,所以为add
if (access("/sys/class/block", F_OK) != 0) { // 当/sys/class/block目录不存在时,才扫描/sys/block
/* Scan obsolete /sys/block only if /sys/class/block
* doesn't exist. Otherwise we'll have dupes.
* Also, do not complain if it doesn't exist.
* Some people configure kernel to have no blockdevs.
*/
recursive_action("/sys/block",
ACTION_RECURSE | ACTION_FOLLOWLINKS | ACTION_QUIET,
fileAction, dirAction, temp, 0);
}
/*
* 这个函数是递归函数,它会扫描/sys/class目录下的所有文件,如果发现dev文件,将按照
* /etc/mdev.conf文件进行相应的配置。如果没有配置文件,那么直接创建设备节点
* 最终调用的创建函数是 make_device
*/
recursive_action("/sys/class",
ACTION_RECURSE | ACTION_FOLLOWLINKS,
fileAction, dirAction, temp, 0);
}
else{
// 获得环境变量,环境变量是内核在调用mdev之前设置的
env_devname = getenv("DEVNAME"); /* can be NULL */
G.subsystem = getenv("SUBSYSTEM");
action = getenv("ACTION");
env_devpath = getenv("DEVPATH");
snprintf(temp, PATH_MAX, "/sys%s", env_devpath);
make_device(env_devname, temp, op);
}
由以上代码分析可知,无论对于何种操作,最后都是调用make_device来创建节点,看一下这个函数
static void make_device(char *device_name, char *path, int operation)
int major, minor, type, len;
char *path_end = path + strlen(path); //path_end指定path结尾处
major = -1;
if (operation == OP_add) {
strcpy(path_end, "/dev"); // 往path结尾处拷贝“/dev”,这时path=/sys/class/test/test_dev/dev
len = open_read_close(path, path_end + 1, SCRATCH_SIZE - 1); // 打开并读取/sys/class/test/test_dev/dev
*path_end = '\0';
if (len < 1) {
if (!ENABLE_FEATURE_MDEV_EXEC)
return;
} else if (sscanf(path_end + 1, "%u:%u", &major, &minor) == 2) { //从/sys/class/test/test_dev/dev获得主次设备号
dbg1("dev %u,%u", major, minor);
} else {
major = -1;
}
}
if (operation == OP_add && major >= 0) // 如果是add,即创建节点
mknod(node_name, rule->mode | type, makedev(major, minor)) // 最终用mknod函数在/dev下创建设备节点
if (operation == OP_remove && major >= -1) // 如果是remove,即删除节点
unlink(node_name);
创建节点最后无非还是调用mknod,当然在class_create和device_create自动创建设备节点时,也会在/sys/class下自动创建
和删除相关设备类和设备,这是sysfs的驱动内容,这里不讲
linux mdev -s没有运行,mdev详解相关推荐
- Linux jobs等前后台运行命令详解
A,Shell支持作用控制,有以下命令: 1. command& 让进程在后台运行 2. jobs 查看后台运行的进程 3. fg %n 让后台运行的进程n到前台来 4. bg %n ...
- linux jobs继续运行,Linux jobs等前后台运行命令详解
A,Shell支持作用控制,有以下命令: 1. command& 让进程在后台运行 2. jobs 查看后台运行的进程 3. fg %n 让后台运行的进程n到前台来 4. bg %n 让进程n ...
- init.d,rc.d详解 Linux运行时详解
Linux 启动时需要哪些步骤呢?本文将详细描述不同的运行级在启动中的作用. 对于那些在DOS/Win9x/NT 平台下的高级用户而言,Linux 似乎是一个怪物.没有config.sys ,没有 a ...
- linux命令优先级设置,linux renice命令参数及用法详解(linux修改程序运行优先级命令)...
linux renice命令参数及用法详解(linux修改程序运行优先级命令) 发布时间:2012-07-21 12:45:32 作者:佚名 我要评论 renice指令可重新调整程序执行的优先 ...
- Linux中/proc目录下文件详解
Linux中/proc目录下文件详解(一) 声明:可以自由转载本文,但请务必保留本文的完整性. 作者:张子坚 email:zhangzijian@163.com 说明:本文所涉及示例均在fedora ...
- linux tf命令,Linux系统命令介绍之vmstat命令详解
今天小编要跟大家介绍的vmstat命令详解.熟悉Linux系统和使用Linux系统工作的小伙伴都知道Linux的命令有很多,而真正在工作中用到的命令应该不超过几十个,为了让大家更好的掌握这些命令,小编 ...
- Linux中/proc目录下文件详解(二)
Linux中/proc目录下文件详解(二) /proc/mdstat文件 这个文件包含了由md设备驱动程序控制的RAID设备信息. 示例: [root@localhost ~]# cat /proc/ ...
- linux 进程间通信 dbus-glib【实例】详解四(上) C库 dbus-glib 使用(附代码)(编写接口描述文件.xml,dbus-binding-tool工具生成绑定文件)(列集散集函数)
linux 进程间通信 dbus-glib[实例]详解一(附代码)(d-feet工具使用) linux 进程间通信 dbus-glib[实例]详解二(上) 消息和消息总线(附代码) linux 进程间 ...
- linux 进程间通信 dbus-glib【实例】详解三 数据类型和dteeth(类型签名type域)(层级结构:服务Service --> Node(对象、object) 等 )(附代码)
linux 进程间通信 dbus-glib[实例]详解一(附代码)(d-feet工具使用) linux 进程间通信 dbus-glib[实例]详解二(上) 消息和消息总线(附代码) linux 进程间 ...
- linux 进程间通信 dbus-glib【实例】详解二(下) 消息和消息总线(ListActivatableNames和服务器的自动启动)(附代码)
linux 进程间通信 dbus-glib[实例]详解一(附代码)(d-feet工具使用) linux 进程间通信 dbus-glib[实例]详解二(上) 消息和消息总线(附代码) linux 进程间 ...
最新文章
- C# chart控件基础使用
- Updater Application Block for .NET
- JS构造map/dict结构
- 官宣!“一流大学”,异地落户!
- Git学习笔记总结和注意事项
- 【Git】error: RPC
- arm 服务器优势,零的突破 戴尔正式宣布基于ARM架构服务器
- php vue插件 hplus-ui初学
- linux 使用ACR122U-A9设备读写M1卡
- 计算机cf编程,警察牧马人宏自定义编程计算机游戏鼠标有线大声笑/ cf英雄联盟光速质量保证....
- 设置win10自动登录/免密码自动登录方法
- 计算机网络实验设计-Tracert 与Ping 程序设计与实现
- 门锁MCU OTA升级指导文档
- HBase rowKey 设计技巧
- 移动政务中的小程序技术
- 电路交换技术与包交换技术
- 以大数据架构电商2.0的新时代
- 产品经理:个人能力提升方法
- 小技巧(8)pimple模式
- web版收银系统,支持支付宝,微信扫描枪支付,数字输入
热门文章
- 查看topic信息_如何规划的你博客文章主题(Topic)
- scala seq java_Scala中的两个Seq比较
- Flowable 数据库表结构 ACT_HI_ATTACHMENT
- 如何把暂存区恢复成和HEAD的一样?
- WebStorm-2019.2.3 下载安装
- Linux6、7 系列 安装、卸载mysql
- Vue全家桶 之 Vue基础
- Java基础--反射
- qtcreator下拉列表怎么制作_设置EXCEL动态下拉菜单,只需要一个组合键,新手也能快速掌握...
- python3新式类_python新式类和旧式类区别