关于块缓存和页缓存的问题

发布于 2022-07-22 17:09:04 字数 655 浏览 16 评论 9

(块设备文件的address space,   文件中页序号)是可以导出
(address space->host->i_rdev ,页序号<<3+i)       i=0,1,2,....7
从而对应上buffer_head的设备和块号,事实上也是这么做的。sys_read,读一个块设备文件中一页的话,虽然只是把这页加入了页缓存,但是当以后getblk的时候,除了搜索块缓存,在块缓存中没找到的话,还要将块扩展到一页,搜索块设备文件的页缓存,搜索成功后还要把块设备的页缓存所关联的buffer_head加入到块缓存hash表。

但是,普通文件的话,就有问题,sys_read普通文件中的一页,搜索页缓存,找到或者分配一页,分配的话还要把这页加入了页缓存,并读取文件上互相分离的8个块。这8个块有可能在块缓存中有对应的块缓存,也可能没有。即使读取完成后,这8个buffer_head对应了磁盘上的8个块,他们也不会加入到块缓存中。

我想知道如果我使用sys_read读取普通文件的一页,这时块缓存中有这页的一个块,而且这个块标记为Uptodata|Dirty。那么我读普通文件时,并没有进行过块缓存的搜索,而是只进行页缓存的搜索,搜索失败就直接分配空页,从磁盘上读取,这样就导致了磁盘上的一块在内存中有两个副本,而且这两个副本并不一致

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

何其悲哀 2022-07-25 05:26:11

to LZ,

也就是说普通文件(内容)没有buffer_head, 那你一开始帖子中说的问题还存在吗

三生路 2022-07-25 05:25:05

原帖由 思一克 于 2006-7-18 14:38 发表
getblk()用的buffer_head 读普通文件[内容----注意这里说的是内容]时被使用吗

读普通文件时好象不使用getblk(),getblk()只在读诸如块组描述符,inode本身的内容,超级块,inode位图什么的时使用。

读普通文件只搜索页缓存,不行就分配空页和buffer_head,然后直接submit()这几个buffer_head

神回复 2022-07-25 05:20:29

getblk()用的buffer_head 读普通文件[内容----注意这里说的是内容]时被使用吗

放赐 2022-07-25 05:11:16

我查看了2.4.32的代码,和你说的似乎一样(在下是初学者).

其实在2.4.0的框架上做稍许修改是能够解决这个问题的。但是2.4.32硬是把设备文件和普通文件的操作统一化,反而使问题朝着反向发展。不知我的理解是否太片面,望高手指点。

其实仔细想一想,还有一个特殊的情况,那就是环回设备。
mount -o loop -t ext2 initrd-2.4.7-10smp.img mnt
一个普通文件,也可以被安装为一个文件系统。
如果再对文件initrd-2.4.7-10smp.img进行读写操作,那么就和访问mnt/下面的普通文件会产生页缓冲内容的不一致/冲突。

这个(类)问题似乎无法解决。唯一的解决办法只能是互斥。

記憶穿過時間隧道 2022-07-25 04:28:31

我这里只有2.4.32,和你的2.4.0看来不一样,我这里是这样的。
struct file_operations def_blk_fops = {
        open:                blkdev_open,
        release:        blkdev_close,
        llseek:                block_llseek,
        read:                generic_file_read,
        write:                generic_file_write,
        mmap:                generic_file_mmap,
        fsync:                block_fsync,
        ioctl:                blkdev_ioctl,
};

而且普通文件和块设备文件的inode->i_mapping->a_ops->readpage分别为

static int ext2_readpage(struct file *file, struct page *page)
{
        return block_read_full_page(page,ext2_get_block);
}

static int blkdev_readpage(struct file * file, struct page * page)
{
        return block_read_full_page(page, blkdev_get_block);
}

区别只在于ext2_get_block和blkdev_get_block
这两个都是个函数,主要的功能就是对于给定的(inode, 文件的逻辑块号),转换为(设备号,磁盘的物理块号)。我们暂时假定磁盘没有分区,没有引导块,buffer_head->block就是物理块号。

对于blkdev_readpage,它的参数实际只使用了 struct page * page。
调用时,这个page已经设置了正确的(块设备文件inode->i_mapping , index),加入了hash,lru,address space->clean,既已经加入了页缓存。

而block_read_full_page只保证的是:page有与其相关的8个buffer_head(假设一页8块),还没有buffer_head就分配,而且这8个buffer_head->b_this_page要正确的设置;

但是并不保证这8个buffer_head链入了块缓存的hash表,不会把他们加入块缓存。既这8个buffer_head所对应的页只存在于页缓存中。
但是,如果这个页是个读块设备文件得来的页,虽然目前它只在于页缓存,但是变化发生在读取目标块的时候, getblk()------->grow_buffers()函数中。

一旦搜索块缓存没找到目标块,那么就会grow_buffers,而grow_buffers调用grow_dev_page,这个grow_dev_page捣鬼,它把块号换算成相应页号,按照 (块设备的inode->i_mapping,  块号>>3)搜索了页缓存,如果在页缓存中找到了目标页,那么就把目标页的8个buffer_head加入了块缓存的hash表,既把这个缓存页的内容分成8块,并把他们加入了块缓存。

所以,从块设备inode读上来的缓存页,基本可以肯定,与它相关联的8个buffer_head,迟早会被加入到块   hash表中。因此,属于块设备inode的缓存页,这些缓存页,就是块缓存。

块设备inode的页缓存,与设备层的块缓存达到了统一,不会引发不一致的问题。
但是普通文件完全不同,普通文件inode的缓存页永远不会对应上块缓存。而且磁盘上的同一页甚至可能会有(普通文件inode->i_mapping  ,  index) 和 (块设备inode->i_mapping , index)两个唯一标识这个页的值,既可能会在页缓存中有两个副本???????????

虽然这绝对不可能。

[ 本帖最后由 motalelf 于 2006-7-17 17:21 编辑 ]

淡紫姑娘! 2022-07-25 02:04:39

不知道你是否注意到,设备文件的读写和普通文件的读写是有区别的。

设备文件的读写是通过block_read(是以块为单位,直接搜索块缓存),普通文件的读写是通过generic_file_read(是以页为单位).

普通文件可以mmap,例如ext2文件系统,inode->i_mapping->a_ops = &ext2_aops(ext2_read_inode中),
而设备文件不可以mmap.因为inode->i_mapping->a_ops=&empty_aops;(clean_inode中,所有的函数全为空),inode->i_fop = &def_blk_fops;(ext2_read_inode中,其中mmmap函数指针为空).

所以你的表述中,诸如“块设备文件的页缓存所占用的内存”之类的话,是值得商榷的。

当然我认为你提的问题很好,需要解决,也给出我的解决办法(v2.4.0)。

↙厌世 2022-07-24 13:27:14

简单的说,之所以可以用( address space ,  index)来唯一标识页缓存中的一页,是因为文件系统的原则:任何一个inode所管辖的文件的块,不能与其他inode所管辖的块有所重叠。因此( address space ,  index)才变的唯一。

但是块设备文件违反了这个原则,块设备文件inode与其他任何一个普通文件inode所管辖的块都有重叠,Linux怎么解决的呢?

我QQ:248162486

[ 本帖最后由 motalelf 于 2006-7-17 12:54 编辑 ]

眼角的笑意。 2022-07-24 13:01:10

并不是楼上所说的那样

块缓存所占的内存空间,应该说是页缓存的真子集。这个真子集在大部分情况下,就是块设备文件的页缓存所占用的内存。

不管是ext2_readpage还是blkdev_readpage,都只是以(inode->i_mapping,  index)为关键字,搜索了页缓存。搜索不到就进行读,直接读一页。并没有搜索块缓存的过程。

但是读的如果是块设备文件的一页,问题不大,搜索页缓存的过程,就等价于搜索块缓存的过程。。
但是读的如果是普通文件的一页的话,麻烦就来了,普通文件的页所关联的buffer_head,永远不会加入到块缓存的hash表中,无论任何时候。

怀中猫帐中妖 2022-07-23 11:07:02

这个问题非常好.我还没有发现v2.4.0解决了这个问题.

我想这个问题的大致解决办法是:把页缓存建立在块缓存之上.
1.对设备文件读写就直接在块缓存中寻找,找不到再把bh加入块缓存,并从设备上读入该块.
2.对普通文件进行内存映射或读写,先在页内存中查找,找不到就新建一新页,并加入页缓存,然后读入该页.
通常情况一页(4k)分成4块(1k),一块一个bh,这些块的内容是顺序的,但是块号通常是不连续的.对于每个块,在块缓存中查找,找到的话就把该块数据复制进新页对应位置,然后从块缓存释放旧bh,加入新bh;如果找不到,就把新bh加入块缓存,并从设备读入.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文