如果udevd不能运行,那么就不会自动加载磁盘驱动程序,也就不能自动创建/dev/linux dev sda1 满了

3880人阅读
Linux相关(106)
摘要: 现在大多数硬件设备的驱动都是作为模块出现的,Linux启动过程中会自动加载这些模块,本文通过内核源码简要说明这个过程。
1 驱动模块本身包含设备商、设备ID号等详细信息
如果想让内核启动过程中自动加载某个模块该怎么做呢?最容易想到的方法就是到/etc/init.d/中添加一个启动脚本,然后在/etc/rcN.d/目录下创建一个符号链接,这个链接的名字以S开头,这内核启动时,就会自动运行这个脚本了,这样就可以在脚本中使用modprobe来实现自动加载。但是我们发现,内核中加载了许多硬件设备的驱动,而搜索/etc目录,却没有发现任何脚本负责加载这些硬件设备驱动程序的模块。那么这些模块又是如何被加载的呢?每一个设备都有Verdon ID, Device ID, SubVendor
ID等信息。而每一个设备驱动程序,必须说明自己能够为哪些Verdon ID, DevieceID, SubVendor ID的设备提供服务。以PCI设备为例,它是通过一个pci_device_id的数据结构来实现这个功能的。例如:RTL8139的pci_device_id定义为:
static struct pci_device_id rtl8139_pci_tbl[] = {
{0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl);
上面的信息说明,凡是Verdon ID为0x10EC, Device ID为0x8的PCI设备(SubVendor ID和SubDeviceID为PCI_ANY_ID,表示不限制。),都可以使用这个驱动程序(8139too)。
2 模块安装过程提取设备商、设备ID信息,并写入modules.alias文件
在模块安装的时候,depmod会根据模块中的rtl8139_pci_tbl的信息,生成下面的信息,保存到/lib/modules/uname-r/modules.alias文件中,其内容如下:
alias pci:v000010ECdsv*sd*bc*sc*i* 8139too
alias pci:v000010ECdsv*sd*bc*sc*i* 8139too
后面的000010EC说明其Vendor ID为10EC,d后面的说明Device ID为8139,而sv,和sd为SubVendor ID和SubDevice ID,后面的星号表示任意匹配。另外在/lib/modules/uname-r/modules.dep文件中还保存这模块之间的依赖关系,其内容如下:
(这里省去了路径信息。)
8139too.ko:mii.ko
3 内核启动过程中,总线枚举时把读取的设备ID等信息发送到udevd,udevd根据modules.alias文件找到匹配的驱动模块,加载之。
在内核启动过程中,总线驱动程序会会总线协议进行总线枚举,并且为每一个设备建立一个设备对象。每一个总线对象有一个kset对象,每一个设备对象嵌入了一个kobject对象,kobject连接在kset对象上,这样总线和总线之间,总线和设备设备之间就组织成一颗树状结构。当总线驱动程序为扫描到的设备建立设备对象时,会初始化kobject对象,并把它连接到设备树中,同时会调用kobject_uevent()把这个(添加新设备的)事件,以及相关信息(包括设备的VendorID,DeviceID等信息。)通过netlink发送到用户态中。在用户态的udevd检测到这个事件,就可以根据这些信息,打开/lib/modules/uname-r/modules.alias文件,根据alias
pci:v000010ECdsv*sd*bc*sc*i* 8139too得知这个新扫描到的设备驱动模块为8139too。于是modprobe就知道要加载8139too这个模块了,同时modprobe根据 modules.dep文件发现,8139too依赖于mii.ko,如果mii.ko没有加载,modprobe就先加载mii.ko,接着再加载 8139too.ko。
在你的shell中,运行:
# ps aux | grep udevd
root 25063 ...... /sbin/udevd --daemon
我们得到udevd的进程ID为25063,现在结束这个进程:
# kill -9 25063
然后跟踪udevd,在shell中运行:
# strace -f /sbin/udevd --daemon
这时,我们看到udevd的输出如下:
close(8) = 0
munmap(0xb7f8c000, 4096) = 0
select(7, [3 4 5 6], NULL, NULL, NULL
我们发现udevd在这里被阻塞在select()函数中。
select函数原型如下:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
第一个参数:nfds表示最大的文件描述符号,这里为7(明明是6 ?)。
第二个参数:readfds为读文件描述符集合,这里为3,4,5,6.
第三个参数:writefds为写文件描述符集合,这里为NULL。
第四个参数:exceptfds为异常文件描述符集合,这里为NULL。
第五个参数:timeout指定超时时间,这里为NULL。
select函数的作用是:如果readfds中的任何一个文件有数据可读,或者witefds中的任何一个文件可以写入,或者exceptfds中的任何一个文件出现异常时,就返回。否则阻塞当前进程,直到上诉条件满足,或者因阻塞时间超过了timeout指定的时间,当前进程被唤醒,select返回。
所以,在这里udevd等待3,4,5,6这几个文件有数据可读,才会被唤醒。现在,到shell中运行:
# ps aux | grep udevd
root 27615 ...... strace -o /tmp/udevd.debug -f /sbin/udevd --daemon
root 27617 ...... /sbin/udevd --daemon
udevd的进程id为27617,现在我们来看看select等待的几个文件:
# cd /proc/27615/fd
udevd的标准输入,标准输出,标准错误全部为/dev/null.
0 -& /dev/null
1 -& /dev/null
2 -& /dev/null
udevd在下面这几个文件上等待。
3 -& /inotify
4 -& socket:[331468]
5 -& socket:[331469]
6 -& pipe:[331470]
7 -& pipe:[331470]
由于不方便在运行中插入一块8139的网卡,因此现在我们以一个U盘来做试验,当你插入一个U盘后,你将会看到strace的输出,从它的输出可以看到 udevd在select返回后,调用了modprobe加载驱动模块,并调用了sys_mknod,在dev目录下建立了相应的节点。
execve(&/sbin/modprobe&, [&/sbin/modprobe&, &-Q&, &usb:v05ACpdc00dsc00dp00&...]
mknod(&/dev/sdb&, S_IFBLK|0660, makedev(8, 16)) = 0
这里modprobe的参数&usb:v05AC...&对应modules.alias中的某个模块。
可以通过udevmonitor来查看内核通过netlink发送给udevd的消息,在shell中运行:
# udevmonitor --env
然后再插入U盘,就会看到相关的发送给udevd的消息。
== 内核处理过程 ==:
这里我们以PCI总线为例,来看看在这个过程中,内核是如何处理的。当PCI总线驱动程序扫描到一个新的设备时,会建立一个设备对象,然后调用 pci_bus_add_device()函数,这个函数最终会调用kobject_uevent()通过netlink向用户态的udevd发送消息。
int pci_bus_add_device(struct pci_dev *dev)
retval = device_add(&dev-&dev);
device_add()代码如下:
int device_add(struct device *dev)
struct device *parent = NULL;
dev = get_device(dev);
error = bus_add_device(dev);
if (error)
kobject_uevent(&dev-&kobj, KOBJ_ADD);
device_add()在准备好相关数据结构后,会调用kobject_uevent(),把这个消息发送到用户空间的udevd。
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
return kobject_uevent_env(kobj, action, NULL);
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, char *envp_ext[])
struct kobj_uevent_env *
const char *action_string = kobject_actions[action];
const char *devpath = NULL;
const char *
struct kobject *top_
struct kset *
struct kset_uevent_ops *uevent_
int i = 0;
int retval = 0;
/* default keys */
retval = add_uevent_var(env, &ACTION=%s&, action_string);
if (retval)
retval = add_uevent_var(env, &DEVPATH=%s&, devpath);
if (retval)
retval = add_uevent_var(env, &SUBSYSTEM=%s&, subsystem);
if (retval)
/* keys passed in from the caller */
if (envp_ext) {
for (i = 0; envp_ext[i]; i++) {
retval = add_uevent_var(env, envp_ext[i]);
if (retval)
/* 通过netlink发送消息,这样用户态的udevd进程就会从select()函数返回,并做相应的处理。 */
#if defined(CONFIG_NET)
/* send netlink message */
if (uevent_sock) {
struct sk_buff *
/* allocate message with the maximum possible size */
len = strlen(action_string) + strlen(devpath) + 2;
skb = alloc_skb(len + env-&buflen, GFP_KERNEL);
if (skb) {
/* add header */
scratch = skb_put(skb, len);
sprintf(scratch, &%s@%s&, action_string, devpath);
/* copy keys to our continuous event payload buffer */
for (i = 0; i & env-&envp_ i++) {
len = strlen(env-&envp[i]) + 1;
scratch = skb_put(skb, len);
strcpy(scratch, env-&envp[i]);
NETLINK_CB(skb).dst_group = 1;
netlink_broadcast(uevent_sock, skb, 0, 1, GFP_KERNEL);
现在我们知道/dev目录下的设备文件是由 udevd负责建立的,但是在内核启动过程中,需要mount一个根目录,通常我们的根目录是在硬盘上,比如:/dev/sda1,但是硬盘对应的驱动程序没有加载前,/dev/sda1是不存在的, 如果没有/dev/sda1,就不能通过mount /dev/sda1 /来挂载根目录。另一方面udevd是一个可执行文件,如果连硬盘驱动程序到没有加载,根目录都不存在,udevd就不能运行。如果udevd不能运行,那么就不会自动加载磁盘驱动程序,也就不能自动创建/dev/sda1。这不是死锁了吗?那么你的Linux是怎么启动的呢?
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1475653次
积分:16862
积分:16862
排名:第379名
原创:314篇
转载:106篇
译文:14篇
评论:333条
阅读:8268
(5)(1)(5)(3)(10)(4)(7)(3)(5)(1)(4)(1)(5)(3)(6)(10)(10)(8)(14)(1)(3)(7)(1)(3)(4)(7)(11)(4)(5)(4)(5)(1)(8)(6)(6)(3)(9)(6)(4)(6)(2)(4)(4)(7)(6)(3)(2)(1)(3)(6)(6)(12)(4)(5)(3)(5)(6)(8)(6)(4)(1)(3)(1)(2)(6)(1)(12)(2)(4)(3)(8)(2)(15)(6)(2)(5)(5)(4)(2)(1)(4)(4)(6)(2)(3)(1)(5)(1)(2)(4)(4)(5)(3)bbs.chinaunix.net - System Error
Discuz! System Error
已经将此出错信息详细记录, 由此给您带来的访问不便我们深感歉意.udev,mdev,udisk加载(1)
  Linux传统上使用静态设备创建的方法,在dev下创建了大量的节点,而不管这些节点相应的硬件设备是否存在。采用udev的方法,系统检测到设备才会往创建这些设备对应的节点。
&udev 的规则配置文件实例
默认的规则配置文件存放在 /etc/udev/rules.d/ 中,我们进入这个可以看到 RedHat 默认对设备建好的一些规则和一些硬件公司写好的规则。
进入目录,可以见到以二位数字开头的前缀的配置文件,可以使用 vi 进入配置文件中查看,一行是一条规则,默认是从小数字到大数字,这些表示生效的顺序。
我们在使用 udev 写规则前,先来看一个例子
&KERNEL==sd*, PROGRAM=/lib/udev/scsi_id -g -s %p, RESULT==123456, SYMLINK=%k_%c
该规则的执行:如果有一个内核设备名称以 sd 开头,且 SCSI ID 为 123456,则为设备文件产生一个符号链接“sda_123456”.& %p %k %c 请看后面的“udev 的值和可调用的替换操作符 ”
udev 的规则配置文件
在规则文件里,除了以“#”开头的行(注释),所有的非空行都被视为一条规则,但是一条规则不能扩展到多行。规则都是由多个 键值对(key-value pairs)组成,并由逗号隔开,键值对可以分为 条件匹配键值对( 以下简称“匹配键 ”) 和 赋值键值对( 以下简称“赋值键 ”),一条规则可以有多条匹配键和多条赋值键。匹配键是匹配一个设备属性的所有条件,当一个设备的属性匹配了该规则里所有的匹配键,就认为这条规则生效,然后按照赋值键的内容,执行该规则的赋值。
规则文件里的规则有一系列的键/值对组成,键/值对之间用逗号(,)分割。
通过上面例子中也能看出,这些配置,但我想大家可能会产生疑惑,为什么 KERNEL 是匹配键,而 NAME 和 MODE 是赋值键呢?这由中间的操作符 (operator) 决定。
仅当操作符是“==”或者“!=”时,其为匹配键;若为其他操作符时,都是赋值键。
匹配键和赋值键操作符解释见下表:
&操作符&&&&
匹配或赋值t&&&&&&&&&&&&&&&&&&&&&&&& 解释
----------------------------------------
==&&&&&&&&&&& 匹配&&&&&&&&&&&&& 相等比较
!=&&&&&&&&&&& 匹配&&&&&&&&&&&& 不等比较
=&&&&&&&&&&& 赋值&&&&&&&&&&&&& 分配一个特定的值给该键,他可以覆盖之前的赋值。
+=&&&&&&&&& 赋值&&&&&&&&&&&&& 追加特定的值给已经存在的键
:=&&&&&&&&&&& 赋值&&&&&&&&&&&&&&&&& 分配一个特定的值给该键,后面的规则不可能覆盖它。
udev 规则的匹配键 :
----------------------------------------
ACTION&&&&&&&& 事件 (uevent)
的行为,例如:add(
添加设备 )、remove(
删除设备 )。
KERNEL&&&&&&&& 在内核里看到的设备名字,比如sd*表示任意SCSI磁盘设备
DEVPATH&&&&&& 内核设备录进,比如/devices/*
SUBSYSTEM&&&&&& 子系统名字,例如:sda
的子系统为 block。
BUS&&&&&&&& 总线的名字,比如IDE,USB
DRIVER&&&&&&&& 设备驱动的名字,比如ide-rom
ID&&&&&&&&&& 独立于内核名字的设备名字
SYSFS{ value}&&&&&& sysfs属性值,他可以表示任意
ENV{ key}&&&&&& 环境变量,可以表示任意
PROGRAM&&&&&& 可执行的外部程序,如果程序返回0值,该键则认为为真(true)
RESULT&&&&&&&& 上一个PROGRAM调用返回的标准输出。
NAME&&&&&&&& 根据这个规则创建的设备文件的文件名。
注意:仅仅第一行的NAME描述是有效的,后面的均忽略。&&
&&& 如果你想使用使用两个以上的名字来访问一个设备的话,可以考虑SYMLINK键。
SYMLINK&&&&&& 为 /dev/下的设备文件产生符号链接。由于 udev
只能为某个设备产生一个设备文件,
&&& 所以为了不覆盖系统默认的 udev
规则所产生的文件,推荐使用符号链接。
OWNER&& &&&&&&设备文件的属组
GROUP&&&&&&&& 设备文件所在的组。
MODE&&&&&&&& 设备文件的权限,采用8进制
RUN&&&&&&&& 为设备而执行的程序列表
LABEL&&&&&&&& 在配置文件里为内部控制而采用的名字标签(下下面的GOTO服务)
GOTO&&&&&&&& 跳到匹配的规则(通过LABEL来标识),有点类似程序语言中的GOTO
IMPORT{ type}&&&& 导入一个文件或者一个程序执行后而生成的规则集到当前文件
WAIT_FOR_SYSFS&& 等待一个特定的设备文件的创建。主要是用作时序和依赖问题。
PTIONS&&&&&&&& 特定的选项:
last_rule 对这类设备终端规则执行;
ignore_device 忽略当前规则;
ignore_remove 忽略接下来的并移走请求。
all_partitions 为所有的磁盘分区创建设备文件。
&我们给出一个列子来解释如何使用这些键。下面的例子来自系统的标准配置文件。
上面的例子给出了个规则,每一个都是或者键开头:
第一个规则是缺省的,他匹配任意被内核识别到的设备,然后设定这些设备的属组是,组是,访问权限模式是。这也是一个安全的缺省设置保证所有的设备在默认情况下只有可以读写。
第二个规则也是比较典型的规则了。它匹配终端设备,然后设置新的权限为,所在的组是。它也设置了一个特别的设备文件名。在这里例子里,代表设备的内核名字。那也就意味着内核识别出这些设备是什么名字,就创建什么样的设备文件名。
第三行开始的表示驱动它创建一对设备符号连接:和。
第四行,开始的表示驱动器。这个规则创建和上面的规则相同的符号连接。驱动器需要值以来区别别的设备,因为可以被内核唯一识别。
第五行以开始,它告诉增加到命令列表,当任意设备增加到系统后,这些命令将执行。其效果就是计算机应该会增加内核模块来侦测新的设备。
当然,上面仅仅是一小部分例子,如果你的系统采用了方式,那你应该可以看到更多的规则。如果你想修改设备的权限或者创建信的符号连接,那么你需要熟读这些规则,特别是要仔细注意你修改的那些与之相关的设备。
udev 的值和可调用的替换操作符
在键值对中的键和操作符都介绍完了,最后是值 (value)。Linux
用户可以随意地定制 udev
规则文件的值。
例如:my_root_disk, my_printer。同时也可以引用下面的替换操作符:
----------------------------------------
$kernel, %k:设备的内核设备名称,例如:sda、cdrom。
$number, %n:设备的内核号码,例如:sda3
的内核号码是 3。
$devpath, %p:设备的 devpath路径。
$id, %b:设备在 devpath里的 ID
$sysfs{}, %s{file}:设备的
sysfs里 file
的内容。其实就是设备的属性值。
例如:$sysfs{size}
表示该设备 ( 磁盘 )
$env{key}, %E{key}:一个环境变量的值。
$major, %M:设备的 major
$minor %m:设备的 minor
$result, %c:PROGRAM
返回的结果
$parent, %P:父设备的设备文件名。
$root, %r:udev_root的值,默认是 /dev/。
$tempnode, %N:临时设备名。
%%:符号 %
$$:符号 $
udev 规则所需要信息的查询
常用的查上面匹配键信息的命令
udevinfo -a -p $(udevinfo& -q path -n /dev/sda1 )
上面的命令两次使用udevinfo:
& 第一次是返回sysfs设备路径(他通常和我们看到的Linux设备文件名所在路径--/dev/hda--不同);
& 第二次才是查询这个设备路径,结果将是非常常的syfs信息汇总
udevinfo -a -p /sys/class/net/eth0
scsi_id -g -s /block/sda
scsi_id -g -x -s /block/sda/sda3
ata_id /dev/hda
udev 信息的测试和生效
查出来后,根据上面文件中的内容写规则后,怎么测试
udevtest /block/sda
start_dev #命令重启 udev守护进程
本操作会对所有的设备重新查询规则目录下所有的规则文件,然后执行所匹配的规则里的行为。
通常使用该命令让新的规则文件立即生效。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:41803次
排名:千里之外
原创:21篇
转载:193篇
(11)(12)(1)(2)(2)(14)(1)(9)(13)(9)(10)(3)(5)(7)(29)(3)(4)(12)(1)(5)(1)(14)(20)(29)博客访问: 671048
博文数量: 1015
博客积分: 0
博客等级: 民兵
技术积分: 7665
注册时间:
storage R&D guy.
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: 服务器与存储
摘要: 现在大多数硬件设备的驱动都是作为模块出现的,Linux启动过程中会自动加载这些模块,本文通过内核源码简要说明这个过程。
1 驱动模块本身包含设备商、设备ID号等详细信息
如果想让内核启动过程中自动加载某个模块该怎么做呢?最容易想到的方法就是到/etc/init.d/中添加一个启动脚本,然后在/etc
/rcN.d/目录下创建一个符号链接,这个链接的名字以S开头,这内核启动时,就会自动运行这个脚本了,这样就可以在脚本中使用modprobe来实现
自动加载。但是我们发现,内核中加载了许多硬件设备的驱动,而搜索/etc目录,却没有发现任何脚本负责加载这些硬件设备驱动程序的模块。那么这些模块又
是如何被加载的呢?每一个设备都有Verdon ID, Device ID, SubVendor
ID等信息。而每一个设备驱动程序,必须说明自己能够为哪些Verdon ID, DevieceID, SubVendor
ID的设备提供服务。以PCI设备为例,它是通过一个pci_device_id的数据结构来实现这个功能的。例如:RTL8139的
pci_device_id定义为:
static struct pci_device_id rtl8139_pci_tbl[] = {
{0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl);
上面的信息说明,凡是Verdon ID为0x10EC, Device ID为0x8的PCI设备(SubVendor ID和SubDeviceID为PCI_ANY_ID,表示不限制。),都可以使用这个驱动程序(8139too)。
2 模块安装过程提取设备商、设备ID信息,并写入modules.alias文件
在模块安装的时候,depmod会根据模块中的rtl8139_pci_tbl的信息,生成下面的信息,保存到/lib/modules/uname-r/modules.alias文件中,其内容如下:
alias pci:v000010ECdsv*sd*bc*sc*i* 8139too
alias pci:v000010ECdsv*sd*bc*sc*i* 8139too
后面的000010EC说明其Vendor ID为10EC,d后面的说明Device
ID为8139,而sv,和sd为SubVendor ID和SubDevice
ID,后面的星号表示任意匹配。另外在/lib/modules/uname-r/modules.dep文件中还保存这模块之间的依赖关系,其内容如
(这里省去了路径信息。)
8139too.ko:mii.ko
3 内核启动过程中,总线枚举时把读取的设备ID等信息发送到udevd,udevd根据modules.alias文件找到匹配的驱动模块,加载之。
在内核启动过程中,总线驱动程序会会总线协议进行总线枚举,并且为每一个设备建立一个设备对象。每一个总线对象有一个kset对象,每一个设备对象
嵌入了一个kobject对象,kobject连接在kset对象上,这样总线和总线之间,总线和设备设备之间就组织成一颗树状结构。当总线驱动程序为扫
描到的设备建立设备对象时,会初始化kobject对象,并把它连接到设备树中,同时会调用kobject_uevent()把这个(添加新设备的)事
件,以及相关信息(包括设备的VendorID,DeviceID等信息。)通过netlink发送到用户态中。在用户态的udevd检测到这个事件,就
可以根据这些信息,打开/lib/modules/uname-r/modules.alias文件,根据alias
pci:v000010ECdsv*sd*bc*sc*i*
8139too得知这个新扫描到的设备驱动模块为8139too。于是modprobe就知道要加载8139too这个模块了,同时modprobe根据
modules.dep文件发现,8139too依赖于mii.ko,如果mii.ko没有加载,modprobe就先加载mii.ko,接着再加载
8139too.ko。
在你的shell中,运行:
# ps aux | grep udevd
root 25063 ...... /sbin/udevd --daemon
我们得到udevd的进程ID为25063,现在结束这个进程:
# kill -9 25063
然后跟踪udevd,在shell中运行:
# strace -f /sbin/udevd --daemon
这时,我们看到udevd的输出如下:
close(8) = 0
munmap(0xb7f8c000, 4096) = 0
select(7, [3 4 5 6], NULL, NULL, NULL
我们发现udevd在这里被阻塞在select()函数中。
select函数原型如下:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
第一个参数:nfds表示最大的文件描述符号,这里为7(明明是6 ?)。
第二个参数:readfds为读文件描述符集合,这里为3,4,5,6.
第三个参数:writefds为写文件描述符集合,这里为NULL。
第四个参数:exceptfds为异常文件描述符集合,这里为NULL。
第五个参数:timeout指定超时时间,这里为NULL。
select函数的作用是:如果readfds中的任何一个文件有数据可读,或者witefds中的任何一个文件可以写入,或者exceptfds中的任何一个文件出现异常时,就返回。否则阻塞当前进程,直到上诉条件满足,或者因阻塞时间超过了timeout指定的时间,当前进程被唤醒,select返回。
所以,在这里udevd等待3,4,5,6这几个文件有数据可读,才会被唤醒。现在,到shell中运行:
# ps aux | grep udevd
root 27615 ...... strace -o /tmp/udevd.debug -f /sbin/udevd --daemon
root 27617 ...... /sbin/udevd --daemon
udevd的进程id为27617,现在我们来看看select等待的几个文件:
# cd /proc/27615/fd
udevd的标准输入,标准输出,标准错误全部为/dev/null.
0 -> /dev/null
1 -> /dev/null
2 -> /dev/null
udevd在下面这几个文件上等待。
3 -> /inotify
4 -> socket:[331468]
5 -> socket:[331469]
6 -> pipe:[331470]
7 -> pipe:[331470]
由于不方便在运行中插入一块8139的网卡,因此现在我们以一个U盘来做试验,当你插入一个U盘后,你将会看到strace的输出,从它的输出可以看到 udevd在select返回后,调用了modprobe加载驱动模块,并调用了sys_mknod,在dev目录下建立了相应的节点。
execve("/sbin/modprobe", ["/sbin/modprobe", "-Q", "usb:v05ACpdc00dsc00dp00"...]
mknod("/dev/sdb", S_IFBLK|0660, makedev(8, 16)) = 0
这里modprobe的参数"usb:v05AC..."对应modules.alias中的某个模块。
可以通过udevmonitor来查看内核通过netlink发送给udevd的消息,在shell中运行:
# udevmonitor --env
然后再插入U盘,就会看到相关的发送给udevd的消息。
== 内核处理过程 ==:
这里我们以PCI总线为例,来看看在这个过程中,内核是如何处理的。当PCI总线驱动程序扫描到一个新的设备时,会建立一个设备对象,然后调用 pci_bus_add_device()函数,这个函数最终会调用kobject_uevent()通过netlink向用户态的udevd发送消息。
int pci_bus_add_device(struct pci_dev *dev)
retval = device_add(&dev->dev);
device_add()代码如下:
int device_add(struct device *dev)
struct device *parent = NULL;
dev = get_device(dev);
error = bus_add_device(dev);
if (error)
kobject_uevent(&dev->kobj, KOBJ_ADD);
device_add()在准备好相关数据结构后,会调用kobject_uevent(),把这个消息发送到用户空间的udevd。
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
return kobject_uevent_env(kobj, action, NULL);
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, char *envp_ext[])
struct kobj_uevent_env *
const char *action_string = kobject_actions[action];
const char *devpath = NULL;
const char *
struct kobject *top_
struct kset *
struct kset_uevent_ops *uevent_
int i = 0;
int retval = 0;
/* default keys */
retval = add_uevent_var(env, "ACTION=%s", action_string);
if (retval)
retval = add_uevent_var(env, "DEVPATH=%s", devpath);
if (retval)
retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
if (retval)
/* keys passed in from the caller */
if (envp_ext) {
for (i = 0; envp_ext[i]; i++) {
retval = add_uevent_var(env, envp_ext[i]);
if (retval)
/* 通过netlink发送消息,这样用户态的udevd进程就会从select()函数返回,并做相应的处理。 */
#if defined(CONFIG_NET)
/* send netlink message */
if (uevent_sock) {
struct sk_buff *
/* allocate message with the maximum possible size */
len = strlen(action_string) + strlen(devpath) + 2;
skb = alloc_skb(len + env->buflen, GFP_KERNEL);
if (skb) {
/* add header */
scratch = skb_put(skb, len);
sprintf(scratch, "%s@%s", action_string, devpath);
/* copy keys to our continuous event payload buffer */
for (i = 0; i envp_ i++) {
len = strlen(env->envp[i]) + 1;
scratch = skb_put(skb, len);
strcpy(scratch, env->envp[i]);
NETLINK_CB(skb).dst_group = 1;
netlink_broadcast(uevent_sock, skb, 0, 1, GFP_KERNEL);
现在我们知道/dev目录下的设备文件是由
udevd负责建立的,但是在内核启动过程中,需要mount一个根目录,通常我们的根目录是在硬盘上,比如:/dev/sda1,但是硬盘对应的驱动程
序没有加载前,/dev/sda1是不存在的, 如果没有/dev/sda1,就不能通过mount /dev/sda1
/来挂载根目录。另一方面udevd是一个可执行文件,如果连硬盘驱动程序到没有加载,根目录都不存在,udevd就不能运行。如果udevd不能运行,
那么就不会自动加载磁盘驱动程序,也就不能自动创建/dev/sda1。这不是死锁了吗?那么你的Linux是怎么启动的呢?
阅读(247) | 评论(0) | 转发(0) |
相关热门文章
给主人留下些什么吧!~~
请登录后评论。}

我要回帖

更多关于 dev sda1 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信