Linux 块设备 读写,块设备读写流程

块设备与字符设备的区别

1、  从字面上理解,块设备和字符设备最大的区别在于读写数据的基本单元不同。块设备读写数据的基本单元为块,例如磁盘通常为一个sector,而字符设备的基本单元为字节。所以Linux中块设备驱动往往为磁盘设备的驱动,但是由于磁盘设备的IO性能与CPU相比很差,因此,块设备的数据流往往会引入文件系统的Cache机制。

2、 从实现角度来看,Linux为块设备和字符设备提供了两套机制。字符设备实现的比较简单,内核例程和用户态API一一对应,用户层的Read函数直接对应了内核中的Read例程,这种映射关系由字符设备的file_operations维护。块设备接口相对于字符设备复杂,read、write API没有直接到块设备层,而是直接到文件系统层,然后再由文件系统层发起读写请求。

打开设备过程

1.asmlinkage  long  sys_open(const char __user *filename, int flags, int mode)

流程:用户态程序通过open()打开指定的块设备,通过systemcall机制陷入内核.

参数:filename文件名

flags 读写标志

mode  权限模式

2.sys_open-->do_sys_open-->do_filp_open-->nameidata_to_filp-->__dentry_open-->f->f_op->open-->blkdev_open

static int blkdev_open(struct inode * inode, struct file * filp)

流程:执行blkdev_open()函数,该函数注册到文件系统方法(file_operations)中的open上。

参数:inode inode号

filp 内核中文件对象结构

注:

filp_open() :

调用 open_namei()函数取出和该文件相关的dentry和inode(因为前提指明了文件已经存在,所以dentry和inode能够查找到,不用创建),然后调用dentry_open()函数创建新的file对象,并用dentry和inode中的信息初始化file对象(文件当前的读写位置在file对象中保存)。注意到dentry_open()中有一条语句:

f->f_op = fops_get(inode->i_fop);

这个赋值语句把和具体文件系统相关的,操作文件的函数指针集合赋给了 file对象的f _op 变量(这个指针集合是保存在inode对象中的),

3.blkdev_open-->bd_acquire

|

\/

do_open

在blkdev_open函数中调用bd_acquire()函数,bd_acquire函数完成文件系统inode到块设备bdev的转换,具体的转换方法通过hash查找实现。得到具体块设备的bdev之后,调用do_open()函数完成设备打开的操作。

4.do_open-->get_gendisk

|

\/

gendisk->fops->open(bdev->bd_inode, file)

流程:在do_open函数中会调用到块设备驱动注册的open方法,具体调用如下:gendisk->fops->open(bdev->bd_inode, file)。

5.

读设备过程

注:不经过文件系统,直接读写块设备,如果经过文件系统,readpage过程有区别

1.asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t count)

流程:用户程序通过调用read(),通过systemcall机制陷入内核,

参数:fd 文件描述符

buf 缓冲区

count 缓冲区大小

2.sys_read-->vfs_read-->file->f_op->read-->do_generic_file_read-->do_generic_mapping_read-->mapping->a_ops->readpage(filp, page);

注:非direct io方式

流程:内核执行generic_file_read,如果不是direct io方式,那么直接调用do_generic_file_read->do_generic_mapping_read()函数,在do_generic_mapping_read(函数位于filemap.c)函数中,首先查找数据是否命中Cache,如果命中,那么直接将数据返回给用户态;否则通过address_space->a_ops->readpage函数发起一个真实的读请求。

读操作在没有命中Cache的情况下通过address_space_operations方法中的readpage函数发起块设备读请求;写操作在替换Cache或者Pdflush唤醒时发起块设备请求。发起块设备请求的过程都一样,首先根据需求构建bio结构,bio结构中包含了读写地址、长度、目的设备、回调函数等信息。构造完bio之后,通过简单的submit_bio函数将请求转发给具体的块设备。从这里可以看出,块设备接口很简单,接口方法为submit_bio(更底层函数为generic_make_request),数据结构为struct bio。

3.static int blkdev_readpage(struct file * file, struct page * page)

你可能感兴趣的:(Linux,块设备,读写)