关闭linux系统中读写页缓存,Linux文件系统FAQ
Linux文件系統(tǒng)FAQ
2010年03月25日
最近實驗室搞了一些列講座,阿福師兄關(guān)于文件系統(tǒng)的講座幫我弄清楚了一些以前不清楚的問題,以問答的形式對文件系統(tǒng)常見的問題進行了總結(jié)。
Q: 文件系統(tǒng)如何看待底層物理塊設(shè)備?
文件系統(tǒng)把塊設(shè)備簡單的看做線性的組合,即對文件系統(tǒng)而言,塊設(shè)備是一系列可以讀寫的塊。文件系統(tǒng)不需要知道這些物理設(shè)備的實際布局及如何讀寫,這些是設(shè)備驅(qū)動的工作。
Q:?跟文件系統(tǒng)相關(guān)的系統(tǒng)調(diào)用主要有那些?
打開文件open,讀取文件read,寫文件write,關(guān)閉文件close,刪除文件unlink,創(chuàng)建目錄mkdir,刪除目錄rmdir等,linux通過VFS提供了符合POSIX標(biāo)準(zhǔn)的接口。
Q:?如何實現(xiàn)一個文件系統(tǒng)?
實現(xiàn)VFS提供的一組文件系統(tǒng)操作接口,向內(nèi)核注冊;
實現(xiàn)用戶空間文件系統(tǒng)或堆疊式文件系統(tǒng);
Q: VFS如何管理super_block,inode,dentry,file,vfsmount等主要數(shù)據(jù)結(jié)構(gòu)?
參見http://blog.chinaunix.net/u2/87570/showart_2126000 .html;
Q:?哪些接口必須實現(xiàn)?
VFS實現(xiàn)了很多通用接口,如基本所有的讀寫操作都可直接使用generic_file_aio_read,generic_file_aio_write接口(我的內(nèi)核版本為2.6.19),ext2的讀寫就是使用該接口,但各個文件系統(tǒng)必須實現(xiàn)自己的read_page方法,用于從磁盤讀取一頁的數(shù)據(jù)(還可實現(xiàn)read_pages,一次讀取多頁,以提高效率),如果要實現(xiàn)direct_io訪問,必須實現(xiàn)direct_IO接口。
read_page的實現(xiàn)需要基于文件系統(tǒng)實際的數(shù)據(jù)組織,它將用戶的文件請求位置(邏輯頁號)轉(zhuǎn)換為物理塊號,并向通用塊層發(fā)送請求。
Q: ext2文件系統(tǒng)如何組織文件的數(shù)據(jù)?
ext2使用長度為15的數(shù)組(ext2_inode的一個字段),其中前12個數(shù)組元素記錄直接塊映射,即其內(nèi)容即為文件前12個塊的地址;第13個元素,記錄一級索引關(guān)系,即該元素的內(nèi)容為一個塊的地址,這個塊的內(nèi)容為一系列塊的地址;第14,15個元素分別為二級索引,三級索引。
Q:?內(nèi)核如何根據(jù)用戶態(tài)傳遞的路徑名得到文件的inode,dentry信息?
通過路徑名查找可以通過路徑名得到inode,dentry的信息;
Linux提供了path_lookup接口來實現(xiàn)路徑名的解析,其具體實現(xiàn)以下工作:
1. 獲取路徑名查找的起點,當(dāng)前目錄或是根目錄;
2. 以/為分隔符,解析每個目錄項。
3. 針對每個目錄項,首先查找目錄項高速緩存,判斷當(dāng)前的目錄項對象是否在緩存中,如果在,則直接從緩存中獲取結(jié)果;如果不在,則需要在上一級目錄中調(diào)用實際文件系統(tǒng)實現(xiàn)的lookup方法查找,并讀取目錄項對應(yīng)的inode信息,填充dentry結(jié)構(gòu),并將該結(jié)構(gòu)加入到高速緩存。
Q:?內(nèi)核如何根據(jù)路徑名查找的結(jié)果得到file結(jié)構(gòu)?
通過dentry_open實現(xiàn),具體執(zhí)行以下工作:
1. 分配一個文件對象;
2. 根據(jù)傳遞進來的dentry信息,vfsmnt信息,初始化file對象的f_fentry,f_vfsmnt;
3. 以索引節(jié)點的i_fop填充f_op。
4. 將文件對象插入到文件系統(tǒng)超級塊的s_files字段所指向的鏈表中。
Q:?索引節(jié)點的i_fop,i_op,i_mapping的a_ops字段何時被初始化?
具體文件系統(tǒng)讀取索引節(jié)點時初始化,如ext2的ext2_read_inode方法;
在ext2_read_inode中,根據(jù)文件的類型不同,將i_fop,i_op,i_mapping的a_ops初始化為相應(yīng)的方法。
1. 對于普通的文件,三者的值分別為ext2_file_operations,ext2_file_inode_operations,ext2_aops;
2. 對于目錄三者的值分別為ext2_dir_operations,ext2_dir_inode_operations,ext2_aops;
3. 對于鏈接文件i_op被賦值為ext2_symlink_inode_operations;
bdget()根據(jù)塊設(shè)備號初始化一個block_device結(jié)構(gòu),該結(jié)構(gòu)字段的bd_inode的i_data被初始化為def_blk_aops的地址,當(dāng)不同過文件系統(tǒng)讀取塊設(shè)備時,def_blk_ops會被調(diào)用;def_blk_aops的read_page方法調(diào)用block_read_full_page以一次讀一塊的方式讀取整個頁的數(shù)據(jù)。
Q:?對于打開的文件,在用戶態(tài)以fd標(biāo)識,在內(nèi)核態(tài)以file結(jié)構(gòu)標(biāo)識,fd與 file如何對應(yīng)?
每一個進程由一個task_struct結(jié)構(gòu)描述,其中的files字段是一個files_struct的結(jié)構(gòu),主要描述文件打開的文件信息,包括fd使用位圖,files對象數(shù)組fd_array,其中fd_array的下標(biāo)即對應(yīng)著該file對象對應(yīng)的fd。
當(dāng)進程通過路徑名獲取到file對象后,會將file對象的指針放入fd_array數(shù)組的相應(yīng)位置;
Q: direct io是怎么回事?
直接IO(direct io)是指讀寫文件系統(tǒng)數(shù)據(jù)時繞過頁高速緩存。
具體文件系統(tǒng)支持直接IO需要實現(xiàn)a_ops中的direct_IO方法;
不管是直接IO,還是經(jīng)過頁高速緩存的IO操作,都是將請求通過bio發(fā)到通用塊層來實現(xiàn)的。
Q:?高速緩存頁分哪些類型?
含有普通文件數(shù)據(jù)或目錄的頁;
含有直接從塊設(shè)備文件,跳過文件系統(tǒng)層,讀出來的數(shù)據(jù)的頁;
含有用戶態(tài)進程數(shù)據(jù)的頁,但頁中的數(shù)據(jù)已經(jīng)被交換到磁盤;
屬于特殊文件系統(tǒng)的頁,如共享內(nèi)存的IPC所使用的特殊文件系統(tǒng)shm;
Q:?頁高速緩存中頁的數(shù)據(jù)都是不同的么?
頁高速緩存可以包含同一磁盤數(shù)據(jù)的多個副本,可以一下兩種方式可以訪問普通文件的同一塊:
1. 讀文件,此時,數(shù)據(jù)包含在普通文件索引節(jié)點所擁有的頁中;
2. 從文件所在的設(shè)備文件(磁盤分區(qū))讀取塊,此時,數(shù)據(jù)就包含在塊設(shè)備文件的主索引節(jié)點所擁有的塊中。
Q:?頁高速緩存如何組織?
Linux支持TB級的文件,訪問大文件時,頁高速緩存中可能充滿太多的文件頁,因此需要對這些頁進行高效的組織,使得內(nèi)核能迅速高效的查找頁。
Linux采用基樹(radix tree)對頁高速緩存進行組織,添加,刪除,查找頁的操作的時間復(fù)雜度都為O(1)。Linux提供一組方法方法用于處理頁高速緩存:
find_get_page()接受address_space對象指針及偏移量,返回對應(yīng)的頁描述符;
find_get_pages()查找一組具有相鄰索引的頁;
add_to_page_cache()把一個新頁的描述符插入到頁高速緩存;
remove_from_page_cache()將頁從高速緩存中移除;
read_cache_page()確保高速緩存中包含最新版本的指定頁;
Q:?緩沖區(qū)頁于頁內(nèi)緩沖區(qū)的關(guān)系?
如下圖所示:page結(jié)構(gòu)的private字段指向第一個緩沖區(qū)首部,各個緩沖區(qū)首部通過b_this_page鏈接,并通過b_page指向包含自己的page結(jié)構(gòu),b_data為其相對于頁的位置。當(dāng)頁在高端內(nèi)存時,b_data為緩沖區(qū)塊在業(yè)內(nèi)的偏移量;否則,b_data為緩沖區(qū)的線性地址,因高端內(nèi)存頁沒有固定的映射。
Q:?什么情況下內(nèi)核會創(chuàng)建緩沖區(qū)頁?
當(dāng)讀或?qū)懙奈募撛诖疟P塊上不相鄰時,即文件系統(tǒng)為文件分配了非連續(xù)的塊,或者因為文件有洞;在塊大小與頁大小相等的情況下,這種情況不會出現(xiàn)。
當(dāng)訪問一個單獨的磁盤塊時(如讀超級塊或索引節(jié)點塊)。
Q:?如何創(chuàng)建和釋放緩沖區(qū)頁?
調(diào)用grow_buffers(),其具體執(zhí)行如下步驟:
1. 如果對應(yīng)的頁不在塊設(shè)備的基樹中,需創(chuàng)建新的頁。
2. 調(diào)用alloc_page_buffer()為頁創(chuàng)建緩沖區(qū),一次創(chuàng)建頁能容納的所有緩沖區(qū),并建立好鏈接關(guān)系,進行必要的初始化。
調(diào)用try_to_free_page()釋放緩沖區(qū)頁。
Q:?如何在頁高速緩存中搜索塊?
將塊號轉(zhuǎn)換成頁號索引,并通過基樹提供的接口進行查找。
__find_get_block(), __getblk()接口提供搜索塊的接口,根據(jù)給定的設(shè)備信息及塊號,塊大小,返回塊對應(yīng)的buffer_head結(jié)構(gòu),后者在塊所在的緩沖區(qū)頁不存在時會分配緩沖區(qū)頁,創(chuàng)建緩沖區(qū)塊,并返回對應(yīng)塊的buffer_head結(jié)構(gòu)。
為了提高系統(tǒng)性能,內(nèi)核維持了一個小的磁盤高速緩存數(shù)組bh_lrus(每CPU變量),數(shù)組包含8個元素,指向最近訪問過的緩沖區(qū)首部。
Q:?如何向通用塊層提交緩沖區(qū)首部?
使用submit_bh()向通用塊層傳遞一個緩沖區(qū)首部,使用ll_rw_block可向通用塊層傳遞一組緩沖區(qū)首部;兩者都附帶讀寫傳輸方向標(biāo)志。
sumbit_bh()根據(jù)緩沖區(qū)首部內(nèi)容創(chuàng)建一個bio,具體執(zhí)行如下步驟:
1. 調(diào)用bio_alloc分配一個bio描述符;
2. 將bi_sector字段賦值為bh->b_blocknr * bh->b_size / 512;
3. 將bi_bdev字段賦值為bh->b_bdev;
4. 把bi_size設(shè)置為塊大小bh->b_size;
5. 初始化bi_io_vec的第一個元素以使該段對應(yīng)于塊緩沖區(qū);
bi_io_vec[0].bv_page = bh->b_page;
bi_io_vec[0].bv_len = bh->b_size;
bi_io_vec[0].bv_offset = bh->b_data;
6. 將bi_cnt字段置1,并把bi_idx置為0;
7. 將bi_end_io字段賦值為end_bh_bio_sync,bi_private字段賦值為緩沖區(qū)首部地址。
作為數(shù)據(jù)傳輸完畢后的執(zhí)行方法,數(shù)據(jù)傳輸完后,通過bi_private獲取buffer_head結(jié)構(gòu),執(zhí)行期bi_end_io字段的方法。
8. 調(diào)用submit_bio將bio提交到通用塊層。
ll_rw_block對數(shù)組中每個buffer_head調(diào)用submit_bh。
Q:?頁高速緩存何時被刷新?
基于性能考慮,linux系統(tǒng)采用延遲寫策略,即將把臟緩沖區(qū)寫入塊設(shè)備的操作延遲執(zhí)行;基于延遲寫,幾次寫操作可能只需要一次物理更新。從而使得物理塊設(shè)備平均為讀請求服務(wù)的時間多于寫請求。
以下條件會觸發(fā)把臟頁寫到磁盤:
1. 頁高速緩存變得太滿,但還需要更多的頁,或者臟頁的數(shù)量已經(jīng)太多;
2. 從頁變成臟頁的時間太長,超過某一閾值;
3. 進程請求或者特定文件系統(tǒng)特定的變化。如同過sync(),fsync(),fdatasync()系統(tǒng)調(diào)用實現(xiàn);
Linux通過pdflush內(nèi)核線程系統(tǒng)地掃描頁高速緩存以刷新臟頁,pdflush線程的數(shù)量隨著系統(tǒng)運行動態(tài)調(diào)整,具體原則如下:
1. 必須有至少兩個,之多八個pdflush內(nèi)核線程;
2. 如果到最近的1s期間沒有空閑的pdflush,就應(yīng)該創(chuàng)建新的pdflush;
3. 如果最近一次pdflush變?yōu)榭臻e的時間超過1s,就應(yīng)該刪除一個pdflush;
4. 通過定期喚醒的pdflush保證陳舊的頁及時寫回,頁保持臟狀態(tài)的最長時間為30s;
Q: sync(), fsync(),fdatasync()系統(tǒng)調(diào)用區(qū)別?
sync()把所有的臟緩沖區(qū)刷新到磁盤;
fsync()把屬于特定打開文件的所有塊刷新到磁盤;
fdatasync()與fsync()類似,但不刷新文件的索引節(jié)點塊;
Q: linux文件系統(tǒng)如何預(yù)讀取?
為了保證預(yù)讀命中率,linux只對順序讀進行預(yù)讀,內(nèi)核通過如下條件判斷read()是否為順序讀:
1. 這是文件被打開后的第一次讀,并且從文件頭開始讀;
2. 當(dāng)前的讀請求與前一次讀請求在文件內(nèi)的位置是連續(xù)的;
否則為隨機讀,任何一次隨機讀將終止預(yù)讀,而不是縮減預(yù)讀窗口。
當(dāng)確定了需要進行預(yù)讀時,就需要確定合適的預(yù)讀大小,預(yù)讀粒度太小,效果提升不明顯;預(yù)讀太多,可能載入太多不需要的頁面而造成資源浪費;linux的策略是:
1. 首次預(yù)讀:readahead_size = read_size * 2; // or *4;
2. 后續(xù)預(yù)讀:readahead_size = readahead_size * 2;
3. 系統(tǒng)設(shè)定的最大預(yù)讀大小為128K,該值可配置;
總結(jié)
以上是生活随笔為你收集整理的关闭linux系统中读写页缓存,Linux文件系统FAQ的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux查看xml文件的配置,3、kv
- 下一篇: c盘java文件误删_C盘爆满怎么办,教