0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux的文件系統(tǒng)及文件緩存的知識(shí)點(diǎn)

馬哥Linux運(yùn)維 ? 來(lái)源:馬哥Linux運(yùn)維 ? 2023-02-13 13:52 ? 次閱讀

文件系統(tǒng)的特點(diǎn)

文件系統(tǒng)要有嚴(yán)格的組織形式,使得文件能夠以塊為單位進(jìn)行存儲(chǔ)。

文件系統(tǒng)中也要有索引區(qū),用來(lái)方便查找一個(gè)文件分成的多個(gè)塊都存放在了什么位置。

如果文件系統(tǒng)中有的文件是熱點(diǎn)文件,近期經(jīng)常被讀取和寫(xiě)入,文件系統(tǒng)應(yīng)該有緩存層。

文件應(yīng)該用文件夾的形式組織起來(lái),方便管理和查詢。

Linux內(nèi)核要在自己的內(nèi)存里面維護(hù)一套數(shù)據(jù)結(jié)構(gòu),來(lái)保存哪些文件被哪些進(jìn)程打開(kāi)和使用。

總體來(lái)說(shuō),文件系統(tǒng)的主要功能梳理如下:

e3f339e2-aa86-11ed-bfe3-dac502259ad0.png

inode與塊的存儲(chǔ)

硬盤(pán)分成相同大小的單元,我們稱為塊(Block)。一塊的大小是扇區(qū)大小的整數(shù)倍,默認(rèn)是4K。在格式化的時(shí)候,這個(gè)值是可以設(shè)定的。

一大塊硬盤(pán)被分成了一個(gè)個(gè)小的塊,用來(lái)存放文件的數(shù)據(jù)部分。這樣一來(lái),如果我們像存放一個(gè)文件,就不用給他分配一塊連續(xù)的空間了。我們可以分散成一個(gè)個(gè)小塊進(jìn)行存放。這樣就靈活得多,也比較容易添加、刪除和插入數(shù)據(jù)。

inode就是文件索引的意思,我們每個(gè)文件都會(huì)對(duì)應(yīng)一個(gè)inode;一個(gè)文件夾就是一個(gè)文件,也對(duì)應(yīng)一個(gè)inode。

inode數(shù)據(jù)結(jié)構(gòu)如下:

structext4_inode{
__le16i_mode;/*Filemode*/
__le16i_uid;/*Low16bitsofOwnerUid*/
__le32i_size_lo;/*Sizeinbytes*/
__le32i_atime;/*Accesstime*/
__le32i_ctime;/*InodeChangetime*/
__le32i_mtime;/*Modificationtime*/
__le32i_dtime;/*DeletionTime*/
__le16i_gid;/*Low16bitsofGroupId*/
__le16i_links_count;/*Linkscount*/
__le32i_blocks_lo;/*Blockscount*/
__le32i_flags;/*Fileflags*/
......
__le32i_block[EXT4_N_BLOCKS];/*Pointerstoblocks*/
__le32i_generation;/*Fileversion(forNFS)*/
__le32i_file_acl_lo;/*FileACL*/
__le32i_size_high;
......
};

inode里面有文件的讀寫(xiě)權(quán)限i_mode,屬于哪個(gè)用戶i_uid,哪個(gè)組i_gid,大小是多少i_size_io,占用多少個(gè)塊i_blocks_io,i_atime是access time,是最近一次訪問(wèn)文件的時(shí)間;i_ctime是change time,是最近一次更改inode的時(shí)間;i_mtime是modify time,是最近一次更改文件的時(shí)間等。

所有的文件都是保存在i_block里面。具體保存規(guī)則由EXT4_N_BLOCKS決定,EXT4_N_BLOCKS有如下的定義:

#defineEXT4_NDIR_BLOCKS12
#defineEXT4_IND_BLOCKEXT4_NDIR_BLOCKS
#defineEXT4_DIND_BLOCK(EXT4_IND_BLOCK+1)
#defineEXT4_TIND_BLOCK(EXT4_DIND_BLOCK+1)
#defineEXT4_N_BLOCKS(EXT4_TIND_BLOCK+1)

在ext2和ext3中,其中前12項(xiàng)直接保存了塊的位置,也就是說(shuō),我們可以通過(guò)i_block[0-11],直接得到保存文件內(nèi)容的塊。

e41babd4-aa86-11ed-bfe3-dac502259ad0.jpg

但是,如果一個(gè)文件比較大,12塊放不下。當(dāng)我們用到i_block[12]的時(shí)候,就不能直接放數(shù)據(jù)塊的位置了,要不然i_block很快就會(huì)用完了。

那么可以讓i_block[12]指向一個(gè)塊,這個(gè)塊里面不放數(shù)據(jù)塊,而是放數(shù)據(jù)塊的位置,這個(gè)塊我們稱為間接塊。如果文件再大一些,i_block[13]會(huì)指向一個(gè)塊,我們可以用二次間接塊。二次間接塊里面存放了間接塊的位置,間接塊里面存放了數(shù)據(jù)塊的位置,數(shù)據(jù)塊里面存放的是真正的數(shù)據(jù)。如果文件再大點(diǎn),那么i_block[14]同理。

這里面有一個(gè)非常顯著的問(wèn)題,對(duì)于大文件來(lái)講,我們要多次讀取硬盤(pán)才能找到相應(yīng)的塊,這樣訪問(wèn)速度就會(huì)比較慢。

為了解決這個(gè)問(wèn)題,ext4做了一定的改變。它引入了一個(gè)新的概念,叫作Extents。比方說(shuō),一個(gè)文件大小為128M,如果使用4k大小的塊進(jìn)行存儲(chǔ),需要32k個(gè)塊。如果按照ext2或者ext3那樣散著放,數(shù)量太大了。但是Extents可以用于存放連續(xù)的塊,也就是說(shuō),我們可以把128M放在一個(gè)Extents里面。這樣的話,對(duì)大文件的讀寫(xiě)性能提高了,文件碎片也減少了。

Exents是一個(gè)樹(shù)狀結(jié)構(gòu):

e43d0194-aa86-11ed-bfe3-dac502259ad0.jpg

每個(gè)節(jié)點(diǎn)都有一個(gè)頭,ext4_extent_header可以用來(lái)描述某個(gè)節(jié)點(diǎn)。

structext4_extent_header{
__le16eh_magic;/*probablywillsupportdifferentformats*/
__le16eh_entries;/*numberofvalidentries*/
__le16eh_max;/*capacityofstoreinentries*/
__le16eh_depth;/*hastreerealunderlyingblocks?*/
__le32eh_generation;/*generationofthetree*/
};

eh_entries表示這個(gè)節(jié)點(diǎn)里面有多少項(xiàng)。這里的項(xiàng)分兩種,如果是葉子節(jié)點(diǎn),這一項(xiàng)會(huì)直接指向硬盤(pán)上的連續(xù)塊的地址,我們稱為數(shù)據(jù)節(jié)點(diǎn)ext4_extent;如果是分支節(jié)點(diǎn),這一項(xiàng)會(huì)指向下一層的分支節(jié)點(diǎn)或者葉子節(jié)點(diǎn),我們稱為索引節(jié)點(diǎn)ext4_extent_idx。這兩種類型的項(xiàng)的大小都是12個(gè)byte。

/*
*Thisistheextenton-diskstructure.
*It'susedatthebottomofthetree.
*/
structext4_extent{
__le32ee_block;/*firstlogicalblockextentcovers*/
__le16ee_len;/*numberofblockscoveredbyextent*/
__le16ee_start_hi;/*high16bitsofphysicalblock*/
__le32ee_start_lo;/*low32bitsofphysicalblock*/
};
/*
*Thisisindexon-diskstructure.
*It'susedatallthelevelsexceptthebottom.
*/
structext4_extent_idx{
__le32ei_block;/*indexcoverslogicalblocksfrom'block'*/
__le32ei_leaf_lo;/*pointertothephysicalblockofthenext*
*level.leafornextindexcouldbethere*/
__le16ei_leaf_hi;/*high16bitsofphysicalblock*/
__u16ei_unused;
};如果文件不大,inode里面的i_block中,可以放得下一個(gè)ext4_extent_header和4項(xiàng)ext4_extent。所以這個(gè)時(shí)候,eh_depth為0,也即inode里面的就是葉子節(jié)點(diǎn),樹(shù)高度為0。

如果文件比較大,4個(gè)extent放不下,就要分裂成為一棵樹(shù),eh_depth>0的節(jié)點(diǎn)就是索引節(jié)點(diǎn),其中根節(jié)點(diǎn)深度最大,在inode中。最底層eh_depth=0的是葉子節(jié)點(diǎn)。

除了根節(jié)點(diǎn),其他的節(jié)點(diǎn)都保存在一個(gè)塊4k里面,4k扣除ext4_extent_header的12個(gè)byte,剩下的能夠放340項(xiàng),每個(gè)extent最大能表示128MB的數(shù)據(jù),340個(gè)extent會(huì)使你的表示的文件達(dá)到42.5GB。

inode位圖和塊位圖

inode的位圖大小為4k,每一位對(duì)應(yīng)一個(gè)inode。如果是1,表示這個(gè)inode已經(jīng)被用了;如果是0,則表示沒(méi)被用。block的位圖同理。

在Linux操作系統(tǒng)里面,想要?jiǎng)?chuàng)建一個(gè)新文件,會(huì)調(diào)用open函數(shù),并且參數(shù)會(huì)有O_CREAT。這表示當(dāng)文件找不到的時(shí)候,我們就需要?jiǎng)?chuàng)建一個(gè)。那么open函數(shù)的調(diào)用過(guò)程大致是:要打開(kāi)一個(gè)文件,先要根據(jù)路徑找到文件夾。如果發(fā)現(xiàn)文件夾下面沒(méi)有這個(gè)文件,同時(shí)又設(shè)置了O_CREAT,就說(shuō)明我們要在這個(gè)文件夾下面創(chuàng)建一個(gè)文件。

創(chuàng)建一個(gè)文件,那么就需要?jiǎng)?chuàng)建一個(gè)inode,那么就會(huì)從文件系統(tǒng)里面讀取inode位圖,然后找到下一個(gè)為0的inode,就是空閑的inode。對(duì)于block位圖,在寫(xiě)入文件的時(shí)候,也會(huì)有這個(gè)過(guò)程。

文件系統(tǒng)的格式 數(shù)據(jù)塊的位圖是放在一個(gè)塊里面的,共4k。每位表示一個(gè)數(shù)據(jù)塊,共可以表示4?1024?8=215個(gè)數(shù)據(jù)塊。如果每個(gè)數(shù)據(jù)塊也是按默認(rèn)的4K,最大可以表示空間為215?4?1024=227個(gè)byte,也就是128M,那么顯然是不夠的。

這個(gè)時(shí)候就需要用到塊組,數(shù)據(jù)結(jié)構(gòu)為ext4_group_desc,這里面對(duì)于一個(gè)塊組里的inode位圖bg_inode_bitmap_lo、塊位圖bg_block_bitmap_lo、inode列表bg_inode_table_lo,都有相應(yīng)的成員變量。

這樣一個(gè)個(gè)塊組,就基本構(gòu)成了我們整個(gè)文件系統(tǒng)的結(jié)構(gòu)。因?yàn)閴K組有多個(gè),塊組描述符也同樣組成一個(gè)列表,我們把這些稱為塊組描述符表。

我們還需要有一個(gè)數(shù)據(jù)結(jié)構(gòu),對(duì)整個(gè)文件系統(tǒng)的情況進(jìn)行描述,這個(gè)就是超級(jí)塊ext4_super_block。里面有整個(gè)文件系統(tǒng)一共有多少inode,s_inodes_count;一共有多少塊,s_blocks_count_lo,每個(gè)塊組有多少inode,s_inodes_per_group,每個(gè)塊組有多少塊,s_blocks_per_group等。這些都是這類的全局信息。

最終,整個(gè)文件系統(tǒng)格式就是下面這個(gè)樣子。

e4581966-aa86-11ed-bfe3-dac502259ad0.jpg

默認(rèn)情況下,超級(jí)塊和塊組描述符表都有副本保存在每一個(gè)塊組里面。防止這些數(shù)據(jù)丟失了,導(dǎo)致整個(gè)文件系統(tǒng)都打不開(kāi)了。

由于如果每個(gè)塊組里面都保存一份完整的塊組描述符表,一方面很浪費(fèi)空間;另一個(gè)方面,由于一個(gè)塊組最大128M,而塊組描述符表里面有多少項(xiàng),這就限制了有多少個(gè)塊組,128M * 塊組的總數(shù)目是整個(gè)文件系統(tǒng)的大小,就被限制住了。

因此引入Meta Block Groups特性。

首先,塊組描述符表不會(huì)保存所有塊組的描述符了,而是將塊組分成多個(gè)組,我們稱為元塊組(Meta Block Group)。每個(gè)元塊組里面的塊組描述符表僅僅包括自己的,一個(gè)元塊組包含64個(gè)塊組,這樣一個(gè)元塊組中的塊組描述符表最多64項(xiàng)。

我們假設(shè)一共有256個(gè)塊組,原來(lái)是一個(gè)整的塊組描述符表,里面有256項(xiàng),要備份就全備份,現(xiàn)在分成4個(gè)元塊組,每個(gè)元塊組里面的塊組描述符表就只有64項(xiàng)了,這就小多了,而且四個(gè)元塊組自己備份自己的。

e470ecca-aa86-11ed-bfe3-dac502259ad0.jpg

根據(jù)圖中,每一個(gè)元塊組包含64個(gè)塊組,塊組描述符表也是64項(xiàng),備份三份,在元塊組的第一個(gè),第二個(gè)和最后一個(gè)塊組的開(kāi)始處。

如果開(kāi)啟了sparse_super特性,超級(jí)塊和塊組描述符表的副本只會(huì)保存在塊組索引為0、3、5、7的整數(shù)冪里。所以上圖的超級(jí)塊只在索引為0、3、5、7等的整數(shù)冪里。

目錄的存儲(chǔ)格式

其實(shí)目錄本身也是個(gè)文件,也有inode。inode里面也是指向一些塊。和普通文件不同的是,普通文件的塊里面保存的是文件數(shù)據(jù),而目錄文件的塊里面保存的是目錄里面一項(xiàng)一項(xiàng)的文件信息。這些信息我們稱為ext4_dir_entry。

在目錄文件的塊中,最簡(jiǎn)單的保存格式是列表,每一項(xiàng)都會(huì)保存這個(gè)目錄的下一級(jí)的文件的文件名和對(duì)應(yīng)的inode,通過(guò)這個(gè)inode,就能找到真正的文件。第一項(xiàng)是“.”,表示當(dāng)前目錄,第二項(xiàng)是“…”,表示上一級(jí)目錄,接下來(lái)就是一項(xiàng)一項(xiàng)的文件名和inode。

如果在inode中設(shè)置EXT4_INDEX_FL標(biāo)志,那么就表示根據(jù)索引查找文件。索引項(xiàng)會(huì)維護(hù)一個(gè)文件名的哈希值和數(shù)據(jù)塊的一個(gè)映射關(guān)系。

如果我們要查找一個(gè)目錄下面的文件名,可以通過(guò)名稱取哈希。如果哈希能夠匹配上,就說(shuō)明這個(gè)文件的信息在相應(yīng)的塊里面。然后打開(kāi)這個(gè)塊,如果里面不再是索引,而是索引樹(shù)的葉子節(jié)點(diǎn)的話,那里面還是ext4_dir_entry的列表,我們只要一項(xiàng)一項(xiàng)找文件名就行。通過(guò)索引樹(shù),我們可以將一個(gè)目錄下面的N多的文件分散到很多的塊里面,可以很快地進(jìn)行查找。

e487466e-aa86-11ed-bfe3-dac502259ad0.jpg

Linux中的文件緩存

ext4文件系統(tǒng)層

對(duì)于ext4文件系統(tǒng)來(lái)講,內(nèi)核定義了一個(gè)ext4_file_operations。

conststructfile_operationsext4_file_operations={
......
.read_iter=ext4_file_read_iter,
.write_iter=ext4_file_write_iter,
......
}

ext4_file_read_iter會(huì)調(diào)用generic_file_read_iter,ext4_file_write_iter會(huì)調(diào)用__generic_file_write_iter。

ssize_t
generic_file_read_iter(structkiocb*iocb,structiov_iter*iter)
{
......
if(iocb->ki_flags&IOCB_DIRECT){
......
structaddress_space*mapping=file->f_mapping;
......
retval=mapping->a_ops->direct_IO(iocb,iter);
}
......
retval=generic_file_buffered_read(iocb,iter,retval);
}


ssize_t__generic_file_write_iter(structkiocb*iocb,structiov_iter*from)
{
......
if(iocb->ki_flags&IOCB_DIRECT){
......
written=generic_file_direct_write(iocb,from);
......
}else{
......
written=generic_perform_write(file,from,iocb->ki_pos);
......
}
}generic_file_read_iter和__generic_file_write_iter有相似的邏輯,就是要區(qū)分是否用緩存。因此,根據(jù)是否使用內(nèi)存做緩存,我們可以把文件的I/O操作分為兩種類型。

第一種類型是緩存I/O。大多數(shù)文件系統(tǒng)的默認(rèn)I/O操作都是緩存I/O。對(duì)于讀操作來(lái)講,操作系統(tǒng)會(huì)先檢查,內(nèi)核的緩沖區(qū)有沒(méi)有需要的數(shù)據(jù)。如果已經(jīng)緩存了,那就直接從緩存中返回;否則從磁盤(pán)中讀取,然后緩存在操作系統(tǒng)的緩存中。對(duì)于寫(xiě)操作來(lái)講,操作系統(tǒng)會(huì)先將數(shù)據(jù)從用戶空間復(fù)制到內(nèi)核空間的緩存中。這時(shí)對(duì)用戶程序來(lái)說(shuō),寫(xiě)操作就已經(jīng)完成。至于什么時(shí)候再寫(xiě)到磁盤(pán)中由操作系統(tǒng)決定,除非顯式地調(diào)用了sync同步命令。

第二種類型是直接IO,就是應(yīng)用程序直接訪問(wèn)磁盤(pán)數(shù)據(jù),而不經(jīng)過(guò)內(nèi)核緩沖區(qū),從而減少了在內(nèi)核緩存和用戶程序之間數(shù)據(jù)復(fù)制。

如果在寫(xiě)的邏輯__generic_file_write_iter里面,發(fā)現(xiàn)設(shè)置了IOCB_DIRECT,則調(diào)用generic_file_direct_write,里面同樣會(huì)調(diào)用address_space的direct_IO的函數(shù),將數(shù)據(jù)直接寫(xiě)入硬盤(pán)。

帶緩存的寫(xiě)入操作

我們先來(lái)看帶緩存寫(xiě)入的函數(shù)generic_perform_write。

ssize_tgeneric_perform_write(structfile*file,
structiov_iter*i,loff_tpos)
{
structaddress_space*mapping=file->f_mapping;
conststructaddress_space_operations*a_ops=mapping->a_ops;
do{
structpage*page;
unsignedlongoffset;/*Offsetintopagecachepage*/
unsignedlongbytes;/*Bytestowritetopage*/
status=a_ops->write_begin(file,mapping,pos,bytes,flags,
&page,&fsdata);
copied=iov_iter_copy_from_user_atomic(page,i,offset,bytes);
flush_dcache_page(page);
status=a_ops->write_end(file,mapping,pos,bytes,copied,
page,fsdata);
pos+=copied;
written+=copied;


balance_dirty_pages_ratelimited(mapping);
}while(iov_iter_count(i));
}

循環(huán)中主要做了這幾件事:

對(duì)于每一頁(yè),先調(diào)用address_space的write_begin做一些準(zhǔn)備;

調(diào)用iov_iter_copy_from_user_atomic,將寫(xiě)入的內(nèi)容從用戶態(tài)拷貝到內(nèi)核態(tài)的頁(yè)中;

調(diào)用address_space的write_end完成寫(xiě)操作;

調(diào)用balance_dirty_pages_ratelimited,看臟頁(yè)是否太多,需要寫(xiě)回硬盤(pán)。所謂臟頁(yè),就是寫(xiě)入到緩存,但是還沒(méi)有寫(xiě)入到硬盤(pán)的頁(yè)面。

對(duì)于第一步,調(diào)用的是ext4_write_begin來(lái)說(shuō),主要做兩件事:

第一做日志相關(guān)的工作。

ext4是一種日志文件系統(tǒng),是為了防止突然斷電的時(shí)候的數(shù)據(jù)丟失,引入了日志(Journal)模式。日志文件系統(tǒng)比非日志文件系統(tǒng)多了一個(gè)Journal區(qū)域。文件在ext4中分兩部分存儲(chǔ),一部分是文件的元數(shù)據(jù),另一部分是數(shù)據(jù)。元數(shù)據(jù)和數(shù)據(jù)的操作日志Journal也是分開(kāi)管理的。你可以在掛載ext4的時(shí)候,選擇Journal模式。這種模式在將數(shù)據(jù)寫(xiě)入文件系統(tǒng)前,必須等待元數(shù)據(jù)和數(shù)據(jù)的日志已經(jīng)落盤(pán)才能發(fā)揮作用。這樣性能比較差,但是最安全。

另一種模式是order模式。這個(gè)模式不記錄數(shù)據(jù)的日志,只記錄元數(shù)據(jù)的日志,但是在寫(xiě)元數(shù)據(jù)的日志前,必須先確保數(shù)據(jù)已經(jīng)落盤(pán)。這個(gè)折中,是默認(rèn)模式。

還有一種模式是writeback,不記錄數(shù)據(jù)的日志,僅記錄元數(shù)據(jù)的日志,并且不保證數(shù)據(jù)比元數(shù)據(jù)先落盤(pán)。這個(gè)性能最好,但是最不安全。

第二調(diào)用grab_cache_page_write_begin來(lái),得到應(yīng)該寫(xiě)入的緩存頁(yè)。

structpage*grab_cache_page_write_begin(structaddress_space*mapping,
pgoff_tindex,unsignedflags)
{
structpage*page;
intfgp_flags=FGP_LOCK|FGP_WRITE|FGP_CREAT;
page=pagecache_get_page(mapping,index,fgp_flags,
mapping_gfp_mask(mapping));
if(page)
wait_for_stable_page(page);
returnpage;
}

在內(nèi)核中,緩存以頁(yè)為單位放在內(nèi)存里面,每一個(gè)打開(kāi)的文件都有一個(gè)struct file結(jié)構(gòu),每個(gè)struct file結(jié)構(gòu)都有一個(gè)struct address_space用于關(guān)聯(lián)文件和內(nèi)存,就是在這個(gè)結(jié)構(gòu)里面,有一棵樹(shù),用于保存所有與這個(gè)文件相關(guān)的的緩存頁(yè)。

對(duì)于第二步,調(diào)用iov_iter_copy_from_user_atomic。先將分配好的頁(yè)面調(diào)用kmap_atomic映射到內(nèi)核里面的一個(gè)虛擬地址,然后將用戶態(tài)的數(shù)據(jù)拷貝到內(nèi)核態(tài)的頁(yè)面的虛擬地址中,調(diào)用kunmap_atomic把內(nèi)核里面的映射刪除。

size_tiov_iter_copy_from_user_atomic(structpage*page,
structiov_iter*i,unsignedlongoffset,size_tbytes)
{
char*kaddr=kmap_atomic(page),*p=kaddr+offset;
iterate_all_kinds(i,bytes,v,
copyin((p+=v.iov_len)-v.iov_len,v.iov_base,v.iov_len),
memcpy_from_page((p+=v.bv_len)-v.bv_len,v.bv_page,
v.bv_offset,v.bv_len),
memcpy((p+=v.iov_len)-v.iov_len,v.iov_base,v.iov_len)
)
kunmap_atomic(kaddr);
returnbytes;
}

第三步中,調(diào)用ext4_write_end完成寫(xiě)入。這里面會(huì)調(diào)用ext4_journal_stop完成日志的寫(xiě)入,會(huì)調(diào)用block_write_end->__block_commit_write->mark_buffer_dirty,將修改過(guò)的緩存標(biāo)記為臟頁(yè)??梢钥闯?,其實(shí)所謂的完成寫(xiě)入,并沒(méi)有真正寫(xiě)入硬盤(pán),僅僅是寫(xiě)入緩存后,標(biāo)記為臟頁(yè)。

第四步,調(diào)用 balance_dirty_pages_ratelimited,是回寫(xiě)臟頁(yè)。

/**
*balance_dirty_pages_ratelimited-balancedirtymemorystate
*@mapping:address_spacewhichwasdirtied
*
*Processeswhicharedirtyingmemoryshouldcallinhereonceforeachpage
*whichwasnewlydirtied.Thefunctionwillperiodicallycheckthesystem's
*dirtystateandwillinitiatewritebackifneeded.
*/
voidbalance_dirty_pages_ratelimited(structaddress_space*mapping)
{
structinode*inode=mapping->host;
structbacking_dev_info*bdi=inode_to_bdi(inode);
structbdi_writeback*wb=NULL;
intratelimit;
......
if(unlikely(current->nr_dirtied>=ratelimit))
balance_dirty_pages(mapping,wb,current->nr_dirtied);
......
}

在balance_dirty_pages_ratelimited里面,發(fā)現(xiàn)臟頁(yè)的數(shù)目超過(guò)了規(guī)定的數(shù)目,就調(diào)用balance_dirty_pages->wb_start_background_writeback,啟動(dòng)一個(gè)背后線程開(kāi)始回寫(xiě)。

另外還有幾種場(chǎng)景也會(huì)觸發(fā)回寫(xiě):

用戶主動(dòng)調(diào)用sync,將緩存刷到硬盤(pán)上去,最終會(huì)調(diào)用wakeup_flusher_threads,同步臟頁(yè);

當(dāng)內(nèi)存十分緊張,以至于無(wú)法分配頁(yè)面的時(shí)候,會(huì)調(diào)用free_more_memory,最終會(huì)調(diào)用wakeup_flusher_threads,釋放臟頁(yè);

臟頁(yè)已經(jīng)更新了較長(zhǎng)時(shí)間,時(shí)間上超過(guò)了設(shè)定時(shí)間,需要及時(shí)回寫(xiě),保持內(nèi)存和磁盤(pán)上數(shù)據(jù)一致性。

帶緩存的讀操作

看帶緩存的讀,對(duì)應(yīng)的是函數(shù)generic_file_buffered_read。

staticssize_tgeneric_file_buffered_read(structkiocb*iocb,
structiov_iter*iter,ssize_twritten)
{
structfile*filp=iocb->ki_filp;
structaddress_space*mapping=filp->f_mapping;
structinode*inode=mapping->host;
for(;;){
structpage*page;
pgoff_tend_index;
loff_tisize;
page=find_get_page(mapping,index);
if(!page){
if(iocb->ki_flags&IOCB_NOWAIT)
gotowould_block;
page_cache_sync_readahead(mapping,
ra,filp,
index,last_index-index);
page=find_get_page(mapping,index);
if(unlikely(page==NULL))
gotono_cached_page;
}
if(PageReadahead(page)){
page_cache_async_readahead(mapping,
ra,filp,page,
index,last_index-index);
}
/*
*Ok,wehavethepage,andit'sup-to-date,so
*nowwecancopyittouserspace...
*/
ret=copy_page_to_iter(page,offset,nr,iter);
}
}

在generic_file_buffered_read函數(shù)中,我們需要先找到page cache里面是否有緩存頁(yè)。如果沒(méi)有找到,不但讀取這一頁(yè),還要進(jìn)行預(yù)讀,這需要在page_cache_sync_readahead函數(shù)中實(shí)現(xiàn)。預(yù)讀完了以后,再試一把查找緩存頁(yè)。

如果第一次找緩存頁(yè)就找到了,我們還是要判斷,是不是應(yīng)該繼續(xù)預(yù)讀;如果需要,就調(diào)用page_cache_async_readahead發(fā)起一個(gè)異步預(yù)讀。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 存儲(chǔ)
    +關(guān)注

    關(guān)注

    13

    文章

    4122

    瀏覽量

    85271
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11123

    瀏覽量

    207891
  • 文件系統(tǒng)
    +關(guān)注

    關(guān)注

    0

    文章

    278

    瀏覽量

    19830

原文標(biāo)題:Linux 的文件系統(tǒng)及文件緩存知識(shí)點(diǎn)整理

文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Linux文件系統(tǒng)知識(shí)點(diǎn)詳解

    今天浩道跟大家分享關(guān)于Linux文件及目錄屬性知識(shí)點(diǎn)的硬核干貨,可以說(shuō)只要你認(rèn)真看完這篇文章內(nèi)容,其相關(guān)知識(shí)點(diǎn)都不在話下,感興趣又想快速掌握的小伙伴們,可以收藏起來(lái)隨時(shí)查看!
    的頭像 發(fā)表于 11-02 09:29 ?555次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>文件系統(tǒng)</b><b class='flag-5'>知識(shí)點(diǎn)</b>詳解

    關(guān)于proc文件系統(tǒng)知識(shí)點(diǎn)總結(jié)的太棒了

    關(guān)于proc文件系統(tǒng)知識(shí)點(diǎn)總結(jié)的太棒了
    發(fā)表于 04-28 06:50

    Linux文件系統(tǒng)課程

    本章學(xué)習(xí)目標(biāo)理解什么是文件系統(tǒng)了解文件系統(tǒng)工作原理理解Fedora Core Linux文件系統(tǒng)的結(jié)構(gòu)掌握Fedora Core Linux
    發(fā)表于 04-10 17:07 ?0次下載

    Linux文件系統(tǒng)簡(jiǎn)介

    Linux文件系統(tǒng)簡(jiǎn)介 什么是根文件   根文件系統(tǒng)首先是一種文件系統(tǒng),但是相對(duì)于普通的文件系統(tǒng)
    發(fā)表于 04-21 17:01 ?5083次閱讀

    玩轉(zhuǎn)Linux,先把文件系統(tǒng)搞懂

    Linux 支持多種文件系統(tǒng),包括 ext2 、 ext3 、 vfat 、 ntfs 、 iso9660 、 jffs 、 romfs 和 nfs 等,為了對(duì)各類文件系統(tǒng)進(jìn)行統(tǒng)一管理, Li
    發(fā)表于 08-16 10:50 ?2009次閱讀
    玩轉(zhuǎn)<b class='flag-5'>Linux</b>,先把<b class='flag-5'>文件系統(tǒng)</b>搞懂

    Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解》第5章、Linux文件系統(tǒng)與設(shè)備文件系統(tǒng)

    Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解》第5章、Linux文件系統(tǒng)與設(shè)備文件系統(tǒng)
    發(fā)表于 10-27 14:13 ?0次下載
    《<b class='flag-5'>Linux</b>設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解》第5章、<b class='flag-5'>Linux</b><b class='flag-5'>文件系統(tǒng)</b>與設(shè)備<b class='flag-5'>文件系統(tǒng)</b>

    文件系統(tǒng)是什么?淺談EXT文件系統(tǒng)歷史

    在先前關(guān)于Linux文件系統(tǒng)的文章中,我很想去深入地討論更多EXT文件系統(tǒng)的特性的信息。所以,首先讓我們來(lái)回答這個(gè)問(wèn)題:什么是文件系統(tǒng)?一個(gè)文件系統(tǒng)
    發(fā)表于 06-28 09:03 ?5560次閱讀
    <b class='flag-5'>文件系統(tǒng)</b>是什么?淺談EXT<b class='flag-5'>文件系統(tǒng)</b>歷史

    Linux 內(nèi)核/sys 文件系統(tǒng)介紹

    linux2.6內(nèi)核引入sysfs文件系統(tǒng),sysfs可以看成與proc,devfs和devpty同類別的文件系統(tǒng),該文件系統(tǒng)是虛擬的文件系統(tǒng)
    發(fā)表于 04-25 16:20 ?4209次閱讀
    <b class='flag-5'>Linux</b> 內(nèi)核/sys <b class='flag-5'>文件系統(tǒng)</b>介紹

    可以了解的Linux 文件系統(tǒng)結(jié)構(gòu)

    Linux中的文件是什么?它的文件系統(tǒng)又是什么?那些配置文件又在哪里?我下載好的程序保存在哪里了?在 Linux
    發(fā)表于 04-27 14:06 ?669次閱讀
    可以了解的<b class='flag-5'>Linux</b> <b class='flag-5'>文件系統(tǒng)</b>結(jié)構(gòu)

    Linux最新UBI文件系統(tǒng)介紹

    嵌入式linux中文站關(guān)注嵌入式linux文件系統(tǒng)的發(fā)展。在linux-2.6.27以前,談到Flash文件系統(tǒng),大家很多時(shí)候多會(huì)想到cra
    發(fā)表于 04-27 19:37 ?6319次閱讀

    linux文件系統(tǒng)中的虛擬文件系統(tǒng)設(shè)計(jì)詳解

    Linux 中允許眾多不同的文件系統(tǒng)共存,如 ext2, ext3, vfat 等。通過(guò)使用同一套文件 I/O 系統(tǒng) 調(diào)用即可對(duì) Linux
    發(fā)表于 04-02 14:37 ?1722次閱讀
    <b class='flag-5'>linux</b><b class='flag-5'>文件系統(tǒng)</b>中的虛擬<b class='flag-5'>文件系統(tǒng)</b>設(shè)計(jì)詳解

    Linux文件系統(tǒng)解析

    Linux 中,最直觀、最可見(jiàn)的部分就是 文件系統(tǒng)(file system)。下面我們就來(lái)一起探討一下關(guān)于 Linux 中國(guó)的文件系統(tǒng),系統(tǒng)
    的頭像 發(fā)表于 09-16 11:29 ?2367次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>文件系統(tǒng)</b>解析

    rootfs根文件系統(tǒng)制作

    本文記錄了根文件系統(tǒng)的一些知識(shí)點(diǎn),Busybox 工具的使用和 最小根文件系統(tǒng)的制作。
    發(fā)表于 10-31 11:56 ?2次下載

    適用于Linux的最佳通用文件系統(tǒng) Linux文件系統(tǒng)的安裝

    為您的計(jì)算機(jī)選擇正確的文件系統(tǒng)可能是一個(gè)困難的過(guò)程。您可能會(huì)想知道:為什么文件系統(tǒng)很重要?有沒(méi)有適用于安裝 Linux 的特定文件系統(tǒng)? 事實(shí)證明,有兩種
    發(fā)表于 08-03 10:22 ?287次閱讀
    適用于<b class='flag-5'>Linux</b>的最佳通用<b class='flag-5'>文件系統(tǒng)</b> <b class='flag-5'>Linux</b><b class='flag-5'>文件系統(tǒng)</b>的安裝

    Linux文件系統(tǒng)特點(diǎn)

    ,近期經(jīng)常被讀取和寫(xiě)入,文件系統(tǒng)應(yīng)該有緩存層。 文件應(yīng)該用文件夾的形式組織起來(lái),方便管理和查詢。 Linux內(nèi)核要在自己的內(nèi)存里面維護(hù)一套數(shù)
    的頭像 發(fā)表于 11-09 14:48 ?976次閱讀
    <b class='flag-5'>Linux</b>的<b class='flag-5'>文件系統(tǒng)</b>特點(diǎn)