📄 034_block_dev_c.html
字号:
/* used to insert page numbers */ div.google_header::before, div.google_footer::before { position: absolute; top: 0; } div.google_footer { flow: static(footer); } /* always consider this element at the start of the doc */ div#google_footer { flow: static(footer, start); } span.google_pagenumber { content: counter(page); } span.google_pagecount { content: counter(pages); } } @page { @top { content: flow(header); } @bottom { content: flow(footer); } } /* end default print css */ /* custom css *//* end custom css */ /* ui edited css */ body { font-family: Verdana; font-size: 10.0pt; line-height: normal; background-color: #ffffff; } .documentBG { background-color: #ffffff; } /* end ui edited css */</style> </head> <body revision="dcbsxfpf_7c8gx6m:181"> <div align=center>
<table align=center border=0 cellpadding=0 cellspacing=0 height=5716 width=802>
<tbody>
<tr>
<td height=5716 valign=top width=802>
<pre>2007-3-6 <br> 参考资料推荐: 当然是《Linux Device Dri vers 2nd》for 2.4.0 版本的。<br><a href=http://www.xml.com/ldd/chapter/book/ch12.html title="Linux Driver 2nd chaper 12 : load block driver"> Linux Driver 2nd chaper 12 : load block driver</a><br><br> 先读一读chaper12, load block driver, 对于理解这个文件相当的有好处。这里仅罗列些block driver 如何与kernel配合的接口和数据结构。<br>block driver需要先注册自己到<b>blkdevs,</b> 就是这个文件实现的东西,这样kernel可以将此设备以设备文件的方式暴露给app。为了支持数据读写<br>driver必须实现自己的request_queue, 并注册到<b>blk_dev</b>, 这个queue和其操作函数是内核读写块设备的接口。 <br> 如果块设备支持分区,driver驱动还要分配并初始化一个<b>gen_disk, </b>并用register_disk 进行注册(分区探测)。下图是对LDD Chapter12的一<br>点简单的总结。<br> <br> <br> blk_size[][]; ;2M disk size<br> blksize_size[][]; ;1k (logic block) <br> hardsect_size[][];;512bytes <br> call read_ahead[]; ;2 sectors <br>kernel call | max_readahead[][]; <br> register_blkdev(major,*name, | blk_init_queue | max_sectors[][]; <br> block_device_operations) | (request_queue_t, request_fn) | max_segments[]; <br> unregister_blkdev(major,*name) | blk_cleanup_queue | ----------------------------------------- <br> /.\ | (request_queue_t *queue) \ / register_disk(); <br> | \ / | + /.\ <br> | + | / | <br> | sbull_bdops = { call / | <br>driver call open: sbull_open, ' | <br> release:sbull_release, / call <br> ioctl: sbull_ioctl, / <br> check_media_change: sbull_check_change, / <br> revalidate: sbull_revalidate, ' <br> }; / <br> + / <br> / \ / <br> | / <br> <font color=#006600><b>blkdevs</b></font>[] [<font color=#ff0000>this file</font>] | / <br> +-----------+ | / <br> | name+bdops|--------\ / <br> +-----------+ / <br> | | | <br> +-----------+ / <br> | | / <br> +-----------+ / <br> / <br> / <br> +-----------+ ` <br> | | / <br> +-----------+ / <br> +-----------------+ <br> <font color=#006600><b>blk_dev</b></font>[] (ll_rw_blk.c) ,.| request_queue_t | sbull_request <br> +-----------+ _.-`` | queue_proc | <br> |queue ops |-'` | void *data; | <br> +-----------+ +-----------------+ <br> | | <br> +-----------+ <br> | | <br> +-----------+ <br> <br> _,,,,...----...,,,,_ <br> +-----------+ _.-'`` ``'-., <br> | | .` `. <br> +-----------+ | pd | <br> ', /dev/pda through /dev/pdd ,- <br> <font color=#006600><b>gendisk_head</b></font>->+----------+ `,,-,, _,.-'` <br> | major _,.-'`` ```'''''-----''''``` <br> | name--`` | +----------+ <br> | max_p | |start_sect| <br> | *hd_struct-------|nr_sects | <br> | *sizes | |devfs de | <br> | ... | +----------+ <br> | | |start_sect| <br> | | |nr_sects | <br> +----------+ |devfs de | <br> +----------+ <br> |start_sect| <br> |nr_sects | <br> |devfs de | <br> +----------+ <br></pre>
<pre> <br> 了解了块设备驱动,再回过头来看block_dev.c就容易些。此模块提供了几个功能<br>1)为块设备建立设备文件,文件系统接口<br>struct file_operations def_blk_fops = {<br> open: <font color=#006600><b>blkdev_open</b></font>,<br> release: <font color=#006600><b>blkdev_close</b></font>,<br> llseek: <font color=#006600><b>block_llseek</b></font>,<br> read: <font color=#006600><b>block_read</b></font>,<br> write: <font color=#006600><b>block_write</b></font>,<br> fsync: <font color=#006600><b>block_fsync</b></font>,<br> ioctl: <font color=#006600><b>blkdev_ioctl</b></font>,<br>};<br>2)管理blkdevs<br>int <font color=#006600><b> unregister_blkdev</b></font>(unsigned int major, const char * name)<br>int <font color=#006600><b> register_blkdev</b></font>(unsigned int major, const char * name, struct block_device_operations *bdops)<br><br><font color=#006600><br><b> <br></b></font>3)管理<font color=#006600>block_device</font><br>struct <font color=#006600>block_device</font> *<font color=#006600><b>bdget</b></font>(dev_t dev); <b>分配 </b>struct <font color=#006600>block_device</font> <br>void <font color=#006600><b>bdput</b></font>(struct block_device *bdev) <b>释放 </b>struct <font color=#006600>block_device</font><br><br>int <font color=#006600><b>blkdev_get</b></font>(struct block_device *bdev, mode_t mode, unsigned flags, int kind) <b>调用blkdevs的bd_ops->open</b><br>int <font color=#006600><b>blkdev_put</b></font>(struct block_device *bdev, int kind) <b>调用blkdevs的bd_ops->release</b><br><br>int<font color=#006600> ioctl_by_bdev</font>(struct block_device *bdev, unsigned cmd, unsigned long arg) :block_device->bd_op->ioctl<br>void __init <font color=#006600>bdev_init</font>(void)<br> struct block_device {<br> struct list_head <font color=#006600><b>bd_hash</b></font>;<br> atomic_t <font color=#006600><b>bd_count</b></font>;<br>/* struct address_space <font color=#006600><b>bd_data</b></font>; */<br> dev_t <font color=#006600><b>bd_dev</b></font>; /* not a kdev_t - it's a search key */<br> atomic_t <font color=#006600><b>bd_openers</b></font>;<br> const struct block_device_operations *<font color=#006600><b>bd_op</b></font>;<br> struct semaphore <font color=#006600><b>bd_sem</b></font>; /* open/close mutex */<br>};<br><br><br>4)block dev 的其他杂项操作 <br> int <font color=#006600>check_disk_change</font>(kdev_t dev)<br> const struct <font color=#000000>block_device_operations</font> * <font color=#006600>get_blkfops</font>(unsigned int major)<br>5) internal function & misc<br> <font color=#009900><b>init_once</b></font>: for mem cache obj init <br> <font color=#009900><b>hash</b></font>: for bdget<br> <font color=#006600><font color=#000000>static struct block_device</font> *bdfind(dev_t dev, struct list_head *head) </font>: for bdget<br> int <font color=#006600>get_blkdev_list</font>(char * p) :一个块设备的列表 <br> const char * <font color=#009900>bdevname</font>(kdev_t dev)<br><br>这个文件出现了几个有关block device的结构,并且名字也类似,真是够乱的:<br>第一类是把块设备虚拟成文件的接口函数:struct <b>file_operations def_blk_fops</b>。<br>第二类是块设备的操作和名字:数组 <b>blkdevs </b>struct block_device_operations *bdops;<br>还有一类是block_device 的操作函数:也包含了 block_device_operations *bd_op,呵呵有点意思。这个block_device的作用是嘛?搜索一下bdget可以看出些<br>端倪:devfs_read_inode 和 init_special_inode 把结构block_device 注册到inode 中。这样看来这个结构是block dev到普通文件系统的一个桥梁。devfs和<br>普通文件系统中的dev文件就记录这个东西。而def_blk_fops通过block_device成为打开的设备文件的操作函数,够绕!看看init_special_inodevoid就明白了。<br>init_special_inode(struct inode *inode, umode_t mode, int rdev)<br>{<br> inode->i_mode = mode;<br> if (S_ISCHR(mode)) {<br> inode->i_fop = &def_chr_fops;<br> inode->i_rdev = to_kdev_t(rdev);<br> } else if (S_ISBLK(mode)) {<br> inode->i_fop = &def_blk_fops;<br> inode->i_rdev = to_kdev_t(rdev);<br> inode->i_bdev = bdget(rdev);<br> } else if (S_ISFIFO(mode))<br> inode->i_fop = &def_fifo_fops;<br> else if (S_ISSOCK(mode))<br> inode->i_fop = &bad_sock_fops;<br> else<br> printk(KERN_DEBUG "init_special_inode: bogus imode (%o)\n", mode);<br>}<br><br>而blkdev_get 和 blkdev_put 则绕过了文件系统本身,通过虚拟一个文件来带开和释放一个块设备。比较典型的应用是get_sb_bdev和mount_root:在这些<br>情景下通过open打开一个块设备是很不对头的:这些情景下不对这个块设备进行read 和open操作,而是通过具体的文件系统如 ext2 再通过ll_rw_block来<br>读写块设备。(还是绕)<br><br> 当直接打开一个块设备(比如 dd)时是通过这个文件提供的def_blk_fops的几个接口把块设备虚拟成一个无结构的扇区序列来进行操作。(参考<br> init_special_inode)。<br><br><br> 至于这个文件的具体函数,则不是那么难以理解。值得一读以前分析的file_map.c 这个文件,回顾一下buffer cache 和page cache 的区别与<br>联系。<br> ssize_t block_write(struct file * filp, const char * buf,<br> size_t count, loff_t *ppos)<br> ssize_t block_read(struct file * filp, char * buf, size_t count, loff_t *ppos)<br> 这两个函数实现对设备文件的读写,从其调用的分配缓冲区的函数 getblk看过去,就知道,直接dd一个设备的话其缓存于buffer cache。 而以前<br>分析filemap。c 的时候知道,普通文件的内容缓存于page cache,普通文件的元数据缓存于buffer cache。<br> 值得注意的是,先前注册到inode中的i_bdev (block_device),并没有在文件的读写过程中发挥什么作用,去搜索i_bdev就可以理解这个inode中<br>的block_device仅仅是为了open, close这个相关的块设备文件而已。真正有用的是inode->i_rdev,init_special_inode中当然也没有忘记初始<br>化这个变量。<br> 读写块设备文件,主要的工作是把文件的以byte为单位的(start_pos,len)转换为blk size为单位的读写单元,然后一个工作就是缓存,设备<br>文件的读写靠的是buffer cache。 到分析buffer.c的时候再说罢。<br> <br>static loff_t block_llseek(struct file *file, loff_t offset, int origin) 没啥说的。<br><br>static int block_fsync(struct file *filp, struct dentry *dentry, int datasync)<br>{<br> return fsync_dev(dentry->d_inode->i_rdev);<br>}<br>注意到 fsync_dev<br>int fsync_dev(kdev_t dev)<br>{<br> sync_buffers(dev, 0);<br><br> lock_kernel();<br> sync_supers(dev);<br> sync_inodes(dev);<br> DQUOT_SYNC(dev);<br> unlock_kernel();<br><br> return sync_buffers(dev, 1);<br>}<br>注意这是对我们分析的一个印证,和一个设备相关的所有数据缓冲:<br>1)dev相关的buffer cache(以(dev,block)为索引),含有像dd这种应用的缓存:sync_buffers,和普通文件的元数据<br>2)sync_supers(dev); 设备的super block<br>3)sync_inodes(dev); sync设备上所有inode(可能是在不同的sb中)的所有文件数据(包括filemap的数据)。(还包括inode本身,inode也算是文件的元数<br>据,但是inode本身没有在buffer cache中,因为inode不仅仅是一个磁盘上的数据影像,他需要进行转换。见mark_inode_dirty,和inode 的分配释放函数,参考<br>inode.c. sync_supers 道理相同。<br><br><br>剩下的函数真的不用多说了。。。。<br><br>end。<br><br><br><br><br><br><br></pre>
</td>
</tr>
</tbody>
</table>
</div></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -