在文件include/linux/major.h中提供了所使用的linux发行版中主设备号的完整列表。
fs/open.c: 本文件实现了许多与文件操作相关的系统调用。主要有文件的创建、打开和关闭,文件宿主和属性的修改、文件访问权限的修改、文件操作时间的修改和系统文件系统 root 的变动等。
- 设备驱动程序如何组织??
- 每个设备驱动程序都可以使用轮询或者中断的策略
- 在传统的UNIX中,块设备和字符设备的接口完全不同,但是在linux中二者之间的区别没有那么明显。设备的接口被设计为看起来和文件系统的接口相同。(每个文件系统都定义了一组固定的操作函数集合,这些函数可以适用于任何文件和目录)。设备驱动程序只需要定义对他有意义的函数操作。设计人员决定这个设备需要那些操作,然后实现这些所需要的函数,最后使用所定义的特定的入口点建立一个struct file_operations结构的实例。
在UNIX中,设备驱动是在系统启动时进行定义的,此时,设备驱动程序在内核中注册自己特有的设备接口的实现;而在linux中,设备驱动接口实现既可以在系统启动时注册,也可以以模块的方式注册。 - 设备驱动程序是一些函数和数据结构的集合,为管理设备实现一个简单的接口。内核通过这个接口来请求驱动程序对设备进行I/O操作。内核通过一个主设备号和从设备号对设备驱动程序进行引用。linux已经为广泛支持的设备类别规定了一些标准的主设备号。如软盘:2;IDE硬盘:3;并口:6。
这些在include/linux/major.h中有介绍。
由于许多主设备号已经静态的分配给了公用设备,linux提供了动态分配机制以获取空闲的主设备号。
从设备号是一个从0开始的一个8位数字。
- 内核怎么知道设备的存在?
在启动时,内核为系统中的每个设备建立一个特殊文件:/dev目录下的设备文件,用来为该设备指定设备驱动程序。??? - 1)初始化代码:定义初始化函数**_init
可装载模块不需要使用init函数,但init_module()函数可用于初始化代码。例如串口的初始化代码为tty_init()。
**_init()在装载驱动程序时允许设备驱动程序设置必需的数据结构。另外,初始化代码应该使用register_chrdev()函数或者register_blkdev()函数在内核中注册字符设备和块设备的驱动程序接口。
2)接下来必须修改内核初始化代码,使得内核可以调用新的**_init()函数.
字符设备:修改drives/char/merr.c中的chr_dev_init()函数;
块设备: 修改drives/block/ll_rw_bl.c中的blk_dev_init()函数。
SCSI设备和网络设备的驱动程序有自己的初始化程序。
3)设备完成初始化工作后,内核就可以在驱动程序的**_open()函数中使用这样的系统调用:
open(/dev/**, O_RDONLY);
用户空间程序调用内核中的open()系统调用时就调用了**_open(),因为**_open()在内核中注册为设备文件/dev/**的open函数。
- 可装载内核模块驱动程序:
init_module()代码中的初始化代码与上述传统方法中的init代码的功能是一样的,唯一不同的是这段代码是由/sbin/insmod调用的,而不是由内核初始化代码调用的。
参考内容:
Linux驱动程序:- 用户态和内核态:
多数的操作系统都把内核和运行在其上的应用程序分为2个层次管理,这就是用户态和内核态。
内核态:有较高的权限,可以控制处理器内存的映射和分配方式,访问外设空间和处理器的特殊状态寄存器,控制中断和DMA等。
用户态:只能运行系统上的应用程序。
在有MMU的处理器上,Linux系统把内核重新映射在3GB(即0XC0000000)以上的虚拟地址空间(3GB~4GB)-内核空间;每一个应用程序或者进程都通过MMU建立独立寻址空间-用户空间(0~3GB)。不能直接通过指针,把用户空间的数据地址传递给内核(因为MMU映射的地址根本不一样)。要想在应用程序和驱动程序之间传递数据,需要通过转换,把用户态“看到”的空间地址转换成内核态可访问的地址(get_user()、put_user()、copy_from_user()、copy_to_user()等) ,他们自己负责访问权限的检查,使用时不用考虑。
从用户态到内核态的切换通常通过软件中断实现。 - 驱动程序:
驱动程序作为系统内核的一部分,他工作在内核态。
- 设备文件和设备文件系统:
Linux设备文件简介:
http://lamp.linux.gov.cn/Linux/device_files.html
设备文件
在linux中,字符设备和块设备是通过文件节点访问的(open()、read()、write()等)。在linux的文件系统中,可以找到(或者使用mknod创建)设备对应的文件名--设备文件。对于设备文件的输入/输出操作,系统会调用对应的设备驱动程序。
习惯上,这些设备文件存放在系统的/dev目录下面。但是,系统并不是靠路径去关联设备文件和对应的驱动程序的,因此你可以通过mknod命令将设备文件创建在任意的其他位置。不过一般不这样做。
既然系统不是靠路径去关联设备文件和对应的驱动程序的,那么它是靠什么来对应的呢?
Linux系统是靠主次设备号来联系驱动程序和设备文件节点的,而不是设备文件的路径。
主设备号:系统依靠主设备号识别不同的驱动程序,每一个驱动程序都对应了一个主设备号。因此,在同一个系统中,一类设备的(当前)主设备号是唯一的。
次设备号:同一个驱动程序可以管理多个设备,他们依靠次设备号来区分。次设备号只在驱动程序的内部使用,系统内核直接把次设备号传递给驱动程序,由驱动程序去管理。(参看内核源码)
附:
1)cat /proc/devices 可以列出系统内核支持的设备驱动程序和对应的主设备号;
2)内核源码中,Documentation/devices.txt文件定义了常用的linux驱动程序的主设备号。
设备文件系统
1)主设备号的获取:
主设备号的范围是1~255,因此为每一个设备驱动程序分配唯一的主设备号是不现实的。因此Linux内核允许使用动态分配主设备号的方式,即在创建设备时,由系统自动分配一个未使用的设备号给驱动程序。事实上,在Linux2.4和2.6内核中的大多数驱动程序都采用这种动态分配方式。
但是,动态设备号又带来了新的问题:如何去对应设备文件和驱动程序之间的关系?
动态分配设备号的缺点::由于分配的主设备号不能保证总是一样的,无法事先创建设备节点。
但是这并不是什么问题,因为一旦分配了设备号,我们就可以从/proc/devices 读到。
最初的解决方法:使用脚本语言来处理。利用脚本读取/proc/devices,得到注册在系统中的各种驱动程序及其对应的主设备号,根据/proc/devices中的对应关系,动态的创建(或者删除)设备文件。这种方法是很麻烦的。
其实后来,内核完全可以自己管理设备文件,只不过是通过设备文件系统来实现的。
2)设备文件系统:
通常,系统启动时会把设备文件系统挂载在/dev目录下面。有了设备文件系统以后,Linux设备文件的创建、删除和目录层次等都由各设备驱动程序管理,再也不用手工创建设备文件节点了,再也不需要mknod时查找对应的主设备号了,也不用依靠复杂的脚本来管理设备文件了。新添加(或者删除)一个设备,如插入一个U盘,系统就会自动的在/dev目录中创建(或删除)对应的设备文件节点。
设备文件路径的改变:
在没有使用设备文件系统之前,比如桢缓冲设备是/dev/fb0,使用了设备文件系统以后,他的路径对应变为/dev/fb/0.
3)使用设备文件系统的条件:
*使用设备文件系统需要驱动程序的支持。事实上,在Linux2.4和2.6内核中,几乎所有的驱动程序都支持设备文件系统。如果一个驱动程序的代码没有对设备文件系统的支持,则宁愿修改这个驱动程序代码(事实上,这种修改是很简单的),而不会去掉内核对设备文件系统的支持。
*在配置内核时,要选中对设备文件系统的支持(/dev file system support)。这样,系统驱动程序通过CONFIG_DEVFS_FS宏定义判断系统是否对备文件系统提供支持。
- 版本变迁:
设备文件系统(devfs)在Linux2.6早期内核(Linux2.6.15以前的版本)中被标记为舍弃的特性,在Linux2.6.15及以后的版本则取消了对他的支持。linux2.6内核引入了系统文件系统(sysfs)为每个系统的硬件树进行分级管理,他提供了设备的基本信息。同时,使用udev脚本,这是一种linux2.6内核采用的/dev目录管理系统。他在用户态动态地维护/dev目录下的设备文件。
参考文章:
http://blog.chinaunix.net/u2/70445/showart_720105.html
阅读(1475) | 评论(0) | 转发(0) |