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/fs.h>中。
每个字符设备都有一个file_operatioins结构。
这个结构指向一组操作函数(open,read…). 每个函数的定义由driver提供。
当然,有些标准操作某些设备并不支持,这时,file_operatons 结构中对应表项为NULL(.随着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 inodefile_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中是唯一的 */umode_t i_mode;/* 表示文件类型以及存取权限 */nlink_t i_nlink;/* 连接到该文件的link数 */uid_t i_uid; /* 用户标识号 */gid_t i_gid; /* 用户组标识号 */kdev_t i_rdev;/* 根设备的设备号 */off_t i_size;/* 文件长度 */time_t i_atime;/* 文件访问时间 */time_t i_mtime; /* 文件修改时间 */time_t i_ctime; /* 文件创建时间 */unsigned long i_blksize;/* 以字节为单位的块大小,一般为1024字节 */ unsigned long i_blocks; /* 文件块数 */unsigned long i_version;unsigned long i_nrpages;/* 文件在内存中所占页数 */struct semaphore i_sem; /* 信号量 */struct inode_operations *i_op;/* 指向一组针对该文件的操作函数,见fs.h */struct super_block *i_sb; /* 指向内存中VFS的超级块 */struct wait_queue *i_wait; /* 在该文件上的等待队列 */struct file_lock *i_flock;struct vm_area_struct *i_mmap;struct page *i_pages; /* 由文件占用页面构成的单向链,通过它可访问内存中的文件数据 */ struct dquot *i_dquot[MAXQUOTAS];struct inode *i_next, *i_prev; /* inode资源管理中使用的链表指针 */ struct inode *i_hash_next, *i_hash_prev; /* inodecache的链表指针 */ struct inode *i_bound_to, *i_bound_by;struct inode *i_mount; /* 指向下挂文件系统的inode的根目录 */unsigned long i_count; /* 引用记数,0表示是空闲inode */unsigned short i_flags;unsigned short i_writecount;unsigned char i_lock; /* 对inode加锁标志 */unsigned char i_dirt;unsigned char i_pipe;unsigned char i_sock;unsigned char i_seek;unsigned char i_update;unsigned char i_condemned;union { /* 各类文件系统inode的特定信息 */ ............struct ext2_inode_info ext2_i;............} u;};对于设备管理而言,主要用到的是inode结构的kdev I_rdev字段。
Struct kdev_t文件系统中,字符设备是通过名字来访问的。
通常字符设备都在/dev下。
在系统内部,每个字符设备都用设备号来表示。
设备号由主,副设备号来表示。
主设备号表示与设备对应的设备驱动程序。
如:设备/dev/zero 和/dev/null都用1作为主设备号,表示他们使用相同的设备驱动程序。