linux驱动学习--第十天:第五章 Linux 文件系统与设备文件系统(四) 之 设备文件系统 devfs 和 udev

来源:互联网 发布:车载空气净化器 知乎 编辑:程序博客网 时间:2024/04/28 02:19

udev设备文件系统

devfs(设备文件系统)是由Linux 2.4 内核引入的,引入时被许多工程师给予了高度评价,它的出现
使得设备驱动程序能自主地管理它自己的设备文件。具体来说,devfs 具有如下优点。
 1、可以通过程序在设备初始化时在/dev 目录下创建设备文件,卸载设备时将它删除。
 2、设备驱动程序可以指定设备名、所有者和权限位,用户空间程序仍可以修改所有者和权限位。

     3、不再需要为设备驱动程序分配主设备号以及处理次设备号,

           在程序中可以直接给register_chrdev()传递0 主设备号以动态获得可用的主设备号,并在devfs_register()中指定次设备号。

 

/*创建设备目录*/
devfs_handle_t devfs_mk_dir(devfs_handle_t dir, const char *name, void *info);
/*创建设备文件*/
devfs_handle_t devfs_register(devfs_handle_t dir, const char *name, unsigned
int flags, unsigned int major, unsigned int minor, umode_t mode, void *ops,
void *info);
/*撤销设备文件*/
void devfs_unregister(devfs_handle_t de);


在Linux 2.4 的设备驱动编程中,分别在模块加载和卸载函数中创建和撤销设备文件是被普遍采用并值
得大力推荐的好方法。代码清单5.5 给出了一个使用devfs 的例子。

 

devfs 的使用范例
1 static devfs_handle_t devfs_handle;
2 static int _ _init xxx_init(void)
3 {
4 int ret;
5 int i;
6 /*在内核中注册设备*/
7 ret = register_chrdev(XXX_MAJOR, DEVICE_NAME, &xxx_fops);
8 if (ret < 0)
9 {
10 printk(DEVICE_NAME " can't register major number\n");
11 return ret;
12 }
13 /*创建设备文件*/
14 devfs_handle =devfs_register(NULL, DEVICE_NAME, DEVFS_FL_DEFAULT,
15 XXX_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, &xxx_fops, NULL);
16 ...
17 printk(DEVICE_NAME " initialized\n");
18 return 0;
19 }
20
21 static void _ _exit xxx_exit(void)
22 {
23 devfs_unregister(devfs_handle); /*撤销设备文件*/
24 unregister_chrdev(XXX_MAJOR, DEVICE_NAME); /*注销设备*/
25 }
26
27 module_init(xxx_init);
28 module_exit(xxx_exit);

代码中第7 行和第24 行分别用于注册和注销字符设备,使用的register_chrdev()和unregister_ chrdev()
在Linux2.6 内核中虽然仍然被支持,但是是过时的做法。第14 行和第23 行分别用于创建和删除devfs 文件节点。

 

udev 设备文件系统

尽管 devfs 有这样和那样的优点,但是,在Linux 2.6 内核中,devfs 被认为是过时的方法,并最终被抛
弃,udev 取代了它。Linux VFS 内核维护者Al Viro 指出了udev 取代devfs 的几点原因:

     1、 devfs 所做的工作被确信可以在用户态来完成。
 2、一些 bug 相当长的时间内未被修复。
 3、devfs 的维护者和作者停止了对代码的维护工作。


udev 完全在用户态工作,利用设备加入或移除时内核所发送的热插拔事件(hotplug event)来工作。在热
插拔时,设备的详细信息会由内核输出到位于/sys 的sysfs 文件系统。udev 的设备命名策略、权限控制和事件
处理都是在用户态下完成的,它利用sysfs 中的信息来进行创建设备文件节点等工作。
由于 udev 根据系统中硬件设备的状态动态更新设备文件,进行设备文件的创建和删除等,因此,在使
用udev 后,/dev 目录下就会只包含系统中真正存在的设备了。
devfs 与udev 的另一个显著区别在于:采用devfs,当一个并不存在的/dev 节点被打开的时候,devfs
能自动加载对应的驱动,而udev 则不能。这是因为udev 的设计者认为Linux 应该在设备被发现的时候加
载驱动模块,而不是当它被访问的时候。udev 的设计者认为devfs 所提供的打开/dev 节点时自动加载驱动
的功能对于一个配置正确的计算机是多余的。系统中所有的设备都应该产生热插拔事件并加载恰当的驱
动,而udev 能注意到这点并且为它创建对应的设备节点。

sysfs 文件系统与Linux 设备模型

1.sysfs 文件系统


2.kobject 内核对象

3.kset 内核对象集合

 

4.subsystem 内核对象子系统

 

5.Linux 设备模型组件


6.属性

 

udev 的组成

 

udev 规则文件

 

创建和配置udev

 

udev 包括udev、udevcontrol、udevd、udevsend、udevmonitor、udevsettle、udevstart、udevinfo、udevtest
和udevtrigger 等处于用户空间的程序。在嵌入式系统中,只需要udevd 和udevstart 就能使udev 工作。我
们可以按照下面的步骤来生成和配置udev。
( 1 ) 从http://www.us.kernel.org/pub/linux/utils/kernel/hotplug/ 下载udev 程序, 如笔者下载的是
udev-114.tar.gz。
(2)运行“tar zxvf udev-114.tar.gz”命令解压缩udev 程序包。
(3)运行make 编译udev,当前目录下会生成test-udev、udevcontrol、udevd、udevinfo、udevmonitor、
udevsettle、udevstart、udevtest 和udevtrigger 共9 个工具程序。
(4)把第(3)步生成的工具程序复制到/sbin 目录,同时把解压缩udev-114.tar.gz 后获得的etc 目录
下的udev 目录复制到系统的/etc 下,etc/udev 下包含udev.conf 配置文件、rules.d 目录(该目录中的文件给
出了设备规则)等。
(5)编写完成启动、停止、重新启动等工作的udev 脚本,

udev 运行脚本范例

 

1 #!/bin/sh -e
2 # udev 初始脚本
3
4 # 检查包是否已经被安装
5 [ -x /sbin/udevd ] || exit 0
6
7 case "$1" in
8 start)
9 # 需要2.6.15 中引入的uevent 的支持,如果系统中没有,使用静态/dev
10 if [ ! -f /sys/class/mem/null/uevent ]; then
11 if mountpoint -q /dev; then

‐ 29 ‐
12 umount -l /dev/.static/dev
13 umount -l /dev
14 fi
15 exit 1
16 fi
17
18 if ! mountpoint -q /dev; then
19 # initramfs 没有挂载/dev,因此,这里需要完成
20 mount -n --bind /dev /etc/udev
21 mount -n -t tmpfs -o mode=0755 udev /dev
22 mkdir -m 0700 -p /dev/.static/dev
23 mount -n --move /etc/udev /dev/.static/dev
24 fi
25
26 # 复制默认的设备树
27 cp -a -f /lib/udev/devices/* /dev
28
29 # 现在全部通过netlink 进行,所以之前的热插拔程序不再使用
30 if [ -e /proc/sys/kernel/hotplug ]; then
31 echo "" > /proc/sys/kernel/hotplug
32 fi
33
34 # 开始udevd
35 log_begin_msg "Starting kernel event manager..."
36 if start-stop-daemon --start --quiet --exec /sbin/udevd -- --daemon; then
37 log_end_msg 0
38 else
39 log_end_msg $?
40 fi
41
42 # 打开udevtrigger 的事务日志
43 /sbin/udevmonitor -e >/dev/.udev.log &
44 UDEV_MONITOR_PID=$!
45
46 # 弥补丢失的事件
47 log_begin_msg "Loading hardware drivers..."
48 /sbin/udevtrigger
49 if /sbin/udevsettle; then
50 log_end_msg 0
51 else
52 log_end_msg $?
53 fi
54
55 # 杀死udevmonitor
56 kill $UDEV_MONITOR_PID
57 ;;
58 stop)
59 log_begin_msg "Stopping kernel event manager..."
60 if start-stop-daemon --stop --quiet --oknodo --exec /sbin/udevd; then
61 log_end_msg 0
62 else
63 log_end_msg $?
64 fi

65 umount -l /dev/.static/dev
66 umount -l /dev
67 ;;
68 restart)
69 cp -au /lib/udev/devices/* /dev
70
71 log_begin_msg "Loading additional hardware drivers..."
72 /sbin/udevtrigger
73 if /sbin/udevsettle; then
74 log_end_msg 0
75 else
76 log_end_msg $?
77 fi
78 ;;
79 reload|force-reload)
80 log_begin_msg "Reloading kernel event manager..."
81 if start-stop-daemon --stop --signal 1 --exec /sbin/udevd; then
82 log_end_msg 0
83 else
84 log_end_msg $?
85 fi
86 ;;
87 *)
88 echo "Usage: /etc/init.d/udev {start|stop|restart|reload|force-reload}"
89 exit 1
90 ;;
91 esac
92
93 exit 0
如果已经知道了设备对应的sysfs 路径,则可以使用udevtest 工具测试udev 对该设备所采取的行动,
这可以帮助进行udev 规则的调试。如运行“udevtest /sys/class/tty/ttys0”命令的结果为:
This program is for debugging only, it does not run any program,
specified by a RUN key. It may show incorrect results, because
some values may be different, or not available at a simulation run.
parse_file: reading '/etc/udev/rules.d/05-udev-early.rules' as rules file
parse_file: reading '/etc/udev/rules.d/60-persistent-input.rules' as rules file
parse_file: reading '/etc/udev/rules.d/60-persistent-storage.rules' as rules file
parse_file: reading '/etc/udev/rules.d/95-udev-late.rules' as rules file
main: looking at device '/class/tty/ttys0' from subsystem 'tty'
udev_rules_get_name: no node name set, will use kernel name ''
udev_db_get_device: found a symlink as db file
udev_device_event: device '/class/tty/ttys0' already in database, cleanup
udev_node_add: creating device node '/dev/ttys0', major=3, minor=48, mode=0660, uid=0,
gid=0
main: run: 'socket:/org/kernel/udev/monitor'

从中可以看出udev 将为与/sys/class/tty/ttys0 对应的设备使用内核中的名称通过udev_node_ add 创建/dev/ttys0 设备文件,主、次设备号分别为3 和48。

 

总结


Linux 系统用户空间的文件编程有两种方法,即通过Linux API 和通过C 库函数访问文件。用户空间
看不到设备驱动,能看到的只有设备对应的文件,因此文件编程即是用户空间的设备编程。
Linux 系统按照功能对文件系统的目录结构进行了良好的规划。/dev 是设备文件的存放目录,devfs 和
udev 分别是Linux 2.4 设备和Linux 2.6 设备生成设备文件节点的方法,前者运行于内核空间,后者运行于
用户空间。
Linux 2.6 设备通过一系列数据结构定义了设备模型,设备模型与sysfs 文件系统中的目录和文件存在
一种对应关系,udev 可以利用sysfs 中记录的信息定义规则并提取主次设备号动态创建/dev 设备文件节点。

 附记:纯理论的东西,学起来非常枯燥,我就简单的看了两遍,有些东西就不做摘抄了,自己下载书本看吧。

然后从明天开始,就有代码可写了。这个时候,估计进度会非常的慢。可能一个章节可能会用时一个星期或者两个星期。

后期的代码和结果我都会有截图上传的。慢慢来。

再次提醒自己:再花三年时间,实现一个不一样的自己。