事实上,我总是对linux开源社区的无名英雄们怀着无限的敬意,因此除了完成工作中需要的功能以外,首先想到的是分享,本篇文章以
GPL发布,在你转发的时候,请遵循GPL协议的规定,在此首先贴出GPL公共许可证,或许你会觉得这过于啰嗦,事实上这是必要的。请谅
解。为了不妨碍大家的阅读,在此我给出GPLv3的连接地址。 (GPLv3)
下面进入正题,我打算分几个步骤来说明:
最后还将给出一个简单的示例程序。
在linux内核中,发生一次写操作,从调用write函数到数据发起一个写数据到具体块设备请求之间,大致需要以下几个过程。
1.如果用户态调用了一个write函数,内核执行blkdev_file_write函数,如果不是direct io操作方式,那么执行buffered write操作
过程,直接调用generic_file_buffered_write函数。Buffered write操作方法会将数据直接写入Cache,并进行Cache的替换操
作,在替换操作过程中需要对实际的快设备进行操作,address_space->a_ops提供了块设备操作的方法。当数据被写入到Cache之
后,write函数就可以返回了,后继异步写入的任务绝大部分交给了pdflush daemon(有一部分在替换的时候做了)。
2.读操作在没有命中Cache的情况下通过address_space_operations方法中的readpage函数发起块设备读请求;写操作在替换
Cache或者Pdflush唤醒时发起块设备请求。发起块设备请求的过程都一样,首先根据需求构建bio结构,bio结构中包含了读写地址、
长度、目的设备、回调函数等信息。构造完bio之后,通过简单的submit_bio函数将请求转发给具体的块设备。从这里可以看出,块设
备接口很简单,接口方法为submit_bio(更底层函数为generic_make_request),数据结构为struct bio。
3.submit_bio函数通过generic_make_request转发bio,generic_make_request是一个循环,其通过每个块设备下注册的
q->make_request_fn函数与块设备进行交互。如果访问的块设备是一个有queue的设备,那么会将系统的__make_request函数
注册到q->make_request_fn中;否则块设备会注册一个私有的方法。在私有的方法中,由于不存在queue队列,所以不会处理具体
的请求,而是通过修改bio中的方法实现bio的转发,在私有make_requ