一般来说,字符型设备驱动程序能够提供如
下几个入口点:
(1) open入口点。打开设备准备I/O操作。对字符特别设备文件进行打开操
作,都会调用设备的open入口点。open子程序必须对将要进行的I/O
操作做好必要的准备工作,如清除缓冲区等。如果设备是独占的,即同一
时刻只能有一个程序访问此设备,则open子程序必须设置一些标志以表
示设备处于忙状态。
(2) close入口点。关闭一个设备。当最后一次使用设备终结后,调用close
子程序。独占设备必须标记设备可再次使用。
(3) read入口点。从设备上读数据。对于有缓冲区的I/O操作,一般是从缓
冲区里读数据。对字符特别设备文件进行读操作将调用read子程序。
(4) write入口点。往设备上写数据。对于有缓冲区的I/O操作,一般是把数
据写入缓冲区里。对字符特别设备文件进行写操作将调用write子程序。
(5) ioctl入口点。执行读、写之外的操作。
(6) select入口点。检查设备,看数据是否可读或设备是否可用于写数据。
select系统调用在检查与设备特别文件相关的文件描述符时使用select入口点。
如果设备驱动程序没有提供上述入口点中的某一个,系统会用缺省的子程序
来代替。对于不同的系统,也还有一些其它的入口点。
在这里主要分析一下在用户空间使用open打开一个设备文件进行操作最后它又是如何关联到对应设备驱动程序上的。对于内核而言,所有打开文件都由文件描述符引用。文件描述符是一个非负整数。当打开一个现存文件或创建一个新文件时,内核向进程返回一个文件描述符。当读、写一个文件时,用open或creat返回的文件描述符标识该文件,将其作为参数传送给read或write。在POSIX.1应用程序中,文件描述符为常数0、1和2分别代表STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO,意即标准输入,标准输出和标准出错输出,这些常数都定义在头文件;中。文件描述符的范围是0~OPEN_MAX,在目前常用的linux系统中,是32位整形所能表示的整数,即65535,64位机上则更多。
系统中每个打开的文件在内核空间都有一个关联的struct file,它由内核在打开文件时创建,并传递给在文件上操作的任何函数,在文件所有实例都关闭后,内核释放这个数据结构,它提供关于被打开的文件的信息。struct file主要用于与文件系统对应的设备驱动程序使用。当然,其它设备驱动程序也可以使用它。此结构定义为:
#include
struct file {
mode_t f_mode;
dev_t f_rdev; /* needed for /dev/tty */
off_t f_pos; /* Curr. posn in file */
unsigned short f_flags; /* The flags arg passed to open */
unsigned short f_count; /* Number of opens on this file */
unsigned short f_reada;
struct inode *f_inode; /* pointer to the inode struct */
struct file_operations *f_op;/* pointer to the fops struct*/
};
其中,struct inode提供了关于特别设备文件/dev/driver(假设此设备名
为driver)的信息,它的定义为:
#include
struct inode {
dev_t i_dev;
unsigned long i_ino; /* Inode number */
umode_t i_mode; /* Mode of the file */
nlink_t i_nlink;
uid_t i_uid;
gid_t i_gid;
dev_t i_rdev; /* Device major and minor numbers*/
off_t i_size;
time_t i_atime;
time_t i_mtime;
time_t i_ctime;
unsigned long i_blksize;
unsigned long i_blocks;
struct inode_operations * i_op;
struct super_block * i_sb;
struct wait_queue * i_wait;
struct file_lock * i_flock;
struct vm_area_struct * i_mmap;
struct inode * i_next, * i_prev;
struct inode * i_hash_next, * i_hash_prev;
struct inode * i_bound_to, * i_bound_by;
unsigned short i_count;
unsigned short i_flags; /* Mount flags (see fs.h) */
unsigned char i_lock;
unsigned char i_dirt;
unsigned char i_pipe;
unsigned char i_mount;
unsigned char i_seek;
unsigned char i_update;
union {
struct pipe_inode_info pipe_i;
struct minix_inode_info minix_i;
struct ext_inode_info ext_i;
struct msdos_inode_info msdos_i;
struct iso_inode_info isofs_i;
struct nfs_inode_info nfs_i;
} u;
};
#include
struct file_operations {
int (*lseek)(struct inode *inode,struct file *filp,
off_t off,int pos);
int (*read)(struct inode *inode,struct file *filp,
char *buf, int count);
int (*write)(struct inode *inode,struct file *filp,
char *buf,int count);
int (*readdir)(struct inode *inode,struct file *filp,
struct dirent *dirent,int count);
int (*select)(struct inode *inode,struct file *filp,
int sel_type,select_table *wait);
int (*ioctl) (struct inode *inode,struct file *filp,
unsigned int cmd,unsigned int arg);
int (*mmap) (void);
int (*open) (struct inode *inode, struct file *filp);
void (*release) (struct inode *inode, struct file *filp);
int (*fsync) (struct inode *inode, struct file *filp);
};
在结构file_operations里,指出了设备驱动程序所提供的入口点位置,分
别是:
(1) lseek,移动文件指针的位置,显然只能用于可以随机存取的设备。
(2) read,进行读操作,参数buf为存放读取结果的缓冲区,count为所要
读取的数据长度。返回值为负表示读取操作发生错误,否则返回实际读取
的字节数。对于字符型,要求读取的字节数和返回的实际读取字节数都必
须是inode->i_blksize的的倍数。
(3) write,进行写操作,与read类似。
(4) readdir,取得下一个目录入口点,只有与文件系统相关的设备驱动程序
才使用。
(5) selec,进行选择操作,如果驱动程序没有提供select入口,select操
作将会认为设备已经准备好进行任何的I/O操作。
(6) ioctl,进行读、写以外的其它操作,参数cmd为自定义的的命令。
(7) mmap,用于把设备的内容映射到地址空间,一般只有块设备驱动程序使
用。
(8) open,打开设备准备进行I/O操作。返回0表示打开成功,返回负数表
示失败。如果驱动程序没有提供open入口,则只要/dev/driver文件存
在就认为打开成功。
(9) release,即close操作。
设备驱动程序所提供的入口点,在设备驱动程序初始化的时候向系统进行登
记,以便系统在适当的时候调用。LINUX系统里,通过调用register_chrdev
向系统注册字符型设备驱动程序。register_chrdev定义为:
#include
#include
int register_chrdev(unsigned int major, const char *name,
struct file_operations *fops);
其中,major是为设备驱动程序向系统申请的主设备号,如果为0则系统为此
驱动程序动态地分配一个主设备号。name是设备名。fops就是前面所说的对各个
调用的入口点的说明。此函数返回0表示成功。返回-EINVAL表示申请的主设备号
非法,一般来说是主设备号大于系统所允许的最大设备号。返回-EBUSY表示所申
请的主设备号正在被其它设备驱动程序使用。如果是动态分配主设备号成功,此
函数将返回所分配的主设备号。如果register_chrdev操作成功,设备名就会出
现在/proc/devices文件里。
初始化部分一般还负责给设备驱动程序申请系统资源,包括内存、中断、时
钟、I/O端口等,这些资源也可以在open子程序或别的地方申请。在这些资源不
用的时候,应该释放它们,以利于资源的共享。
阅读(538) | 评论(0) | 转发(0) |