字符设备基础
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Linux 字符设备基础
字符设备驱动程序在系统中的位置
操作系统内核需要访问两类主要设备,简单的字符设备,如打印机,键盘等;块设备,如软盘、硬盘等。与此对应,有两类设备驱动程序。分别称为字符设备驱动程序和块设备驱动程序。两者的主要差异是:与字符设备有关的系统调用几乎直接和驱动程序的内部功能结合在一起。而读写块设备则主要和快速缓冲存储区打交道。只有需要完成实际的输入/输出时,才用到块设备驱动程序。见下图:
Linux 设备驱动程序的主要功能有:
● 对设备进行初始化;
● 使设备投入运行和退出服务;
● 从设备接收数据并将它们送到内核;
● 将数据从内核送到设备;
●
检测和处理设备出现的错误。
当引导系统时,内核调用每一个驱动程序的初始化函数。它的任务之一是将这一设备驱动程序使用的主设备号通知内核。同时,初始化函数还将驱动程序中的函数地址结构的指针送给内核。
内核中有两X 表。一X 表用于字符设备驱动程序,另一X 用于块设备驱动程序。这两X 表用来保存指向file_operations 结构的指针,
设备驱动程序内部的函数地址就保
存在这一结构中。内核用主设备号作为索引访问file_operations结构,因而能访问驱动程序内的子程序。
从开机到驱动程序的载入
系统启动过程中可能出现几种不同的方式检测设备硬件。首先机器硬件启动时BIOS会检测一部分必要的设备,如内存、显示器、键盘和硬盘等等。机器会把检测到的信息存放在特定的位置,如CMOS数据区。而另外某些设备会由设备驱动程序进行检测。
1 开机
2 引导部分(linux/config.h,arch/i386/boot/bootsect.S)
3 实模式下的系统初始化(arch/i386/boot/setup.S)
4 保护模式下的核心初始化
5 启动核心(init/main.c)
init函数中函数调用关系如下:
main.c init()
filesystems.c sys_setup()
genhd.c device_setup()
mem.c chr_dev_init()
至此,驱动程序驻入内存。
设备驱动程序基本数据结构:
struct device_struct
系统启动过程中要登记的块设备和字符设备管理表的定义在文件fs/devices.c中:struct device_struct {
const char * name;
struct file_operations * fops;
};
static struct device_struct chrdevs[MAX_CHRDEV];
static struct device_struct blkdevs[MAX_BLKDEV];
其实块设备表和字符设备表使用了相同的数据结构。在某些系统中,这些设备表也称作设备开关表,不同的是它们直接定义了一组函数指针进行对设备的管理。而这里系统用文件操作(file_operations)代替了那组开关。文件操作是文件系统与设备驱动程序之间的接口,系统特殊文件在建立的时候并没有把两者对应起来,只是把设备的缺省文件结构和i节点结构赋给设备文件,而真正的对应定义在系统启动之后,当设备被打开时时才进行的。
操作blkdev_open和chrdev_open定义在文件devices.c中,它们的基本功能是当设备文件初次打开时,根据该文件的i节点信息找到设备真正的文件操作接口,然后更新原来的设
备表项;最后再调用该设备的open操作。
/include/linux/major.h中定义了设备表的长度。设备表中不同的表项表示不同种类的设备,也就是说,LINUX系统分别支持各128种不同的块设备和字符设备。
Struct file_operations
操作系统将一个字符设备当作文件来处理,内核通过file_operations结构来访问driver 的功能。这也是linux的OO思想的体现之一。file_operations的定义在文件
(.随着linux内核的不断升级,file_operatioins结构也不断变大。最新的版本中,甚至函数原型也发生了一些变化。当然,新版本总会向下兼容的。)
下面是2.0.35中的file_operations结构定义:
struct file_operations {
int (*lseek) (struct inode *, struct file *, off_t, int);
int (*read) (struct inode *, struct file *, char *, int);
int (*write) (struct inode *, struct file *, const char *, int);
int (*readdir) (struct inode *, struct file *, void *, filldir_t);
int (*select) (struct inode *, struct file *, int, select_table *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
int (*mmap) (struct inode *, struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
void (*release) (struct inode *, struct file *);
int (*fsync) (struct inode *, struct file *);
int (*fasync) (struct inode *, struct file *, int);
int (*check_media_change) (kdev_t dev);
int (*revalidate) (kdev_t dev);
};
Struct inode
file_operations中的大多数操作都将inode做为第一个参数。Linux的VFS是对物理文件系统,物理设备的一个封装。Inode结构就是VFS与下层模块对话的重要结构。文件系统由子目录和文件构成。每个子目录或文件只能由唯一的inode描述。每个设备也是用inode来描述的。inode 是LINUX管理文件系统的最基本单位,也是文件系统连接任何子目录、任何文件,设备的桥梁。
struct inode {
kdev_t i_dev; /* 文件所在设备的设备号,第一个IDE硬盘为0x0301 */ unsigned long i_ino; /* 外存inode的节点号,
(i_dev,i_ino)在VFS中是唯一的 */