Chinaunix

标题: 映射的冲突 [打印本页]

作者: qtdszws    时间: 2007-06-19 10:27
标题: 映射的冲突
设备文件和设备上的文件都可以被映射

文件被映射时,都是按照逻辑块顺序被映射到连续的虚拟地址空间和物理页中

而文件在设备上的块是乱序的

如果块大小<物理页大小,那么设备文件和设备上的文件同时被映射时,岂不是无法满足其中的一个映射要求而形成冲突吗?

目前的块大小都是4k,难道是为了避免这个冲突?
作者: zx_wing    时间: 2007-06-19 11:19
原帖由 qtdszws 于 2007-6-19 10:27 发表于 1楼  
设备文件和设备上的文件都可以被映射

文件被映射时,都是按照逻辑块顺序被映射到连续的虚拟地址空间和物理页中

而文件在设备上的块是乱序的

如果块大小<物理页大小,那么设备文件和设备上的文件同时 ...

楼主误解了mmap的实质了。首先在每个inode节点都有一组cache,称为page cache。也就是说你打开一个文件,它会有一个cache,你打开这个文件所在的设备,也会有个cache。mmap的作用就是把cache中的这些页和进程的地址空间关联起来。这样你在读一个文件的时候,mmap会帮你把文件中相应的内容读到cache中来,你就可以像操作内存一样操作文件。写的时候类似,所有更改都写到cache中,在同步到文件中去。所以映射和文件如何存储没有关系。

其次,通过文件和通过存放文件的设备文件来操作磁盘的某个区域实质是一样的。文件和存放文件的设备只是操作系统给你的对同一事物的不同抽象。乱序存放是存储器的概念,通过disk controller提供给操作系统的扇区号都是逻辑上顺序的。不管用哪种方式,你能看到的逻辑号都是顺序的。lz把文件系统和实际存储混淆了。

最后,linux中很少用4k这么大的block,这个在x86上已经是一个block的极限了(不能超过一个页),linux通常默认是1024的block,正好是2个sector的大小。
作者: qtdszws    时间: 2007-06-19 12:19
假如块大小为1k,文件f的开头四个块为a,b,c,d(不连续)
则mmap该文件后A
物理页|-------------------------------------------------|
块    |----a----|----b----|----c-----|-------d--------|

再映射该设备文件的话,必须这样映射B
物理页|-------------------------------------------------|
块    |----a----|----a+1---|---a+2---|----a+3----|    b......c......d
但是这是不可能的
a,b,c,d已经被读入一个完整的页中,如何再映射到B中?
作者: zx_wing    时间: 2007-06-19 12:33
原帖由 qtdszws 于 2007-6-19 12:19 发表于 3楼  
假如块大小为1k,文件f的开头四个块为a,b,c,d(不连续)
则mmap该文件后A
物理页|-------------------------------------------------|
块    |----a----|----b----|----c-----|-------d--------|

再映射该设 ...

请lz注意这句话:
首先在每个inode节点都有一组cache,称为page cache。也就是说你打开一个文件,它会有一个cache,你打开这个文件所在的设备,也会有个cache。mmap的作用就是把cache中的这些页和进程的地址空间关联起来。
所以,你这两个物理页面是不同的,一个属于文件,一个属于设备。
其实对于A的映射中,它只是把文件的内容读出来方到属于文件的物理页面中,并不更改文件的内容。所以对于B进行映射时,它同样可以把文件中的内容读入到自己的物理页面中。两者并不冲突。
作者: qtdszws    时间: 2007-06-19 14:14
在buffer层上,设备上的块是由设备号和块号唯一确定

按照你的解释,不就有了两个同样的buffer_head结构了吗?
作者: zx_wing    时间: 2007-06-19 14:38
原帖由 qtdszws 于 2007-6-19 14:14 发表于 5楼  
在buffer层上,设备上的块是由设备号和块号唯一确定

按照你的解释,不就有了两个同样的buffer_head结构了吗?

首先没有所谓的buffer层,其次buffer_head只是把page cache中的page细分成以block为单位。当你用以文件的方式打开一个文件的时候,如果文件没有hole,则读写是以page为单位的而不是以block为单位的,所以不需要buffer_head。如果你用设备方式读写文件,则如你所说通过buffer_head来管理。不过这和映射没有什么关系。所以两种方式一般是不会有同样的buffer_head结构(除非文件有hole),及时有,也不存在问题,因为这些页面分属于不同的inode。

lz如果想把问题搞清楚,请阅读《understanding linux kernel》的第9章、第15章、第16章。如果仅仅想知道mmap的原理,阅读第16章第2节memory mapping就可以了。

[ 本帖最后由 zx_wing 于 2007-6-19 14:47 编辑 ]
作者: qtdszws    时间: 2007-06-19 14:54
>>则读写是以page为单位的而不是以block为单位的,所以不需要buffer_head。

那么你的这些数据是如何来的呢?是不是也要经过buffer层(buffer_head)从设备读取呢?
作者: zx_wing    时间: 2007-06-19 15:11
原帖由 qtdszws 于 2007-6-19 14:54 发表于 7楼  
>>则读写是以page为单位的而不是以block为单位的,所以不需要buffer_head。

那么你的这些数据是如何来的呢?是不是也要经过buffer层(buffer_head)从设备读取呢?

lz混淆了buffer_head和下层block的关系了。
没有所谓的buffer层,我不知道你这个名字是哪儿看到的。buffer_head属于page cache,是kernel用来管理cache的一种数据结构,一般用于块设备。
最后的读操作最后是要转换成block,进而转换成sector,但这是在generic I/O layer做的事情,它在page cache的下层。
作者: qtdszws    时间: 2007-06-19 17:52
看了一下代码,发现还真可以存在两个一样的buffer_head(dev,block)

映射设备操作
v2.4.32
文件/dev/sda1

1.打开设备
fp=fopen("/dev/sda1","r");
sys_open->...->ext2_read_inode->init_special_inode->
inode->i_fop = &def_blk_fops;
该结构为(block_dev.c中)
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,
};

sys_open->....->blkdev_open->bd_acquire->bdget
inode->i_data.a_ops = &def_blk_aops;
该结构为
struct address_space_operations def_blk_aops = {
        readpage: blkdev_readpage,
        writepage: blkdev_writepage,
        sync_page: block_sync_page,
        prepare_write: blkdev_prepare_write,
        commit_write: blkdev_commit_write,
        direct_IO: blkdev_direct_IO,
};

->bd_acquire
inode->i_mapping = bdev->bd_inode->i_mapping;

2.映射设备
mmap(fp,....)
sys_mmap2->....->generic_file_mmap
vma->vm_ops = &generic_file_vm_ops;
结构为
static struct vm_operations_struct generic_file_vm_ops = {
        nopage:                filemap_nopage,
};

3.对该内存块,发生缺页中断
do_page_fault->handle_mm_fault->handle_pte_fault->do_no_page
new_page = vma->vm_ops->nopage(vma, address & PAGE_MASK, 0);
调用filemap_nopage->page_cache_read
int error = mapping->a_ops->readpage(file, page);
调用blkdev_readpage->block_read_full_page->submit_bh

这样一来,就有可能存在一个块的两份拷贝了,从而可能导致混乱

谢谢zx_wing的帮助
作者: zx_wing    时间: 2007-06-19 18:09
原帖由 qtdszws 于 2007-6-19 17:52 发表于 9楼  
看了一下代码,发现还真可以存在两个一样的buffer_head(dev,block)

映射设备操作
v2.4.32
文件/dev/sda1

1.打开设备
fp=fopen("/dev/sda1","r");
sys_open->...->ext2_rea ...

>>这样一来,就有可能存在一个块的两份拷贝了,从而可能导致混乱
所以这些同步的工作应该由kernel来保证,应该由我们程序员来做。赞lz遇到问题阅读源码的精神!
作者: qtdszws    时间: 2007-06-19 19:19
发现在hash_table中的都是metadata块,而属于文件的数据块和设备文件的块的buffer_head都不进入hash_table

因此出现两份拷贝是有可能的:文件系统出错导致文件交叉等

以前总是认为所有的块都进入, 所有我有buffer层这一说,理解错了.
作者: 塑料袋    时间: 2007-06-20 11:23
确实有bufferCache这一层呀。

在page的hash表中的page既为pagecache,在buffer_head的hash表中的buffer_head既为bufferCache。

但是在2.4.x版本的kernel中,对buffer_head这个结构的处理变化很大。

以下的讨论中,我们假设memory足够大,大到可以把所有磁盘中的所有内容都cache到memory中来,所有释放cache的过程均不起作用。

2.4.0中,
1)  普通文件的inode->I_mapping中,才有page,这些page全部进入page的hash表,构成了pagecache;而块设备文件的inode->I_mapping为空,没有任何page。

2)pagecache中的page全部关联4个buffer_head,这些buffer_head最终将全部进入
buffer_head的lru链,但是决不会进入buffer_head的hash表,既普通文件的page所关联的buffer_head,绝对不属于buffercache;

3) 在page的lru链中,另有一部分page,它们的(mapping,index)均为空,既所谓的匿名页,这些匿名页只在page的lru链,而不在page的hash表。这些匿名页分两部分,一部分匿名页影射到了0~3G上,swap_out最终会将它们变成有名页,进入swapcache,既进入page的hash表,不考虑;另有一部分匿名页不存在0~3G的影射,永远不会变为有名页,永远不会进入pagecache,他们的存在,只是为了关联4个buffer_head,这4个buffer_head,必须全部都在buffer_head的hash表中。既这部分匿名页所关联的buffer_head,构成了bufferCache。

2.4.32中
1)无论普通文件,还是块设备文件,其inode->I_mapping中,均有page,并且这些page均在pagecache中。既2.4.0没有为块设备文件建立page级的缓存,但是2.4.32建立了。
2)pagecache中的page分为两类,一类是普通文件的page,一类是块设备文件的page。所有这些page全部关联4个buffer_head,但是只有块设备文件的page所关联的buffer_head,才能进入buffer_head的hash表;普通文件的page所关联的buffer_head,依然绝对不能进入buffer_head的hash表。

3)2.4.32中,已经没有了真正的匿名页。所有匿名页最终将全部进入swapcache。


说的不太清楚,简而言之,从所占用内存空间的角度来看:
   2.4.0:buffercache与pagecache是两个不同的内存空间的集合。但是这两个内存集合中,有可能每个集合里边,都有一小块内存影射到了同一小块磁盘上。
   2.4.32:buffercache是块设备文件的inode->i_mapping的的子集,而块设备文件的inode->I_mapping又是pagecache的真子集。这样,在pagecache这个最大的集合中,可能有两个不同的元素,影射到了同一小块磁盘。
作者: zx_wing    时间: 2007-06-20 11:36
原帖由 塑料袋 于 2007-6-20 11:23 发表于 12楼  
确实有bufferCache这一层呀。

在page的hash表中的page既为pagecache,在buffer_head的hash表中的buffer_head既为bufferCache。

但是在2.4.x版本的kernel中,对buffer_head这个结构的处理变化很大。

以 ...


在2.4.10之前,buffer cache和page cache是分开的,但在此之后buffer page属于page cache的一部份,没有单独的page cache了。
作者: 思一克    时间: 2007-06-20 17:22
当你用以文件的方式打开一个文件的时候,如果文件没有hole,则读写是以page为单位的而不是以block为单位的,所以不需要buffer_head
??

详细的解释?


原帖由 zx_wing 于 2007-6-19 14:38 发表于 6楼  

首先没有所谓的buffer层,其次buffer_head只是把page cache中的page细分成以block为单位。当你用以文件的方式打开一个文件的时候,如果文件没有hole,则读写是以page为单位的而不是以block为单位的,所以不需 ...

作者: qtdszws    时间: 2007-06-20 17:47
塑料袋 2.4.32中
2)...但是只有块设备文件的page所关联的buffer_head,才能进入buffer_head的hash表...

没找到具体的代码,能否指定一下
作者: zx_wing    时间: 2007-06-20 18:02
原帖由 思一克 于 2007-6-20 17:22 发表于 14楼  
当你用以文件的方式打开一个文件的时候,如果文件没有hole,则读写是以page为单位的而不是以block为单位的,所以不需要buffer_head
??

详细的解释?



以页为单位是针对page cache这一层来说的。在linux中,只要不是以DIRECT I/O的方式操作文件,都要和page cache打交道。当以文件的形式进行读写时,如果文件没有hole,则当你发起一次读/写操作,即使只有1个字节,kernel也会在page cache中为你分配一个页面,将多余的部分清零。因为read/write首先是和page cache打交道,所以说是以页为单位。
当然,最终的I/O操作还是以segment或sector为单位的。
作者: 塑料袋    时间: 2007-06-20 18:05
原帖由 思一克 于 2007-6-20 17:22 发表于 14楼  
当你用以文件的方式打开一个文件的时候,如果文件没有hole,则读写是以page为单位的而不是以block为单位的,所以不需要buffer_head
??

详细的解释?




写一个page的最一般化,理想化的过程:

1) 写了page后,置PG_Dirty

2) 将page关联的buffer_head置bh_dirty后,清PG_Dirty

3) 由bdflush启动写磁盘,或者进程自己直接submit_bh


表现在kernel中,经常省略了置PG_Dirty这一步,写有两种形式。

1)sys_write:根本不置PG_Dirty,直接找到/分配buffer_head,将buffer_head记bh_dirty,然后由bdflush启动写磁盘。

2)sys_mmap2:影射后,swap_out已经将page记PG_Dirty。

                 kswapd直接就清了PG_Dirty,然后又根sys_write一样,找到/分配buffer_head,将buffer_head记做bh_dirty,不同的是然后直接由submit_bh启动写。



不管有洞没洞,所影响的只是找到/分配buffer_head的过程,不影响其他。
作者: zx_wing    时间: 2007-06-20 18:08
原帖由 塑料袋 于 2007-6-20 18:05 发表于 17楼  



写一个page的最一般化,理想化的过程:

1) 写了page后,置PG_Dirty

2) 将page关联的buffer_head置bh_dirty后,清PG_Dirty

3) 由bdflush启动写磁盘,或者进程自己直接submit_bh


表现在kerne ...

这是块设备的情况,以regular file操作的读写不需要buffer_head的
作者: zx_wing    时间: 2007-06-20 18:10
原帖由 塑料袋 于 2007-6-20 18:05 发表于 17楼  



写一个page的最一般化,理想化的过程:

1) 写了page后,置PG_Dirty

2) 将page关联的buffer_head置bh_dirty后,清PG_Dirty

3) 由bdflush启动写磁盘,或者进程自己直接submit_bh


表现在kerne ...

bdflush ??? 看来兄弟确实用的是2.4内核
作者: 思一克    时间: 2007-06-20 18:58
我是问
"则读写是以page为单位的而不是以block为单位的,所以不需要buffer_head
"
作者: zx_wing    时间: 2007-06-20 19:26
原帖由 思一克 于 2007-6-20 18:58 发表于 20楼  
我是问
"则读写是以page为单位的而不是以block为单位的,所以不需要buffer_head
"


哦,这个是因为对于以regular file方式打开的文件的,读写的时候kernel以page为单位管理page cache,这个时候一个页不会被分成多个block,所以就没有对应的buffer_head结构。
我不太清楚2.4内核的时候是否也如此,但2.6内核的时候是这样的。
作者: 思一克    时间: 2007-06-20 19:44
没有bh的原因是block的大小等于page大小.

你前边说linux ext2 block大小一般是1024. 不是的, 一般是4096.

原帖由 zx_wing 于 2007-6-20 19:26 发表于 21楼  


哦,这个是因为对于以regular file方式打开的文件的,读写的时候kernel以page为单位管理page cache,这个时候一个页不会被分成多个block,所以就没有对应的buffer_head结构。
我不太清楚2.4内核的时候是否 ...

作者: zx_wing    时间: 2007-06-20 20:32
原帖由 思一克 于 2007-6-20 19:44 发表于 22楼  
没有bh的原因是block的大小等于page大小.

你前边说linux ext2 block大小一般是1024. 不是的, 一般是4096.


>>没有bh的原因是block的大小等于page大小.

我想不是这样的,这个可以通过源码来证明。以读为例子,我们从regluar file的read操作中和page cache打交道的函数do_generic_mapping_read()开始(这里只列出了和page cache相关的函数):
do_generic_mapping_read() --->find_get_page() ---->radix_tree_lookup() ---->__lookup_slot()
上述就是read操作在page cache中查找对应page的过程,整个过程中我们可以看出,kernel是以radix_tree_node 这个结构来管理以页为单位的page cache的,它是直接查找radix树,不涉及到buffer page(也就是包含bh结构的page)。
对于bh存在的理由,我们用一段ULK上的话来解释:

  1. In old versions of the Linux kernel, there were two different main disk caches: the page cache, which stored whole pages of disk data resulting from accesses to the contents of the disk files, and the buffer cache , which was used to keep in memory the contents of the blocks accessed by the VFS to manage the disk-based filesystems.

  2. Starting from stable version 2.4.10, the buffer cache does not really exist anymore. In fact, for reasons of efficiency, block buffers are no longer allocated individually; instead, they are stored in dedicated pages called "buffer pages ," which are kept in the page cache.

  3. [color=Red]Formally, a buffer page is a page of data associated with additional descriptors called "buffer heads ," whose main purpose is to quickly locate the disk address of each individual block in the page. In fact, the chunks of data stored in a page belonging to the page cache are not necessarily adjacent on disk[/color]
复制代码

可以看出,bh的主要目的在于一个page中包含的block在disk不相邻的情况。这种不相邻的情况就是我说的文件的hole(对于regular file)。

此外,我的印象中记得linux默认的block是1024字节,但刚才查了查资料又看看了源码,没有找到证明。版主说是4k,什么地方能查到呢?我认为4k的block对于设备来说太大了,因为DMA操作多以segment为单位,一个segment包含多个sector。硬件规定了segment中的sector必须物理上连续,所以如果一个block太大的话,包含不连续sector的概率就会增大,这样很可能一个次I/O要拆分成多次dma,毕竟block是VFS管理文件的单位。
作者: 塑料袋    时间: 2007-06-20 20:56
原帖由 qtdszws 于 2007-6-20 17:47 发表于 15楼  
塑料袋 2.4.32中
2)...但是只有块设备文件的page所关联的buffer_head,才能进入buffer_head的hash表...

没找到具体的代码,能否指定一下



见grow_buffers函数


所有增加buffercache的操作,归根到底是要调用grow_buffers。这个应该没疑问。

grow_buffers在2.4.32的实现是以( block_device->bd_inode->i_mapping,index)为唯一索引,搜索pagecache,在pagecache中找到或者创建一个,属于块设备文件的缓存页,然后将这个page上关联的buffer_head哈希之。

由此可见,所有hash表中的buffer_head所占用的内存空间,是块设备文件页缓存的子集;极端条件下,两者相等。
作者: zx_wing    时间: 2007-06-20 21:08
原帖由 塑料袋 于 2007-6-20 20:56 发表于 24楼  



见grow_buffers函数


所有增加buffercache的操作,归根到底是要调用grow_buffers。这个应该没疑问。

grow_buffers在2.4.32的实现是以( block_device->bd_inode->i_mapping,index)为唯一索引 ...

在2.4中,用文件方式读写而不用打开设备文件的方式读写,也会调用到 grow_buffers分配buffer page吗?
作者: 塑料袋    时间: 2007-06-20 21:17
原帖由 zx_wing 于 2007-6-20 21:08 发表于 25楼  

在2.4中,用文件方式读写而不用打开设备文件的方式读写,也会调用到 grow_buffers分配buffer page吗?


没看明白,你是指的:
文件方式读写==读写regular文件
打开设备文件方式读写==读写块设备文件??
作者: zx_wing    时间: 2007-06-20 21:24
原帖由 塑料袋 于 2007-6-20 21:17 发表于 26楼  


没看明白,你是指的:
文件方式读写==读写regular文件
打开设备文件方式读写==读写块设备文件??

是的,是的是的是的是的是的是的(不好意思,10字限制)
作者: 塑料袋    时间: 2007-06-20 21:42
无论怎么写文件,都不会调用grow_buffers。只有那些诸如ext2_get_block,ext2_unlink,ext2_alloc_branches......这些以文件系统的管理为目的的函数才会直接调用getblk---->grow_buffers。

不论写什么文件,2.4.32都是直接写在pagecache里的某一page上,然后将这个page关联上buffer_head结构,仅只是buffer_head结构,而不包括那1024的空间。将buffer_head记bh_dirty或submit_bh后就返回了。

假设你写的块设备文件,那么到现在为止,page关联的buffer_head并不属于buffercache,并没有对buffer_head进行hash,他们只在lru链和b_this_page链。

只有当getblk要查找某一buffer_head时:
1:先在buffercache中查找;
2:查找失败,则将目标buffer_head的(k_dev,nr)换算成(block_device->i_mapping,index),到pagecache中查找/创建对应页。这时就找到了你刚才所写的page。
3:将你刚才所写的page所关联的buffer_head进行hash,进入buffercache
作者: zx_wing    时间: 2007-06-20 21:54
原帖由 塑料袋 于 2007-6-20 21:42 发表于 28楼  
无论怎么写文件,都不会调用grow_buffers。只有那些诸如ext2_get_block,ext2_unlink,ext2_alloc_branches......这些以文件系统的管理为目的的函数才会直接调用getblk---->grow_buffers。

不论写什么文件 ...

谢谢,明白了
这样看来,2.4中的page cache都会被分成block,然后和bh关联

[ 本帖最后由 zx_wing 于 2007-6-20 22:00 编辑 ]
作者: qtdszws    时间: 2007-06-21 12:19
原帖由 塑料袋 于 2007-6-20 21:42 发表于 28楼  
无论怎么写文件,都不会调用grow_buffers。只有那些诸如ext2_get_block,ext2_unlink,ext2_alloc_branches......这些以文件系统的管理为目的的函数才会直接调用getblk---->grow_buffers。

不论写什么文件 ...


是否可以说,
在v2.4.0中,对普通文件的读写的buffer_head不进入hash,对设备文件的读写和文件系统的metadata进行读写的buffer_head进入hash
在v2.4.32中,对普通文件的读写和对设备文件的读写的buffer_head都不进入hash,只有对文件系统的metadata进行读写的buffer_head才进入hash
作者: 思一克    时间: 2007-06-21 12:41
1)block 大小在EXT2是4K, 你用什么方法都可以知道,比如用lde看磁盘。或看__getblk的大小, 或找blkdev中的参数(估计有)。

2)读regluar file, 你读FAT分区上的一个block大小是512的文件内容看,page cache都有bh.


原帖由 zx_wing 于 2007-6-20 20:32 发表于 23楼  

>>没有bh的原因是block的大小等于page大小.

我想不是这样的,这个可以通过源码来证明。以读为例子,我们从regluar file的read操作中和page cache打交道的函数do_generic_mapping_read()开始(这里只 ...

作者: zx_wing    时间: 2007-06-21 13:55
1)block 大小在EXT2是4K, 你用什么方法都可以知道,比如用lde看磁盘。或看__getblk的大小, 或找blkdev中的参数(估计有)。
__getblk使用的大小取决于传入的参数。希望版主能给出明确的证据,因为我对默认block大小1k的印象非常强烈,没有明确的证据我无法认为它是4k。并且我也想不出用4k的理由。不用4k的理由我上个帖子中已经给出。

2)读regluar file, 你读FAT分区上的一个block大小是512的文件内容看,page ca ...
如果版主坚持这么认为,我也没有办法。但我可以非常确定的在2.6内核中不是你说这样。上个回帖中已经给出了源码路径了,只要真去阅读了问题自然明了,这个问题就不争论了。

[ 本帖最后由 zx_wing 于 2007-6-21 14:07 编辑 ]
作者: 思一克    时间: 2007-06-21 14:06
我的也是2.6.13 KERNEL。盘是6G, 8G, 20G, ext2.
你是用什么看出block大小是1024的(也可能在你特定安装环境下就是)?


原帖由 zx_wing 于 2007-6-21 13:55 发表于 32楼  

如果版主坚持这么认为,我也没有办法。但我可以非常确定的在2.6内核中不是你说这样。

作者: zx_wing    时间: 2007-06-21 14:15
原帖由 思一克 于 2007-6-21 14:06 发表于 33楼  
我的也是2.6.13 KERNEL。盘是6G, 8G, 20G, ext2.
你是用什么看出block大小是1024的(也可能在你特定安装环境下就是)?



这个是我印象中的大小,昨天我也查了不少资料,但没查到。源码跟了一遍,也没跟到默认值。所以我想知道版主说ext2上默认4k的大小是从哪儿得来的。
我记得block size在什么地方是可以改的,就是记不起来了

[ 本帖最后由 zx_wing 于 2007-6-21 14:21 编辑 ]
作者: 思一克    时间: 2007-06-21 14:24
你印象的那就不行了。也不用查资料。你下载可lde(linux disk editor)一看不就知道了。

block_dev 中的bd_block_size
sb 中的 s_blocksize 都是是这个数值。

可以printk出来看。


原帖由 zx_wing 于 2007-6-21 14:15 发表于 34楼  

这个是我印象中的大小,昨天我也查了不少资料,但没查到。源码跟了一遍,也没跟到默认值。所以我想知道版主说ext2上默认4k的大小是从哪儿得来的。

作者: 塑料袋    时间: 2007-06-21 14:31
blocksize应该是1024

在ext2_read_super中决定了块的大小

首先是默认的BLOCK_SIZE,定义于fs.h,是1<<10
作者: 思一克    时间: 2007-06-21 14:31
我不是坚持,也不是认为,
而是用程序实验了结论:读FAT分区的普通文件后,每一个page(cached)都有buffer_head.

读EXT2的文件后没有。
所以”读普通文件不用buffer_head"至少在读FAT分区文件是不对的。




读regluar file, 你读FAT分区上的一个block大小是512的文件内容看,page ca ...

”如果版主坚持这么认为,我也没有办法。但我可以非常确定的在2.6内核中不是你说这样。上个回帖中已经给出了源码路径了,只要真去阅读了问题自然明了,这个问题就不争论了。“

[ 本帖最后由 思一克 于 2007-6-21 14:36 编辑 ]
作者: 思一克    时间: 2007-06-21 14:34
你截获__getblk看size是多少,或用lde看。

在我的系统(普通的,2。6。13 笔记本),
ext2, ext3, tmpfs, usbfs, debugfs, rootfs, sysfs的blocksize都是4096。
FAT分区是512
bdev, proc, pipefs, sockfs, devpts才是1024.


原帖由 塑料袋 于 2007-6-21 14:31 发表于 36楼  
blocksize应该是1024

在ext2_read_super中决定了块的大小

首先是默认的BLOCK_SIZE,定义于fs.h,是1<<10

作者: zx_wing    时间: 2007-06-21 14:40
原帖由 思一克 于 2007-6-21 14:24 发表于 35楼  
你印象的那就不行了。也不用查资料。你下载可lde(linux disk editor)一看不就知道了。

block_dev 中的bd_block_size
sb 中的 s_blocksize 都是是这个数值。

可以printk出来看。



好的,等忙完手上的工作,我会在block_size函数中加printk看看。
但我仍会试着找到有决定性证明的资料。因为这种通过“看”得到的信息不可靠,很可能换个平台就不一样了。例如我一直认为x86平台上linux默认的用户栈大小是4M,但昨天我看FC5发现它默认的大小是10M,所以还是能找到资料比较好。
作者: 思一克    时间: 2007-06-21 14:40
这是用一个程序输出的结果。

super blocks list for all file systems:
sb c12b8a00 type c03a8680        sysfs     -- blksize 4096 s_root c12b28c0 / bdev 00000000
sb c12b8800 type c03a9140       rootfs     -- blksize 4096 s_root c12b2830 / bdev 00000000
sb c12b8600 type c03a5700         bdev     -- blksize 1024 s_root c12b27a0 bdev: bdev 00000000
sb c12b8200 type c03a68a0         proc     -- blksize 1024 s_root c12b2710 / bdev 00000000
sb cfb84e00 type c03beba0       sockfs     -- blksize 1024 s_root cf6e0320 socket: bdev 00000000
sb cfeb5400 type c03a5c60       pipefs     -- blksize 1024 s_root cfc59cb0 pipe: bdev 00000000
sb cfc71c00 type c03a2b20      futexfs     -- blksize 1024 s_root cfc78dd0 futex bdev 00000000
sb cfc71800 type c03a54c0        tmpfs     -- blksize 4096 s_root cfc78d40 / bdev 00000000
sb cfc71000 type c03a6300    inotifyfs     -- blksize 1024 s_root cfc78cb0 inotify bdev 00000000
sb cfc8d600 type c03a63a0  eventpollfs     -- blksize 1024 s_root cfc78c20 eventpoll: bdev 00000000
sb cfc8d000 type c03a8820       devpts     -- blksize 1024 s_root cfc78b90 / bdev 00000000
sb cfc96800 type c03a9360    hugetlbfs     -- blksize 4194304 s_root cfc78b00 / bdev 00000000
sb cfc96200 type c03acea0       mqueue     -- blksize 4096 s_root cfc78a70 / bdev 00000000
sb cf261800 type c03a54c0        tmpfs     -- blksize 4096 s_root cf27ae60 / bdev 00000000
sb cecaf200 type c03a8dc0         ext2   hda6 blksize 4096 s_root cec4a290 / bdev c12b0480
sb cecaf800 type c03a54c0        tmpfs     -- blksize 4096 s_root cf3e9c20 / bdev 00000000
sb cf3d9600 type c03a54c0        tmpfs     -- blksize 4096 s_root cf3e2290 / bdev 00000000
sb cd081800 type c03ac960      debugfs     -- blksize 4096 s_root cce0f7a0 / bdev 00000000
sb cd081200 type d08fa5c0        usbfs     -- blksize 4096 s_root cce2bb00 / bdev 00000000
sb cecaf000 type c03a8dc0         ext2   hda8 blksize 4096 s_root cd3650e0 / bdev c12b0a80
sb c61ab800 type d0c7d6c0         vfat   hda5 blksize  512 s_root ced660e0 / bdev cf28d900
作者: zx_wing    时间: 2007-06-21 14:42
原帖由 思一克 于 2007-6-21 14:31 发表于 37楼  
我不是坚持,也不是认为,
而是用程序实验了结论:读FAT分区的普通文件后,每一个page(cached)都有buffer_head.

读EXT2的文件后没有。
所以”读普通文件不用buffer_head"至少在读FAT分区文件是不对的 ...

请讲述一下你是如何实验证明出每一个page都分配了buffer_head结构的。
作者: 思一克    时间: 2007-06-21 14:43
好。

这中问题找材料没有大用处。因为1024也不是错的呀。比如在非常老的386上,也许默认安装成了1024块大小的文件系统。或者在好机器上安装时指定了块大小。

我说的是一般机器默认安装的情况下是4096。

原帖由 zx_wing 于 2007-6-21 14:40 发表于 39楼  

好的,等忙完手上的工作,我会在block_size函数中加printk看看。
但我仍会试着找到有决定性证明的资料。因为这种通过“看”得到的信息不可靠,很可能换个平台就不一样了。例如我一直认为x86平台上linux默认的 ...

作者: zx_wing    时间: 2007-06-21 14:44
原帖由 思一克 于 2007-6-21 14:40 发表于 40楼  
这是用一个程序输出的结果。

super blocks list for all file systems:
sb c12b8a00 type c03a8680        sysfs     -- blksize 4096 s_root c12b28c0 / bdev 00000000
sb c12b8800 type c03a9140        ...

很好,这个例子说明了不同文件系统中block size大小是不一样的。
作者: 思一克    时间: 2007-06-21 14:49
看到的信息比”资料“更可靠。至少在你看的那个机器和系统版本上是无比正确的。

资料对于非对即错的问题有大帮助。对于可以调整参数“块大小”问题,基本没有多大意义。

原帖由 zx_wing 于 2007-6-21 14:40 发表于 39楼  

好的,等忙完手上的工作,我会在block_size函数中加printk看看。
但我仍会试着找到有决定性证明的资料。因为这种通过“看”得到的信息不可靠,很可能换个平台就不一样了。例如我一直认为x86平台上linux默认的 ...

作者: 塑料袋    时间: 2007-06-21 14:53
我截获的不一定是默认值,而且对于linux下的那些工具也不怎么会使。

但是首先是默认的1024,
然后hardsect_size做一修改,这种修改除非hardsect_size>1024才发生
然后以1024为块大小读sb
然后以sb->sb->s_blocksize_bits再一次修改,可能修改为2048或者4096

最后当ext2_read_super做完如上的工作后,block_size得以最终确定,写到了blksize_size数组中,就不会再变化了。
作者: 思一克    时间: 2007-06-21 14:54
读文件后,找到文件的address_space, 找到所以影射的pages,
如果
page_has_buffers(page)
为真,
打印出各buffer_head.  

page_bufers(page)

你可以试图做以下看。很有意思的。


原帖由 zx_wing 于 2007-6-21 14:42 发表于 41楼  

请讲述一下你是如何实验证明出每一个page都分配了buffer_head结构的。

作者: 思一克    时间: 2007-06-21 14:56
那你那LDE看,块大小是存在硬盘上的(super block)。
多么简单的方法。


原帖由 塑料袋 于 2007-6-21 14:53 发表于 45楼  
我截获的不一定是默认值,而且对于linux下的那些工具也不怎么会使。

但是首先是默认的1024,
然后hardsect_size做一修改,这种修改除非hardsect_size>1024才发生
然后以1024为块大小读sb
然后以sb-> ...

作者: zx_wing    时间: 2007-06-21 15:13
原帖由 思一克 于 2007-6-21 14:54 发表于 46楼  
读文件后,找到文件的address_space, 找到所以影射的pages,
如果
page_has_buffers(page)
为真,
打印出各buffer_head.  

page_bufers(page)

你可以试图做以下看。很有意思的。



page_has_buffers??2.6内核中有这个函数吗?在哪儿?要检测一个页是否是buffer page,只要看PG_private标志就行了啊。
这个实验原理简单,但实现麻烦,还要专门写个module,如果版主能给出源码最好,没有就算了,我也不打算去做,没有时间。因为通过读read整个源码过程里面我没有看到和buffer head打交道的地方,加上ULK的佐证,我还是比较确信读regular file不需要bh。
作者: 思一克    时间: 2007-06-21 15:21
不用MODULE,修改vfs_read

在file->f_op->read()
之后,根据file 可以找到一切。



原帖由 zx_wing 于 2007-6-21 15:13 发表于 48楼  

page_has_buffers??2.6内核中有这个函数吗?在哪儿?要检测一个页是否是buffer page,只要看PG_private标志就行了啊。
这个实验原理简单,但实现麻烦,还要专门写个module,如果版主能给出源码最好,没有就 ...

作者: qtdszws    时间: 2007-06-22 08:55
原帖由 qtdszws 于 2007-6-21 12:19 发表于 30楼  


是否可以说,
在v2.4.0中,对普通文件的读写的buffer_head不进入hash,对设备文件的读写和文件系统的metadata进行读写的buffer_head进入hash
在v2.4.32中,对普通文件的读写和对设备文件的读写的buffer_hea ...


请高手指点一下啊
作者: daemeon    时间: 2007-06-22 09:25
原帖由 qtdszws 于 2007-6-19 10:27 发表于 1楼  
设备文件和设备上的文件都可以被映射

文件被映射时,都是按照逻辑块顺序被映射到连续的虚拟地址空间和物理页中

而文件在设备上的块是乱序的

如果块大小<物理页大小,那么设备文件和设备上的文件同时 ...



文件和块设备使用的address space是不同的, 块设备上的一个块可能同时映射到两个页中.
一个页属于文件的address space, 另一个属于块设备的address space.
作者: 塑料袋    时间: 2007-06-23 17:59
原帖由 qtdszws 于 2007-6-21 12:19 发表于 30楼  
在v2.4.0中,对普通文件的读写的buffer_head不进入hash,对设备文件的读写和文件系统的metadata进行读写的buffer_head进入hash
在v2.4.32中,对普通文件的读写和对设备文件的读写的buffer_head都不进入hash,只有对文件系统的metadata进行读写的buffer_head才进入hash


2.4.0,是


2.4.32,  1)  对设备文件读写是针对page的读写,除了使page关联的buffer_head进入lru以外,无其他针对buffer_head的操作。

           2) 对文件系统的metadata进行读写前,为了能够hash搜索到这个buffer_head,会把1)中page所关联的buffer_head
              加入到hash。然后就可以做到:针对metadata进行的读写,都是读写hash中的buffer_head
作者: qtdszws    时间: 2007-06-23 18:15
>>>2) 对文件系统的metadata进行读写前,为了能够hash搜索到这个buffer_head,会把1)中page所关联的buffer_head加入到hash。然后就可以做到:针对metadata进行的读写,都是读写hash中的buffer_head

"会把1)中page所关联的buffer_head加入到hash"

这句有问题,没有看到具体的代码,请指点

[ 本帖最后由 qtdszws 于 2007-6-24 10:37 编辑 ]
作者: qtdszws    时间: 2007-06-24 20:45
下面的文字是通过短信询问塑料袋兄的回复
"
可能我说的不清楚吧,我是指的grow_buffers函数

为了读写文件系统的metadata,必然是bread->getblk->grow_buffers->grow_dev_page->find_or_creat_page->hash_page_buffers。

以上就是说,找到块设备文件的page:  grow_dev_page->find_or_creat_page。
但这时page关联的buffer_head本就没有hash,于是现在将它们进行hash: hash_page_buffers
"

跟踪代码后发现,在操作metadata时,实际上已经是对块设备的address space在进行操作了并hash.
page = find_or_create_page(bdev->bd_inode->i_mapping, index, GFP_NOFS);
这样操作块设备和操作metadata就有机地融合一起了

谢谢塑料袋兄和众多高手的指点
作者: EZWORD    时间: 2010-08-29 19:33
太深奥了,看不明白。。。
作者: 卐伪装卐    时间: 2010-08-29 22:04
建立/common/staff工作目录:
         对用户taliesin无任何权限
          对其它使用者有rw权限
???????
应该怎么做呀,我做的怎么总是错误哩!!!!!!!!





欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2