原文地址 http://blog.163.com/againinput4@yeah/blog/static/122764271200962305339483/

最近有在研究SD卡设备节点自动创建及挂载,发现TI的达芬奇平台使用的是udev而非mdev,所以花了点时间看了看udev,查到了《udev轻松上路》这篇文章,看了下《Writing udev rules》,那篇文章写的不错,建议有需要的朋友一定要好好看看,另外,在网上有篇关于SD卡和U盘自动创建节点自动挂载的文章,分别通过udev和mdev实现,也可以作为参考,真正理解还需要看看老外的文章,理解下里面的udev书写规则,真正做到标本兼治!

通过上述文章,加上自己的理解,有了这篇文章!

第一、什么是udev?

这篇文章UDEV Primer给我们娓娓道来,花点时间预习一下是值得的。当然,不知道udev是什么也没关系,
把它当个助记符好了,有了下面的上路指南,可以节省很多时间。我们只需要树立一个信念:udev很简单!
嵌入式的udev应用尤其简单。

第二、为什么udev要取代devfs?

这是生产关系适应生产力的需要,udev好,devfs坏,用好的不用坏的。

udev是硬件平台无关的,属于user space的进程,它脱离驱动层的关联而建立在操作系统之上,基于这种设
计实现,我们可以随时修改及删除/dev下的设备文件名称和指向,随心所欲地按照我们的愿望安排和管理设
备文件系统,而完成如此灵活的功能只需要简单地修改udev的配置文件即可,无需重新启动操作系统。udev
已经使得我们对设备的管理如探囊取物般轻松自如。

第三、如何得到udev?

udev的主页在这里:http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html
我们按照下面的步骤来生成udev的工具程序,以arm-linux为例:
1、wget http://www.us.kernel.org/pub/linux/utils/kernel/hotplug/udev-100.tar.bz2
2、tar xjf udev-100.tar.bz2
3、cd udev-100 编辑Makefile,查找CROSS_COMPILE,修改CROSS_COMPILE ?= arm-linux-
4、make

没有什么意外的话当前目录下生成udev,udevcontrol,udevd,udevinfo,udevmonitor,udevsettle,udevstart,
udevtest,udevtrigger九个工具程序,在嵌入式系统里,我们只需要udevd和udevstart就能使udev工作得很好,
其他工具则帮助我们完成udev的信息察看、事件捕捉或者更高级的操作。

另外一个方法是直接使用debian提供的已编译好的二进制包,美中不足的是版本老了一些。
1、wget http://ftp.us.debian.org/debian/pool/main/u/udev/udev_0.056-3_arm.deb
2、ar -xf udev_0.056-3_arm.deb
3、tar xzf data.tar.gz

在sbin目录里就有我们需要的udevd和udevstart工具程序。

建议大家采用第一种方式生成的udevd和udevstart。为什么要用最新udev呢?新的强,旧的弱,用强的不用弱的。

第四、如何配置udev?

首先,udev需要内核sysfs和tmpfs的支持,sysfs为udev提供设备入口和uevent通道,tmpfs为udev设备文件提
供存放空间,也就是说,在上电之前系统上是没有足够的设备文件可用的,我们需要一些技巧让kernel先引导
起来。

由于在kernel启动未完成以前我们的设备文件不可用,如果使用mtd设备作为rootfs的挂载点,这个时候/dev/mtdblock
是不存在的,我们无法让kernel找到rootfs,kernel只好停在那里惊慌。
这个问题我们可以通过给kernel传递设备号的方式来解决,在linux系统中,mtdblock的主设备号是31,part号
从0开始,那么以前的/dev/mtdblock/3就等同于31:03,以次类推,所以我们只需要修改bootloader传给kernel
的cmd line参数,使root=31:03,就可以让kernel在udevd未起来之前成功的找到rootfs。
O.K.下一个问题。

其次,需要做的工作就是重新生成rootfs,把udevd和udevstart复制到/sbin目录。然后我们需要在/etc/下为udev
建立设备规则,这可以说是udev最为复杂的一步。这篇文章提供了最完整的指导:Writing udev rules (http://reactivated.net/writing_udev_rules.html)
文中描述的复杂规则我们可以暂时不用去理会,上路指南将带领我们轻松穿过这片迷雾。这里提供一个由简入
繁的方法,对于嵌入式系统,这样做可以一劳永逸。

1、在前面用到的udev-100目录里,有一个etc目录,里面放着的udev目录包含了udev设备规则的详细样例文
本。为了简单而又简洁,我们只需要用到etc/udev/udev.conf这个文件,在我们的rootfs/etc下建立一个udev目
录,把它复制过去,这个文件很简单,除了注释只有一行,是用来配置日志信息的,嵌入式系统也许用不上
日志,但是udevd需要检查这个文件。

2、在rootfs/etc/udev下建立一个rules.d目录,生成一个空的配置文件touch etc/udev/rules.d/udev.conf。然后
我们来编辑这个文件并向它写入以下配置项:

###############################################
# vc devices
KERNEL=="tty[0-9]*", NAME="vc/%n"

# block devices
KERNEL=="loop[0-9]*", NAME="loop/%n"

# mtd devices
KERNEL=="mtd[0-9]*", NAME="mtd/%n"
KERNEL=="mtdblock*", NAME="mtdblock/%n"

# input devices
KERNEL=="mice" NAME="input/%k"
KERNEL=="mouse[0-9]*", NAME="input/%k"
KERNEL=="ts[0-9]*", NAME="input/%k"
KERNEL=="event[0-9]*", NAME="input/%k"

# misc devices
KERNEL=="apm_bios", NAME="misc/%k"
KERNEL=="rtc", NAME="misc/%k"
################################################

保存它,我们的设备文件系统基本上就可以了,udevd和udevstart会自动分析这个文件。

这里重点说下%n,先来看2个例子:

# cat /etc/udev/rules.d/honeywell.rules
#Honeywell
#SD card
#KERNEL=="mmcblk[0-9]p*", NAME="SD/%n", SYMLINK+="SdPart%n", RUN+="/sbin/SdCard", OPTIONS+="last_rule"
KERNEL=="mmcblk*", NAME="SD/%n",SYMLINK+="SdPart%n",RUN+="/sbin/SdCard"
#
插入SD卡:

# ls /dev/SD/ -l
brw-rw----    1 root     root     254,   0 Jan  1 00:41 0
brw-rw----    1 root     root     254,   1 Jan  1 00:41 1
# ls /dev/SdPart* -l
lrwxrwxrwx    1 root     root            4 Jan  1 00:41 /dev/SdPart0 -> SD/0
lrwxrwxrwx    1 root     root            4 Jan  1 00:41 /dev/SdPart1 -> SD/1
#
#
修改规则后:

# cat /etc/udev/rules.d/honeywell.rules
#Honeywell
#SD card
#KERNEL=="mmcblk[0-9]p*", NAME="SD/%n", SYMLINK+="SdPart%n", RUN+="/sbin/SdCard", OPTIONS+="last_rule"
KERNEL=="mmcblk0p*", NAME="SD/%n",SYMLINK+="SdPart%n",RUN+="/sbin/SdCard"
#
插入SD卡(需要先umount)

#
# ls -l /dev/SD/
brw-rw----    1 root     root     254,   1 Jan  1 00:49 1
#
# ls -l /dev/SdPart*
lrwxrwxrwx    1 root     root            4 Jan  1 00:49 /dev/SdPart1 -> SD/1
#

修改部分内容后如下:

# cat /etc/udev/rules.d/honeywell.rules
#Honeywell
#SD card
#KERNEL=="mmcblk[0-9]p*", NAME="SD/%n", SYMLINK+="SdPart%n", RUN+="/sbin/SdCard", OPTIONS+="last_rule"
KERNEL=="mmcblk?", NAME="SD/%n",SYMLINK+="SdPart%n",RUN+="/sbin/SdCard"
#

插入SD卡:
#
# ls -l /dev/SD/
brw-rw----    1 root     root     254,   0 Jan  1 00:54 0
# ls -l /dev/Sd*
lrwxrwxrwx    1 root     root            4 Jan  1 00:54 /dev/SdPart0 -> SD/0
#

结论:

%n是KERNEL中的数字号码,

    在 KERNEL=="mmcblk*"中,KERNEL分别匹配mmcblk0(SD卡)和mmcblk0p1(SD卡1分区),所以%n分别对应数字0 1,也就创建了/dev/SdPart0 /dev/SdPart1两个软链接,
    当改为KERNEL=="mmcblk0p*"时,KERNEL只能匹配到mmcblk0p1,所以只会创建/dev/SdPart1软连接。
    当改为KERNEL=="mmcblk?"时,KERNEL只匹配一次,优先匹配为到mmcblk0,所以只会创建/dev/SdPart0软连接

3、为了使udevd在kernel起来后能够自动运行,我们在rootfs/etc/init.d/rcS中增加以下几行:

##################################
/bin/mount -t tmpfs tmpfs /dev

echo "Starting udevd..."
/sbin/udevd --daemon
/sbin/udevstart
##################################

4、重新生成rootfs,烧写到flash指定的rootfs part中。

5、如果需要动态改变设备规则,可以把etc/udev放到jffs或yaffs part,以备修改,根据需求而定,可以随时扩
充udev.conf中的配置项。

6、我们知道,用户空间的程序与设备通信的方法,主要有以下几种方式,
  1. 通过ioperm获取操作IO端口的权限,然后用inb/inw/ inl/ outb/outw/outl等函数,避开设备驱动程序,直接去操作IO端口。(没有用过)
  2. 用ioctl函数去操作/dev目录下对应的设备,这是设备驱动程序提供的接口。像键盘、鼠标和触摸屏等输入设备一般都是这样做的。
  3. 用write/read/mmap去操作/dev目录下对应的设备,这也是设备驱动程序提供的接口。像framebuffer等都是这样做的。
  
  上面的方法在大多数情况下,都可以正常工作,但是对于热插拨(hotplug)的设备,比如像U盘,就有点困难了,因为你不知道:什么时候设备插上了,什么时候设备拔掉了。这就是所谓的hotplug问题了。
  
  处理hotplug传统的方法是,在内核中执行一个称为hotplug的程序,相关参数通过环境变量传递过来,再由hotplug通知其它关注 hotplug事件的应用程序。这样做不但效率低下,而且感觉也不那么优雅。新的方法是采用NETLINK实现的,这是一种特殊类型的socket,专门用于内核空间与用户空间的异步通信。下面的这个简单的例子,可以监听来自内核hotplug的事件。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/types.h>
#include <linux/netlink.h>
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h> #define UEVENT_BUFFER_SIZE 2048 static int init_hotplug_sock()
{
const int buffersize = 1024;
int ret; struct sockaddr_nl snl;
bzero(&snl, sizeof(struct sockaddr_nl));
snl.nl_family = AF_NETLINK;
snl.nl_pid = getpid();
snl.nl_groups = 1; int s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
if (s == -1)
{
perror("socket");
return -1;
}
setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize)); ret = bind(s, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl));
if (ret < 0)
{
perror("bind");
close(s);
return -1;
} return s;
} int main(int argc, char* argv[])
{
int hotplug_sock = init_hotplug_sock(); while(1)
{
/* Netlink message buffer */
char buf[UEVENT_BUFFER_SIZE * 2] = {0};
recv(hotplug_sock, &buf, sizeof(buf), 0);
printf("%s\n", buf); /* USB 设备的插拔会出现字符信息,通过比较不同的信息确定特定设备的插拔,在这添加比较代码 */ }
return 0;
}

udev的主体部分在udevd.c文件中,它主要监控来自4个文件描述符的事件/消息,并做出处理:
  1. 来自客户端的控制消息。这通常由udevcontrol命令通过地址为/org/kernel/udev/udevd的本地socket,向udevd发送的控制消息。其中消息类型有:
  l UDEVD_CTRL_STOP_EXEC_QUEUE 停止处理消息队列。
  l UDEVD_CTRL_START_EXEC_QUEUE 开始处理消息队列。
  l UDEVD_CTRL_SET_LOG_LEVEL 设置LOG的级别。
  l UDEVD_CTRL_SET_MAX_CHILDS 设置最大子进程数限制。好像没有用。
  l UDEVD_CTRL_SET_MAX_CHILDS_RUNNING 设置最大运行子进程数限制(遍历proc目录下所有进程,根据session的值判断)。
  l UDEVD_CTRL_RELOAD_RULES 重新加载配置文件。
  2. 来自内核的hotplug事件。如果有事件来源于hotplug,它读取该事件,创建一个udevd_uevent_msg对象,记录当前的消息序列号,设置消息的状态为EVENT_QUEUED,然后并放入running_list和exec_list两个队列中,稍后再进行处理。
  3. 来自signal handler中的事件。signal handler是异步执行的,即使有signal产生,主进程的select并不会唤醒,为了唤醒主进程的select,它建立了一个管道,在 signal handler中,向该管道写入长度为1个子节的数据,这样就可以唤醒主进程的select了。
  4. 来自配置文件变化的事件。udev通过文件系统inotify功能,监控其配置文件目录/etc/udev/rules.d,一旦该目录中文件有变化,它就重新加载配置文件。
  
  其中最主要的事件,当然是来自内核的hotplug事件,如何处理这些事件是udev的关键。udev本身并不知道如何处理这些事件,也没有必要知道,因为它只实现机制,而不实现策略。事件的处理是由配置文件决定的,这些配置文件即所谓的rule。
  
  关于rule的编写方法可以参考《writing_udev_rules》,udev_rules.c实现了对规则的解析。
  
  在规则中,可以让外部应用程序处理某个事件,这有两种方式,一种是直接执行命令,通常是让modprobe去加载驱动程序,或者让mount去加载分区。另外一种是通过本地socket发送消息给某个应用程序。
  
  在udevd.c:udev_event_process函数中,我们可以看到,如果RUN参数以”socket:”开头则认为是发到socket,否则认为是执行指定的程序。
  
  下面的规则是执行指定程序:
  60-pcmcia.rules: RUN+="/sbin/modprobe pcmcia"
  
  下面的规则是通过socket发送消息:
  90-hal.rules:RUN+="socket:/org/freedesktop/hal/udev_event"

mission completed!

-----------------------------------------

在2.6内核里面,如果配置了sysfs的话,kernel启动的时候会自动在sysfs空间查找root设备。所以,型如root=/dev/mtdblock3等的参数还是可以工作的。至少在我这里可以。

-----------------------------------------

是这样的,谢谢指正!

更正如下:
========================
由于在kernel启动未完成以前我们的设备文件不可用,如果使用mtd设备作为rootfs的挂载点,这个时候/dev/mtdblock 这个设备目录是不存在的,我们无法让kernel通过/dev/mtdblock/X这样的设备找到rootfs,kernel只好停在那里惊慌。
这个问题我们可以通过给kernel传递设备号的方式来解决,在linux系统中,mtdblock的主设备号是31,part号 从0开始,那么以前的/dev/mtdblock/3就等同于31:03,以次类推,所以我们只需要修改bootloader传给kernel 的cmd line参数,使root=31:03,就可以让kernel在udevd未起来之前成功的找到rootfs。

另外一种方法就是给kernel传递未经归类的设备文件名,在udev未创建之前,所有的设备实际上已经通过sysfs 建立,mtdblockX的位置相对于/sys/block/mtdblockX/dev,这个文件里存放着mtdblockX的设备号,形式与上 一种方式相同。这时由于没有相应的udev规则,所有的设备都被隐含地映射到/dev目录下,mtdblockX对应于 /dev/mtdbockX,这样我们给kernel传递root=/dev/mtdblock3,kernel发现/dev没有被建立,就自动从映射表里 查找对应关系,最后取出/sys/block/mtdblockX/dev里的设备号,完成rootfs的挂载。
========================

-----------------------------------------

>2、在rootfs/etc/udev下建立一个rules.d目录,生成一个空的配置文件touch etc/udev/rules.d/udev.conf。 这里是不是有些问题,我本机上的udev.conf文件是在/etc/udev/下的。
-----------------------------------------

在udev_version.h中有以下两个定义:
#define UDEV_CONFIG_FILE "/etc/udev/udev.conf"
#define UDEV_RULES_FILE "/etc/udev/rules.d"

我想之所以采用rules.d的目录形式,其目的和rcX.d相同,可以在规则十分复杂的情况下用数字链接的方式控制
启动顺序。由于版本的不断升级,代码以及结构也越来越漂亮。

我们按照这种默认的方式来编写和存放规则文件,不失为一个好习惯。

-----------------------------------------

我的/etc/udev/下有rules.d目录和udev.conf文件
我的意思是udev.conf文件是否应该在/etc/udev目录而非/etc/udev/rules.d目录下

-----------------------------------------

是这样的,谢谢指正!

更正如下:
========================
2、在rootfs/etc/udev下建立一个rules.d目录,生成一个空的配置文件touch etc/udev/rules.d/udev.rules。
========================

兄弟确实是火眼金睛啊,这里有个笔误。

-----------------------------------------

pc的应用相对复杂些。

通过查看pc上的脚本/etc/init.d/udev我们发现:

udevtrigger同样可以完成udevstart所做的事情,而且它能够兼容以前MAKEDEV创建/dev的方式,配合MAKEDEV
把漏掉的设备自动补上。

udevsettle用来检查/dev是不是已经按照我们的规则在指定时间内创建完成,不管结果如何,只是向系统写入
一个报告,并不能够解决所遇到的问题。

udevstart是强制性的,udevtrigger则先做判断。

嵌入式系统上远没有这么复杂,所以我推荐在用udevstart来一次完成/dev的创建工作,因为现在的udevtrigger
还存在一些问题,处理uevent有时会重复创建同一个设备,创建失败的提示看起来很不美观。

-----------------------------------------

我看到这样的一段说明:
Every time the kernel notices an update in the device structure, it calls the /sbin/hotplug program. Hotplug runs the applications linked in the /etc/hotplug.d/default directory where you will also find a symlink to the udev application. Hotplug directs the information given by the kernel to the udev application which performs the necessary actions on the /dev structure (creating or deleting device files).

Q1: 在嵌入式系统中只需要udevd和udevstart就够了吗??? 不需要udev吗?

Q2: 不需要在/etc/hotplug.d/default 目录下建立到udev的符号链接吗? 如果不建立,hotplug怎样通知udev了????

-----------------------------------------

这段说明已经是老皇历了。
在古代的时候是需要hotplug的,现在这种framework已经被抛弃。
取而代之的是udevd通过netlink接口接收内核发来的消息。

-----------------------------------------

【相关资源】

Udev最新source code源码下载:

http://www.us.kernel.org/pub/linux/utils/kernel/hotplug/

【相关文章】

1.Udev
2.The README file in the udev source tree.
3.A OLS 2003 paper about how udev was originally designed, and why it was created.
4.The OLS 2003 presentation that went along with the paper.
5.An article about udev and how it works was published in Linux Journal in 2004.
6.Writing udev rules
7.A general udev primer
8.The last word on how udev and devfs compare.

udev 使用方法的更多相关文章

  1. 关于实现udev/mdev自动挂载与卸载

    在网上有很多关于讲mdev的自动挂载基本上都是一个版本,经过测试自动挂载确实可行,但是关于自动卸载mdev似乎不能很好的支持,经过修改已经可以做到与udev的效果相似.不能在挂载的目录中进行热插拔,否 ...

  2. udev和mdev hotplug事件

    关于udev和mdev之间的区别与联系我发现自己现在还没有把它完整的给区分开来和联系起来. 设备文件系统有devfs,mdev,udev mdev是udev的简化版本,是busybox中所带的程序,最 ...

  3. ARM上的linux如何实现无线网卡的冷插拔和热插拔

    ARM上的linux如何实现无线网卡的冷插拔和热插拔 fulinux 凌云实验室 1. 冷插拔 如果在系统上电之前就将RT2070/RT3070芯片的无线网卡(以下简称wlan)插上,即冷插拔.我们通 ...

  4. linux 设备驱动概述

    linux 设备驱动概述 目前,Linux软件工程师大致可分为两个层次: (1)Linux应用软件工程师(Application Software Engineer):       主要利用C库函数和 ...

  5. linux中的热插拔和mdev机制

    mdev手册(自己翻译的留着看) mdev实现U盘或SD卡的自动挂载 mdev的使用以及mdev.conf的规则配置--busybox linux中的热插拔和mdev机制 关于实现udev/mdev自 ...

  6. javaSE27天复习总结

    JAVA学习总结    2 第一天    2 1:计算机概述(了解)    2 (1)计算机    2 (2)计算机硬件    2 (3)计算机软件    2 (4)软件开发(理解)    2 (5) ...

  7. Linux服务器init 5启动图形界面,报错Retrigger failed udev events的解决方法

    1.开启linux系统的桌面环境,使用startx未成功,报如下错误: 提示:Retrigger failed udev events [root@ /]# startx xauth: creatin ...

  8. Ubuntu adb devices :???????????? no permissions (verify udev rules) 解决方法

    Ubuntu adb devices :???????????? no permissions (verify udev rules) 解决方法http://www.cnblogs.com/cat-l ...

  9. oracle_使用udev绑定磁盘方法

    scsi_id命令发出一个SCSI INQUIRY指令给设备,访问vital product data (VPD)页0x83的数据,那里包含设备的WWID和其他的信息,或者页0x80的数据,那里包含单 ...

随机推荐

  1. vue-自定义指令(directive )的使用方法

    前言 在vue项目中我们经常使用到 v-show ,v-if,v-for等内置的指令,除此之外vue还提供了非常方便的自定义指令,供我们对普通的dom元素进行底层的操作.使我们的日常开发变得更加方便快 ...

  2. 进程代数CSP基础知识总结(Communicating sequencing process)

    进程代数(Process Algebra) Process Algebra 理论 提出者 理论名称 缩写 论文链接 简介 C. A. R. Hoare/Tony Hoare Communicating ...

  3. Docker系列(26)- 发布镜像到阿里云容器服务

    1.登录阿里云 2.找到容器镜像服务 3.创建命名空间 4.创建镜像仓库 5.上传镜像

  4. Git(2) - git安装、本地仓库与远程仓库使用详细指南

    git版本控制工具 下载地址:https://www.git-scm.com/download/win选择对应版本的工具,下载后是一个exe执行文件: 常用git命令 命令 作用 git init(在 ...

  5. yum 安装 php 环境

    如此简单 第一步: sudo rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7. ...

  6. 鸿蒙内核源码分析(线程概念篇) | 是谁在不停的折腾CPU? | 百篇博客分析OpenHarmony源码 | v21.06

    百篇博客系列篇.本篇为: v21.xx 鸿蒙内核源码分析(线程概念篇) | 是谁在不断的折腾CPU | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调 ...

  7. P7295-[USACO21JAN]Paint by Letters P【平面图欧拉公式】

    正题 题目链接:https://www.luogu.com.cn/problem/P7295 题目大意 给出\(n*m\)的网格,每个格子上有字母,相同字母的四联通相邻格子为连通,每次询问一个子矩阵求 ...

  8. Java入门基础,必读!Java单行、多行和文档注释!

    在编写Java的过程中,我们需要对一些程序进行注释.除了方便自己阅读之外,我们还需要为他人更好地理解我们的程序.因此,我们需要对一些程序进行注释,这些注释可以是编程思想,也可以是程序的作用,可以说是J ...

  9. 使用Mybatis的一些基本配置及Mybatis与数据库交互测试验证

    1.简介 什么是MyBatis? MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.My ...

  10. 如何做好 NodeJS 框架选型?

    作为一个有一定工作经验的工程师,工作中经常会遇到技术选型的问题.比如当我们在工作中需要使用到 NodeJS 时,第一个要解决的问题就是如何选择一个合适的框架. 不同的框架有不同的特点,如果我们仅仅从框 ...