Linux设备模型

2009年12月14日 17:12    linux_Ultra
关键词: linux , 模型 , 设备
Linux设备驱动程序学习(15)
-Linux设备模型(热插拔、mdev 与 firmware)

热插拔
有 2 个不同角度来看待热插拔:
   从内核角度看,热插拔是在硬件、内核和内核驱动之间的交互。
   从用户角度看,热插拔是内核和用户空间之间,通过调用用户空间程序(如hotplug、udev 和 mdev)的交互。 当需要通知用户内核发生了某种热插拔事件时,内核才调用这个用户空间程序。
现在的计算机系统,要求 Linux 内核能够在硬件从系统中增删时,可靠稳定地运行。这就对设备驱动作者增加了压力,因为在他们必须处理一个毫无征兆地突然出现或消失的设备。
热插拔工具
当用户向系统添加或删除设备时,内核会产生一个热插拔事件,并在 /proc/sys/kernel/hotplug 文件里查找处理设备连接的用户空间程序。这个用户空间程序主要有
hotplug:这个程序是一个典型的 bash 脚本,只传递执行权给一系列位于 /etc/hot-plug.d/ 目录树的程序。hotplug 脚本搜索所有的有 .hotplug后缀的可能对这个事件进行处理的程序并调用它们, 并传递给它们许多不同的已经被内核设置的环境变量。(基本已被淘汰,具体内容请参阅《LDD3》)
udev :用于linux2.6.13或更高版本的内核上,为用户空间提供使用固定设备名的动态/dev目录的解决方案。它通过在 sysfs 的 /class/ 和/block/ 目录树中查找一个称为 dev的文件,以确定所创建的设备节点文件的主次设备号。所以要使用udev,驱动必须为设备在sysfs中创建类接口及其dev属性文件,方法和sculld模块中创建dev属性相同。 udev的资料网上十分丰富,我就不在这废话了,给出以下链接有兴趣的自己研究:
《UDEV Primer》(英文),地址:
http://webpages.charter.net/decibelshelp/LinuxHelp_UDEVPrimer.html

《udev规则编写》(luofuchong翻译),地址:
http://www.cnitblog.com/luofuchong/archive/2007/12/18/37831.html

《什么是udev》地址:
http://blog.csdn.net/steganography/archive/2006/04/10/657620.aspx

《udev-FAQ 中文翻译》地址:
http://gnawux.bokee.com/3225765.html

《udev轻松上路》地址:
http://www.blog.edu.cn/user1/3313/archives/2007/1635169.shtml

《Udev (简体中文)》地址:
http://wiki.archlinux.org/index.php/Udev_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87
)

Udev官方主页:
http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html
下载地址:
http://www.kernel.org/pub/linux/utils/kernel/hotplug/

在《LFS》中也有介绍udev的使用,很值得参考!下载地址:
http://lfs.osuosl.org/lfs/downloads/stable/


mdev:一个简化版的udev,是busybox所带的程序,十分适合嵌入式系统。

因为hotplug现在也在被慢慢地淘汰,udev不再依赖hotplug了,所以这里不再介绍;
udev较mdev复杂,不太适合嵌入式使用。(本人也有做udev的实验,交叉编译是通过了,但是使用上有问题,没有实现其功能。也许是我的文件系统没做好,以后有时间再研究和写记录。有成功高人的通知一声,交流一下经验。^_^谢谢!);
mdev简单易用,比较适合嵌入式系统,实验成功。以下详细介绍mdev的使用。
mdev

在一开始建立根文件系统时,我根据 WeiBing 的博客上《UDEV on embeded Linux-2.6.19.2》(地址:
http://weibing.blogbus.com/logs/4485453.html
)这篇文章的提示,开始使用mdev,但是当时只是启动时mdev -s一下,并没有深究。现在在学习了Linux设备模型之后,对于Linux中/dev目录的动态管理有了更深的认识,并认真的看了一下busybox中的mdev.txt文档并翻译了一下,做成了PDF(下载地址:
http://blogimg.chinaunix.net/blog/upfile2/080111091002.pdf
),在看下面的内容时请先看看这篇文档。

先声明一个要点:要实现设备节点文件的自动、动态的增删,必须在你自己的驱动源码中实现 类 接口,并在类设备的目录中添加包含设备号的名为“dev”的属性文件。

mdev原理及bug

要使用mdev,适当知道一下原理是必不可少的(能完整地研究mdev源码是最好的)。说实话起初我并没有想看mdev的源码,是在使用时发现了问题后才去研究了一下mdev的源码。现在简单介绍一下mdev的原理:

执行mdev -s:以‘-s’为参数调用位于/sbin目录写的mdev(其实是个链接,作用是传递参数给/bin目录下的busybox程序并调用它),mdev扫描 /sys/class 和/sys/block中所有的类设备目录,如果在目录中含有名为“dev”的文件,且文件中包含的是设备号,则mdev就利用这些信息为这个设备在/dev下创建设备节点文件。一般只在启动时才执行一次 “mdev -s”。

热插拔事件:由于启动时运行了命令:echo /sbin/mdev > /proc/sys/kernel/hotplug,那么当有热插拔事件产生时,内核就会调用位于 /sbin目录的mdev。这时mdev通过环境变量中的 ACTION 和DEVPATH,来确定此次热插拔事件的动作以及影响了/sys中的那个目录。接着会看看这个目录中是否有“dev”的属性文件,如果有就利用这些信息为这个设备在/dev 下创建设备节点文件。

源码的bug(个人意见):由于mdev是通过判断“dev”属性文件的路径字符串中的第6个字符是否为‘c’,来决定设备是字符设备还是块设备【type = (path[5] == 'c' ? S_IFCHR : S_IFBLK);例如path = "/sys/class/ldd/sculld*/"为字符设备,而/sys/devices/ldd0/sculld*/就会被误判为块设备】,那么如果你在非 /sys/class 和 /sys/block目录下建立了“dev”属性文件且内容是设备号(像sculld中就这样做了),那么mdev也会在/dev下创建设备节点文件。这样可能所创建的设备节点文件是错的。
以我实验为例,我以上一篇的文章中的sculld为基础,加上了类接口(这样在/sys/devices/ldd0/sculld*/和 /sys/class/ldd/sculld* 中都有内容为设备号的“dev”属性文件)。在运行时发现一直会将有的sculld*创建为块设备节点文件。郁闷死了,难道我的驱动有错???最后研究了mdev源码之后发现,只要在/sys中建立了“dev”属性文件且内容是设备号,mdev就会以所在的目录为名在/dev下创建设备节点文件。像sculld模块,mdev会为一个设备创建两次设备文件,由于文件名一样,第二次的文件会覆盖第一次的。如果第二次是因为/sys/devices/ldd0/sculld*/dev 产生的设备节点文件,那么设备节点文件就会被错误地创建为块设备。
我认为这个bug的解决办法有如下两种:
(1)在你写驱动的时候,只在/sys/class 和 /sys/block 中的类设备目录中存在包含设备号的“dev”属性文件。(你无法保证被人的驱动会这么做)
(2)修正mdev源码:
修改/busybox-1.9.0/util-linux/mdev.c文件的第328行:
if (!strcmp(action, "remove"))
       make_device(temp, 1);
else if (!strcmp(action, "add")) {
         if (env_path[2]=='l') make_device(temp,0);  //tekkamanninja
            if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE)
                load_firmware(getenv("FIRMWARE"), temp);
        }
也就是在增加设备节点文件之前检查/sys/目录下的路径是否为/class和/block(通过检查路径字符串的第3个字符是否为‘l’)。

本人推荐第二种做法!

mdev使用
mdev的使用在busybox中的mdev.txt文档已经将得很详细了。但作为例子,我简单讲讲我的使用过程:

(1)在编译时加上对mdev的支持(我是使用的是busybox1.9.0):
    Linux System Utilities  --->   
            
mdev      
               Support /etc/mdev.conf
                 Support command execution at device addition/removal

(2)在启动时加上使用mdev的命令:
我在自己创建的根文件系统(nfs)中的/linuxrc文件中添加了如下指令:
#挂载/sys为sysfs文件系统
    echo "----------mount /sys as sysfs"
    /bin/mount -t tmpfs mdev /dev
    /bin/mount -t sysfs sysfs /sys
    echo "----------Starting mdev......"
    /bin/echo /sbin/mdev > /proc/sys/kernel/hotplug
    mdev -s
注意:是/bin/echo /sbin/mdev > /proc/sys/kernel/hotplug,并非/bin/echo /bin/mdev > /proc/sys/kernel/hotplug。busybox的文档有错!!
  
(3)在你的驱动中加上对类设备接口的支持,并在类设备目录下添加包含设备号的名为“dev”的属性文件。

(4)至于/etc/mdev.conf文件,可有可无,不影响使用,只是添加了些功能。
     为了实验我在/etc创建了mdev.conf文件并输入了:
    sculld[0-1] 0:0 666 * echo tekkaman > /tmp/mdev
     这样,在挂载和卸载sculld.ko时,在/tmp/下会出现mdev文件,里面字符为tekkaman
具体的实验源码和现象在文章后面有。

firmware
硬件市场的激烈竞争, 使得制造商连一点用于设备控制固件的 EEPROM 的成本都不愿意花费。因此固件一般发布在和硬件配套的驱动包中,由操作系统(其实是驱动程序)负责传送固件到设备。
内核固件接口
获取固件的正确方法是当需要时从用户空间获取它。一定不要试图从内核空间直接打开包含固件的文件,那是一个易出错的操作, 因为它把策略(以文件名的形式)包含进了内核。正确的方法是使用固件接口:
#include linux/firmware.h>
int request_firmware(const struct firmware **fw,
                     const char *name, /* name 为固件文件名*/
                     struct device *device);
/*要求用户空间定位并提供一个固件映象给内核;若成功加载, 返回值是 0(否则返回错误码)*/
/*因为 request_firmware 需要用户空间的操作, 所以返回前将保持休眠。若驱动必须使用固件而不能进入休眠时,可使用以下异步函数:*/
int request_firmware_nowait(
    struct module *module, /* = THIS_MODULE*/
    int uevent,
    const char *name,
    struct device *device,
    void *context,/*不由固件子系统使用的私有数据指针*/
    void (*cont)(const struct firmware *fw, void *context));
/*如果一切正常,request_firmware_nowait 开始固件加载过程并返回 0. 过了一段时间后(默认10秒),将用加载的结果(若加载失败, fw 为 NULL)作为参数调用 cont。*/
/* fw 参数指向以下结构体:*/
struct firmware {
    size_t size;
    u8 *data;
};
/*那个结构包含实际的固件, 它现在可被下载到设备中.但是请注意:在发送它到硬件之前,必须检查这个文件以确保它是正确的固件映象(设备固件常常包含标识字符串、 校验和等等)*/
/*当固件已经发送到设备后,应当释放 firmware 结构体, 使用:*/
void release_firmware(struct firmware *fw);
注意:要使用firmware,必须要在配置内核时选上:
   Device Drivers  --->   
          Generic Driver Options  --->      
               Userspace firmware loading support
否则会出现: Unknown symbol release_firmware 和: Unknown symbol request_firmware 的错误。
               
固件接口工作原理
固件子系统使用 sysfs 和热插拔机制工作。当调用 request_firmware时, 函数将在 /sys/class/firmware 下创建一个以设备名为目录名的新目录,其中包含 3 个属性:
loading :这个属性应当被加载固件的用户空间进程设置为 1。当加载完毕, 它将被设为 0。被设为 -1 时,将中止固件加载。
data :一个用来接收固件数据的二进制属性。在设置 loading 为1后, 用户空间进程将固件写入这个属性。
device :一个链接到 /sys/devices 下相关入口项的符号链接。
一旦创建了 sysfs 入口项, 内核将为设备产生一个热插拔事件,并传递包括变量 FIRMWARE 的环境变量给处理热插拔的用户空间程序。FIRMWARE 被设置为提供给 request_firmware 的固件文件名。
用户空间程序定位固件文件, 并将其拷贝到内核提供的二进制属性;若无法定位文件, 用户空间程序设置 loading 属性为 -1。
若固件请求在 10 秒内没有被服务, 内核就放弃并返回一个失败状态给驱动。超时周期可通过 sysfs 属性 /sys/class/firmware/timeout 属性改变。
request_firmware 接口允许使用驱动发布设备固件。当正确地集成进热插拔机制后, 固件加载子系统允许设备不受干扰地工作。显然这是处理问题的最好方法,但固件受版权保护,小心违反版权法。
ARM9开发板实验

实验源码:
http://blogimg.chinaunix.net/blog/upfile2/080114113255.gz


实验现象:
[Tekkaman2440@SBC2440V4]#ls -l /dev/sculld*
ls: /dev/sculld*: No such file or directory
[Tekkaman2440@SBC2440V4]#cat /tmp/mdev
cat: can't open '/tmp/mdev': No such file or directory

[Tekkaman2440@SBC2440V4]#insmod /lib/modules/lddbus.ko
Mount lddbus ok !
Bus device is ldd0 !
You can see me in sys/module/ , sys/devices/ , sys/class/ and sys/bus/ !
[Tekkaman2440@SBC2440V4]#insmod /lib/modules/sculld.ko
[Tekkaman2440@SBC2440V4]#ls -l /dev/sculld*
crw-rw-rw-    1 root     root     252,   0 Jan  1 00:00 /dev/sculld0
crw-rw-rw-    1 root     root     252,   1 Jan  1 00:00 /dev/sculld1
crw-rw----    1 root     root     252,   2 Jan  1 00:00 /dev/sculld2
crw-rw----    1 root     root     252,   3 Jan  1 00:00 /dev/sculld3
[Tekkaman2440@SBC2440V4]#rmmod sculld
The LDD class
ldd_classdev_release : sculld0 release!
The LDD class
ldd_classdev_release : sculld1 release!
The LDD class
ldd_classdev_release : sculld2 release!
The LDD class
ldd_classdev_release : sculld3 release!
[Tekkaman2440@SBC2440V4]#ls -l /dev/sculld*
ls: /dev/sculld*: No such file or directory
[Tekkaman2440@SBC2440V4]#cat /tmp/mdev
tekkaman



到此 Linux设备模型 的学习暂时告一段落。
欢迎分享本文,转载请保留出处:http://www.eechina.com/thread-6525-1-1.html     【打印本页】
linux_Ultra 发表于 2009-12-14 18:35:46
udev实现原理

            

转载时请注明出处和作者联系方式:http://blog.csdn.net/absurd

作者联系方式:李先静<xianjimli at hotmail dot com>

更新时间:2007-4-29



相对于linux来说,udev还是一个新事物。然而,尽管它03年才出现,尽管它很低调(J),但它无疑已经成为linux下不可或缺的组件了。udev是什么?它是如何实现的?最近研究Linux设备管理时,花了一些时间去研究udev的实现。



udev是什么?u 是指user space,dev是指device,udev是用户空间的设备驱动程序吗?最初我也这样认为,调试内核空间的程序要比调试用户空间的程序复杂得多,内核空间的程序的BUG所引起的后果也严重得多,device driver是内核空间中所占比较最大的代码,如果把这些device driver中硬件无关的代码,从内核空间移动到用户空间,自然是一个不错的想法。



但我的想法并不正确,udev的文档是这样说的,

1.         dynamic replacement for /dev。作为devfs的替代者,传统的devfs不能动态分配major和minor的值,而major和minor非常有限,很快就会用完了。udev能够像DHCP动态分配IP地址一样去动态分配major和minor。



2.         device naming。提供设备命名持久化的机制。传统设备命名方式不具直观性,像/dev/hda1这样的名字肯定没有boot_disk这样的名字直观。udev能够像DNS解析域名一样去给设备指定一个有意义的名称。



3.         API to access info about current system devices 。提供了一组易用的API去操作sysfs,避免重复实现同样的代码,这没有什么好说的。



我们知道,用户空间的程序与设备通信的方法,主要有以下几种方式,

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>



static int init_hotplug_sock(void)

{

    struct sockaddr_nl snl;

    const int buffersize = 16 * 1024 * 1024;

    int retval;



    memset(&snl, 0x00, sizeof(struct sockaddr_nl));

    snl.nl_family = AF_NETLINK;

    snl.nl_pid = getpid();

    snl.nl_groups = 1;



    int hotplug_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);

    if (hotplug_sock == -1) {

        printf("error getting socket: %s", strerror(errno));

        return -1;

    }



    /* set receive buffersize */

    setsockopt(hotplug_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize));



    retval = bind(hotplug_sock, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl));

    if (retval < 0) {

        printf("bind failed: %s", strerror(errno));

        close(hotplug_sock);

        hotplug_sock = -1;

        return -1;

    }



    return hotplug_sock;

}



#define UEVENT_BUFFER_SIZE      2048



int main(int argc, char* argv[])

{

         int hotplug_sock       = init_hotplug_sock();

        

         while(1)

         {

                   char buf[UEVENT_BUFFER_SIZE*2] = {0};

                   recv(hotplug_sock, &buf, sizeof(buf), 0);

                   printf("%s\n", buf);

         }



         return 0;

}



编译:

gcc -g hotplug.c -o hotplug_monitor



运行后插/拔U盘,可以看到:

add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1

add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/usbdev2.2_ep00

add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0

add@/class/scsi_host/host2

add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep81

add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep02

add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep83

add@/class/usb_device/usbdev2.2

add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/host2/target2:0:0/2:0:0:0

add@/class/scsi_disk/2:0:0:0

add@/block/sda

add@/block/sda/sda1

add@/class/scsi_device/2:0:0:0

add@/class/scsi_generic/sg0

remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep81

remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep02

remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep83

remove@/class/scsi_generic/sg0

remove@/class/scsi_device/2:0:0:0

remove@/class/scsi_disk/2:0:0:0

remove@/block/sda/sda1

remove@/block/sda

remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/host2/target2:0:0/2:0:0:0

remove@/class/scsi_host/host2

remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0

remove@/class/usb_device/usbdev2.2

remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/usbdev2.2_ep00

remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1



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"



hal正是我们下一步要关心的,接下来我会分析HAL的实现原理。



~~end~~
linux_Ultra 发表于 2009-12-14 18:37:18
第一、什么是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
文中描述的复杂规则我们可以暂时不用去理会,上路指南将带领我们轻松穿过这片迷雾。这里提供一个由简入
繁的方法,对于嵌入式系统,这样做可以一劳永逸。

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会自动分析这个文件。

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中的配置项。
linux_Ultra 发表于 2009-12-14 18:37:59
Udev (简体中文)
From ArchWiki
Jump to: navigation, search


i18n
English
Русский
繁體中文
简体中文

注意: 如果您是从DevFS升级到Udev, 请查看 DevFS to Udev.

这篇文档将介绍udev的一些新的变化。从084版开始,udev能够代替hotplug和coldplug的所有功能。正因为这样,hotplug包已经从Arch仓库中去掉了。
Contents
[hide]

    * 1 重要提示
    * 2 基本需求
    * 3 最近更新
    * 4 模块禁用列表
    * 5 load_modules: 有用的启动参数
    * 6 已知的硬件问题
    * 7 自动加载带来的一些问题
          o 7.1 CPUFreq模块
          o 7.2 声音问题和一些不能自动加载的模块
          o 7.3 多个同类型设备(网卡,声卡)每次启动的都不同
    * 8 自己编译内核造成的一些已知问题
          o 8.1 Udev无法启动
          o 8.2 CD/DVD符号和权限错误
    * 9 Udev小窍门
          o 9.1 自动加载usb设备

重要提示

...切记,在使用udev加载任何modules(内核模块)之前(无论是否是启动时自动加载),您必须在/etc/rc.conf将MOD_AUTOLOAD选项设置为yes ,否则您必须手动加载这些modules。您可以修改rc.conf中的MODULES或者使用modprobe命令来手动加载您所需要的modules。另一种方法是用hwdetect --modules生成系统硬件的modules列表,然后将这个列表添加到rc.conf中让系统启动时自动加载这些modules。
基本需求

    * 内核: 2.6.15或更高版本。
    * 您将无法在fstab和bootloader设置中再使用DevFS格式的设备名称! 更多相关内容请查看DevFS to Udev。

最近更新

    * startudev程序被取出。如果需要重新加载udev规则请使用 /etc/start_udev。
    * Udev代替了hotplug和hwdetect的功能。同时我们保存了hwdetect,并且只在 mkinitrd程序生成initrd的时候用到。
    * Udev可以同时加载多个模块,这样可以加快启动速度,然而,这样做的结果是她不能保证每次加载的顺序,所以当你使用多声卡或网卡的时候就会出现问题,这个问题下面将会再讨论。

模块禁用列表

udev也会犯错或加载错误的模块。为了防止错误的发生,你可以使用模块禁用列表 。一旦加入该列表的模块,无论是启动时,或者是运行时(如usb硬盘等)udev都不会加载这些模块。

只需在您在 rc.conf的MODULES中对应模块前加上感叹号(!)就可禁用该模块。

例如,

MODULES=(!moduleA !moduleB)

load_modules: 有用的启动参数

如果您在内核启动参数中加入load_modules=off,那么udev会停止任何自动加载工作. 如果系统出现问题时,这个功能会十分有用。如果udev加载了有问题的模块导致系统挂起或者其它严重的问题时,你可以使用这个参数来禁用自动加载,以此来防止加载有问题的模块。
已知的硬件问题

- BusLogic设备被损坏而且导致启动时死机。

这是一个内核的Bug目前还没有修正。

- PCMCIA读卡器被认为是可移除设备.

把它们加入到/etc/pmount.allow中,使用hal的pmount来读取

自动加载带来的一些问题
CPUFreq模块

我门还没有找到一个很好的方法加载不同的CPUFreq控制器,所以我们把从自动加载进程里把它去掉了。如果您需要测量CPU频率,你必须在rc.conf的MODULES队列中显式的加入合适的模块。
声音问题和一些不能自动加载的模块

一些用户跟踪发现问题出在/etc/modprobe.conf中一些旧的部分,试着去掉这些旧的部分再试试看。
多个同类型设备(网卡,声卡)每次启动的都不同

因为udev同时加载所有模块,所以一些设备可能初始化顺序不同。例如同时有两个网卡时,它们总是在eth0和eth1之间变来变去。

常用的解决办法是在您的rc.conf文件中通过修改MODULES队列来指明顺序。这个队列里的模块将在udev自动加载之前由系统加载,因此您可以控制模块在启动时加载顺序。

# 在e100之前加载8139too
MODULES=(8139too e100)

另一个解决网卡的方法是使用udev-sanctified方法为每个网卡静态命名。创建文件/etc/udev/rules.d/10-network.rules然后将不同的网卡通过MAC地址绑定到不同的名字上:

SUBSYSTEM=="net", SYSFS{address}=="aa:bb:cc:dd:ee:ff", NAME="lan0"
SUBSYSTEM=="net", SYSFS{address}=="ff:ee:dd:cc:bb:aa", NAME="wlan0"

同时,您需要注意以下内容:

    * 您可以通过下面的命令获得网卡的MAC地址:: udevinfo -a -p /sys/class/net/<你的网卡>
    * 注意在udev规则文件中使用小写的16进制MAC地址,因为udev无法识别大写的MAC地址。
    * 一些用户在使用旧的命名方式时出现问题,例如: eth0, eth1, 等等. 如果出现这个问题,试试使用 "lan"或者"wlan"之类的名字.

注意不要忘记修改您的/dec/rc.conf和其它使用ethX命名的配置文件。
自己编译内核造成的一些已知问题
Udev无法启动

请确定您的内核版本大于或等于2.6.15。较早的内核没有udev自动装载所需要的uevent功能。
CD/DVD符号和权限错误

如果您使用2.6.15的内核的话,您需要安装ABS的uevent补丁(它从2.6.16内核中抽取了一些uevent功能)。您可以使用abs命令来同步ABS树,然后您就可以在/var/abs/kernels/kernel26/下找到abs补丁。
Udev小窍门
自动加载usb设备

KERNEL=="sd[a-z]", NAME="%k", SYMLINK+="usb%m", GROUP="users", OPTIONS="last_rule"
ACTION=="add", KERNEL=="sd[a-z][0-9]", SYMLINK+="usb%n", GROUP="users", NAME="%k"
ACTION=="add", KERNEL=="sd[a-z][0-9]", RUN+="/bin/mkdir -p /mnt/usb%n"
ACTION=="add", KERNEL=="sd[a-z][0-9]", PROGRAM=="/lib/udev/vol_id -t %N", RESULT=="vfat", RUN+="/bin/mount -t vfat -o rw,noauto,sync,dirsync,noexec,nodev,noatime,dmask=000,fmask=111 /dev/%k /mnt/usb%n", OPTIONS="last_rule"
ACTION=="add", KERNEL=="sd[a-z][0-9]", RUN+="/bin/mount -t auto -o rw,noauto,sync,dirsync,noexec,nodev,noatime /dev/%k /mnt/usb%n", OPTIONS="last_rule"
ACTION=="remove", KERNEL=="sd[a-z][0-9]", RUN+="/bin/umount -l /mnt/usb%n"
ACTION=="remove", KERNEL=="sd[a-z][0-9]", RUN+="/bin/rmdir /mnt/usb%n", OPTIONS="last_rule"

把这些udev规则放到/etc/udev/rules.d/下任何一个文件名以.rules结尾的文件中,例如/etc/udev/rules.d/sda.rules。

如果想同时建立/media到/mnt符号连接,可以使用下面的版本:

KERNEL=="sd[a-z]", NAME="%k", SYMLINK+="usbhd-%k", GROUP="users", OPTIONS="last_rule"
ACTION=="add", KERNEL=="sd[a-z][0-9]", SYMLINK+="usbhd-%k", GROUP="users", NAME="%k"
ACTION=="add", KERNEL=="sd[a-z][0-9]", RUN+="/bin/mkdir -p /media/usbhd-%k"
ACTION=="add", KERNEL=="sd[a-z][0-9]", RUN+="/bin/ln -s /media/usbhd-%k /mnt/usbhd-%k"
ACTION=="add", KERNEL=="sd[a-z][0-9]", PROGRAM=="/lib/udev/vol_id -t %N", RESULT=="vfat", RUN+="/bin/mount -t vfat -o rw,noauto,sync,dirsync,noexec,nodev,noatime,dmask=000,fmask=111 /dev/%k /media/usbhd-%k", OPTIONS="last_rule"
ACTION=="add", KERNEL=="sd[a-z][0-9]", RUN+="/bin/mount -t auto -o rw,noauto,sync,dirsync,noexec,nodev,noatime /dev/%k /media/usbhd-%k", OPTIONS="last_rule"
ACTION=="remove", KERNEL=="sd[a-z][0-9]", RUN+="/bin/rm -f /mnt/usbhd-%k"
ACTION=="remove", KERNEL=="sd[a-z][0-9]", RUN+="/bin/umount -l /media/usbhd-%k"
ACTION=="remove", KERNEL=="sd[a-z][0-9]", RUN+="/bin/rmdir /media/usbhd-%k", OPTIONS="last_rule"

注意!如果你是用的其它的固定设备(例如SATA的硬盘,您可以从/etc/fstab中查看)被识别为/dev/sdX,您必须从sd[a-z] 中去掉你的那个sdX。例如,如果您的SATA硬盘被是识别为/dev/sda,您就需要把所有的“sd[a-z]”替换成“sd[b-z]”。在规则文件的文件名前加上数字(如:010.udev.rules)是个很好的主意,这样udev在读取标准规则前,将会读取这个规则文件。这些规则设置后不需要修改/etc/fstab文件。请查看mount命令的参数来修改权限等特性(您可以从论坛搜索查看mount命令的参数,然后根据您的需要修改它们)。
linux_Ultra 发表于 2009-12-14 18:39:20
udev 是Linux kernel 2.6系列的设备管理器。它主要的功能是管理/dev目錄底下的设备节点。它同时也是用来接替devfs及hotplug的功能,这意味着它要在添加/删除硬件时处理/dev目录以及所有用户空间的行为,包括加载firmware时。

udev的最新版本依赖于升级后的Linux kernel 2.6.13的uevent接口的最新版本。使用新版本udev的系统不能在2.6.13以下版本启动,除非使用noudev参数来禁用udev并使用传统的/dev来进行设备读取。
目录
[隐藏]

    * 1 概要
    * 2 运行方式
    * 3 系统架构
    * 4 作者
    * 5 外部链接
    * 6 参考文献

[编辑] 概要

在传统的Linux系统中,/dev目录下的设备节点为一系列静态存在的文件,而udev则动态提供了在系统中实际存在的设备节点。虽然devfs提供了类似功能,udev的支持者也给出了很多udev实现比devfs好的理由[1]:

    * udev支持设备的固定命名,而并不依赖于设备插入系统的顺序。默认的udev设置提供了存储设备的固定命名。任何硬盘都根据其唯一的文件系统id、磁盘名称及硬件连接的物理位置来进行识别。
    * udev完全在用户空间执行,而不是像devfs在内核空间一样执行。结果就是udev将命名策略从内核中移走,并可以在节点创建前用任意程序在设备属性中为设备命名。

[编辑] 运行方式

udev是一个通用的内核设备管理器。它以守护进程的方式运行于Linux系统,并监听在新设备初始化或设备从系统中移除时内核(通过netlink socket)发出的uevent。

系统提供了一套规则用于匹配可发现的设备事件和属性的导出值。匹配规则可能命名并创建设备节点,并运行配置程序来对设备进行设置。udev规则可以匹配像内核子系统、内核设备名称、设备的物理等属性,或设备序列号的属性。规则也可以请求外部程序提供信息来命名设备,或指定一个永远一样的自定义名称来命名设备,而不管设备什么时候被系统发现。
[编辑] 系统架构
Ambox outdated serious.svg
        当前條目或章節需要更新。
請更新本文以反映近況和新增内容。完成修改時,請移除本模板。

udev系统可以分为三个部分:

    * namedev函数库,处理设备的命名。
    * libsysfs函数库,进行设备信息的读取(080版本后废弃)
    * 守护进程udevd,处于用户空间,用于创建虚拟/dev

系统获取内核通过netlink socket发出的信息。早期的版本使用hotplug,并在/etc/hotplug.d/default添加一个链接到自身来达到目的。
[编辑] 作者

udev由Greg Kroah-Hartman和Kay Sievers共同开发,并得到Dan Stekloff等人的帮助。
[编辑] 外部链接

    * (英文)udev在kernel.org的主页
    * (英文)Kay Sievers写的udev最近动态
    * (英文)如何编写udev规则
    * (英文)udev问答集
    * (英文)Gentoo的udev指南
    * (英文)udev和devfs的对比
    * (英文)Linux1394常见问题:在不同驱动器上创建设备节点要如何设置udev规则
    * (英文)udev教程

[编辑] 参考文献

   1. ^ (英文)udev and devfs - The final word(2003年12月30日).於2008年1月13日查閱.
geyingzhen 发表于 2009-12-15 15:26:24
alpha321 发表于 2009-12-16 09:42:14
非常感谢楼主提供这么精彩的文章!
cybxlyh 发表于 2011-1-26 11:30:56
楼主,我在板子上运行udevd后,插入U盘,内核有打印信息,并且在/sys/block下有sdb,sdb1以及里面的dev和uevent,但是udevd却捕获不到uevent事件,导致无法创建节点,规则文件无法运行,U盘也无法挂载。如果我运行udevstart,节点就可以被创建,规则文件就能被执行,实现U盘挂载,这说明dev,uevent事件是有效的,只是udevd无法自动进行捕获处理,你知道这会是什么原因吗?
freeboy898 发表于 2011-11-4 14:46:15
相当详细的文章哦,非常感谢楼主分享!
wk978 发表于 2016-8-14 15:02:36
谢谢分享
您需要登录后才可以发表评论 登录 | 立即注册

相关文章

相关视频演示

厂商推荐


关于我们  -  服务条款  -  使用指南  -  站点地图  -  友情链接  -  联系我们
电子工程网 © 版权所有   京ICP备11013910号 | 京公网安备11010502021702
回顶部