免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 21188 | 回复: 55

映射的冲突 [复制链接]

论坛徽章:
0
发表于 2007-06-19 10:27 |显示全部楼层
设备文件和设备上的文件都可以被映射

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

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

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

目前的块大小都是4k,难道是为了避免这个冲突?

论坛徽章:
0
发表于 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的大小。

论坛徽章:
0
发表于 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中?

论坛徽章:
0
发表于 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进行映射时,它同样可以把文件中的内容读入到自己的物理页面中。两者并不冲突。

论坛徽章:
0
发表于 2007-06-19 14:14 |显示全部楼层
在buffer层上,设备上的块是由设备号和块号唯一确定

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

论坛徽章:
0
发表于 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 编辑 ]

论坛徽章:
0
发表于 2007-06-19 14:54 |显示全部楼层
>>则读写是以page为单位的而不是以block为单位的,所以不需要buffer_head。

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

论坛徽章:
0
发表于 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的下层。

论坛徽章:
0
发表于 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的帮助

论坛徽章:
0
发表于 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遇到问题阅读源码的精神!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP