分类: LINUX
2007-06-01 19:24:22
.文件写入最终是通过writepage(struct * page)完成的,但writepage(struct * page)调用完之后,
数据并没有被写入缓存中。这时如果修改page内的数据,连带着写入硬盘的数据也会变化。
假设我想修改page内的数据,但不希望影响写入硬盘的数据,应该怎样做呢?
在readpage()时,执行完readpage()之后,只要再执行一个wait_on_page(),page中即为有效数据。
感觉writepage()类似,执行一个wait_on_buffer(page->buffers)应该就可以了,试了一下,毫无效果。
——————————————————
***
mm/filemap.c
***
又重翻了一遍情景分析,read在generic_file_read()中调用了文件inode里的mapping->a_ops->readpage()函数,
而generic_file_write()函数中则是建立缓存后调用mapping->a_ops->prepare_write()
函数和mapping->a_ops->commit_write()函数把缓存内容提交给bdflush()进程的。
writepage()与readpage()至少在文件读写操作上并非对应关系。
现在有这么几个问题不明白。
1.writepage()函数到底什么时候运行,在文件写入操作中是否起作用?(是不是filemap操作会调用它)?
2. write操作时使用的缓存与readpage()读的页有什么关系吗?
假设一个文件以读写方式打开,readpage()读入某位置数据之后,
如果再在相同位置进行写操作,是否直接使用readpage时所用的页作为缓存?
3.filemap操作的时候,写入时利用了mapping->a_ops的哪些函数?大致流程是什么?
——————————————————
1.writepage()函数到底什么时候运行,在文件写入操作中是否起作用?(是不是filemap操作会调用它)?
需要将数据写入到address_space对象的backstore的时候,比如
sync的时候(flush dirty page to disk),又比如内存紧张,需要回收内存时,
将dirty page写入swap space或后备文件(filemap)。
filemap的情况应该是归到上述情况中去了,filemap操作应该不会直接调用,
系统在需要的时候会间接或自动调用。
writepage()和readpage()都是异步完成的,会设置BH_Async标志,
I/O操作完成时调用end_buffer_io_async(),
如有必要,可以显式等待(wait_on_page)
linux写文件时(通过sys_write),metadata是同步写入disk的,
而普通data(即文件内容)是通过prepare_write(),commit_write()
将data交给cache就完事了,由bdflush()在需要时将这些数据写入disk。
***************
bdflush()过程常驻核心空间,由核心唤醒来清理被写过的内存缓冲区,当bdflush()由kernel_thread()启动后,
它将自己命名为kflushd)
创建kupdate核心线程(kupdate()过程常驻核心空间,由核心按时调度执行,将内存缓冲区中的信息更新到磁盘中,
更新的内容包括超级块和inode表)
设置并启动核心调页线程kswapd(为了防止kswapd启动时将版本信息输出到其他信息中间,
核心线调用kswapd_setup()设置kswapd运行所要求的环境,然后再创建 kswapd核心线程)
****************
而bdflush()本身也并不调用writepage(),因为bdflush()实际上是针对
buffer的,它从BUF_DIRTY的buffer队列中依次取单个的buffer,
将其写入disk,它这里并没有设置BH_Async,并且I/O操作完成时
调用的是end_buffer_io_sync()。
因此,整个写文件期间并没有调用writepage()。
"情景分析"在这方面并无具体论述,ULK2有一些,建议看看ULK2,
并且2.4.0和现在的2.4.20在VM系统改变很大。
>2. write操作时使用的缓存与readpage()读的页有什么关系吗?假设一个文件以读写方式打开,
readpage()读入某位置数据之后,如果再在相同位置进行写操作,是否直接使用readpage时所用的页作为缓存?
是一个cache,即page cache。
>3.filemap操作的时候,写入时利用了mapping->a_ops的哪些函数?大致流程是什么?
mmap-->缺页-->filemap_nopage()-->page_cache_read()-->
readpage()-->用户写内存-->page变dirty-->内存紧张-->
shrink_cache()-->writepage()-->用户访问-->缺页-->
——————————————————
现在基本把文件系统的流程搞清楚了。
觉得Linux文件系统的源代码相当散,很难划出一个层次比较分明的结构。是否因为把效率放在第一位考虑的缘故?
另外,我能不能在commit_write执行之后就强行把这一页的页面数据写到硬盘上?或者说,有没有直接把一页中的缓冲区写到硬盘上的函数?
——————————————————
wait_on_page(struct *page)即可。
但要注意必须在UnlockPage()之前。
——————————————————
我在commit_write执行之后,修改page中的数值,unlock,wait_on_page,lock,再把数据改回去,结果写入的是改回去的数值。
我换了另一种方法,commit_write之后,修改page中的数值,unlock,调用ll_rw_block写page->buffers队列, lock,再把数据改回去,结果写入的是修改的数值。
如果执行wait_on_buffer(page->buffers),不知道行不行,一个page里的几个缓存能不能都被写进去?
——————————————————
wait_on_page()好象是有点问题,主要是page的lock的问题。
因为wait_on_page()要求I/O结束时对page解锁,而同步写并
不会这么做。这也就是说wait_on_page()只能用在设置了
end_buffer_io_async的page。
直接用ll_rw_block应该可以,但需要一个循环来依次写page的
各个buffer,并等待该buffer。
——————————————————
write之前改数据,write之后改回来。测试通过。
在block_prepare_write后也添了一段代码,把新读出来的数据全部修改,用vi,文件管道等测试,ok,又给readpage提供改数据的功能,一个简单的加密文件系统就做出来了!
不过,wait_on_buffer时如果有对页的访问,就访问到错误数据了吧?如果这时侯锁住页或者buffer,是不是写盘操作也进行不下去了?
还在考虑另一个问题,假设我想在linux上做一个跨多种文件系统的压缩文件系统,把一些标定的文件在磁盘上压缩存储,读取时转化为明文,该怎么做?由于文件存储长度和明文长度相比,变化很大,因此需要在page_cache和读写文件操作之间再插入一个新的buffer,维持这个buffer是否非常困难?
——————————————————
>不过,wait_on_buffer时如果有对页的访问,就访问到错误数据了吧?如果这时侯锁住页或者buffer,是不是写盘操作也进行不下去了?
不会呀,因为对页的访问都会看该页是否已经锁住,如果是,
则要么等待,要么推迟处理。
——————————————————
现在我做ll_rw_block和wait_on_buffer时没有锁住页,担心锁住页会影响ll_rw_block操作。
马上要回家了,周一再锁住页试试看。
——————————————————
在readpage,prepare_write,commit_write和writepage上都加上了对特定文件的跟踪调试。
然后写这个文件,prepare_write和commit_write中的调试信息都出现了,
关闭文件,再读这个文件,无任何调试信息输出。
先读文件时,readpage中的调试信息会正常输出。
因此,可以确定,read和write操作使用的缓存是一致的。
但还从没见过writepage里的调试信息输出。
对mmap,只知道私有mmap是用readpage函数读入的,share的还不清楚。
磁盘高速缓存分为两种:
buffer cache:当一个磁盘块被调入内存时,它首先存放在一个缓冲区中,每个缓冲区与一个磁盘块对应,
它相当于磁盘块在内存中的表示,每个缓冲区都有一个对应的描述符,叫做buffer_head。缓冲区头的目的
在于描述磁盘块和物理内存缓冲区之间的映射关系。因此,缓冲区头作为I/O操作的单元,不仅描述了从磁
盘到物理内存的映射,而且还是所有块I/O操作的容器。但是,2.6中,I/O操作系统基本容器变为bio结构了。
page cache是由内存中的物理页组成的,缓存中的每一页对应着磁盘中的多个块。其核心数据结构为address_space。
在执行I/O操作,比如 read()操作,内核首先会检查数据是否已经在页高速缓存中,如果在,那么内核就可以马上
从页高速缓存中得到所需要的页,而不需要从磁盘中读取数据了。
1.总结起来buffer cache以缓冲区为单位,对应一个磁盘块,page cache以页为单位,对应多个磁盘块,
那么是否是page cache对应多个buffer cache呢?或者说页跟缓冲区有什么关系?
2.page cache中的页以address_space为核心数据结构,那么跟和保存页框的状态信息的struct page的页描述符有什么联系呢?
page cache重要数据结构:
(1). 页散列表:利用address_space对象的地址和偏移量导出页描述符的地址(page中的mapping和index字段)
(2). address_space对象包含三种页描述符链表(clean_pages页不是最新的,dirty_pages页包含最新数据但还未写入磁盘,
locked_pages页内容正在内存与磁盘间传送)
(3). 页描述符page中有与page cache相关的字段(list\mapping\index\next_hash\pprev_hash)
3.问题和1比较相近