免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 3274 | 回复: 10
打印 上一主题 下一主题

关于块缓存和页缓存的问题 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2006-07-15 15:20 |只看该作者 |倒序浏览
(块设备文件的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。那么我读普通文件时,并没有进行过块缓存的搜索,而是只进行页缓存的搜索,搜索失败就直接分配空页,从磁盘上读取,这样就导致了磁盘上的一块在内存中有两个副本,而且这两个副本并不一致

论坛徽章:
0
2 [报告]
发表于 2006-07-16 16:59 |只看该作者
这个问题非常好.我还没有发现v2.4.0解决了这个问题.

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

论坛徽章:
0
3 [报告]
发表于 2006-07-17 12:43 |只看该作者
并不是楼上所说的那样

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

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

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

论坛徽章:
0
4 [报告]
发表于 2006-07-17 12:50 |只看该作者
简单的说,之所以可以用( address space ,  index)来唯一标识页缓存中的一页,是因为文件系统的原则:任何一个inode所管辖的文件的块,不能与其他inode所管辖的块有所重叠。因此( address space ,  index)才变的唯一。

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


我QQ:248162486

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

论坛徽章:
0
5 [报告]
发表于 2006-07-17 14:34 |只看该作者
不知道你是否注意到,设备文件的读写和普通文件的读写是有区别的。

设备文件的读写是通过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)。

论坛徽章:
0
6 [报告]
发表于 2006-07-17 17:17 |只看该作者
我这里只有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 编辑 ]

论坛徽章:
0
7 [报告]
发表于 2006-07-18 11:06 |只看该作者
我查看了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/下面的普通文件会产生页缓冲内容的不一致/冲突。

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

论坛徽章:
0
8 [报告]
发表于 2006-07-18 14:38 |只看该作者
getblk()用的buffer_head 读普通文件[内容----注意这里说的是内容]时被使用吗

论坛徽章:
0
9 [报告]
发表于 2006-07-18 22:16 |只看该作者
原帖由 思一克 于 2006-7-18 14:38 发表
getblk()用的buffer_head 读普通文件[内容----注意这里说的是内容]时被使用吗



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

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

论坛徽章:
0
10 [报告]
发表于 2006-07-19 09:08 |只看该作者
to LZ,

也就是说普通文件(内容)没有buffer_head, 那你一开始帖子中说的问题还存在吗
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP