若設(shè)置信號標(biāo)記,act.sa_flags |= SA_RESTART,接收信號,read阻塞不返回,但是信號響應(yīng)函數(shù)還是會調(diào)用;相當(dāng)于系統(tǒng)自動重新進(jìn)入阻塞。
實(shí)際業(yè)務(wù)中,一般有多個IO請求,每個請求響應(yīng)都用簡單的阻塞模型效率太低,Linux提供了多路復(fù)用的的系統(tǒng)調(diào)用:
當(dāng)IO數(shù)量不多時,可以用select或epoll,但當(dāng)IO非常多時,比如大型網(wǎng)絡(luò)應(yīng)用,響應(yīng)多個IO請求時,用epoll效率遠(yuǎn)高于select。
比如讀觸摸屏應(yīng)用,read被阻塞,只有觸摸屏被按下,觸發(fā)中斷程序響應(yīng),讀取觸摸屏行為數(shù)據(jù)后,內(nèi)核發(fā)送信號喚醒APP的等待,APP讀到觸摸動作信息,做相應(yīng)業(yè)務(wù)處理。
(1) C庫提供的Glibc-AIO:Glibc-AIO原理,aio_read()立即返回,后臺自動創(chuàng)建線程讀取io,aio_suspend()查詢IO是否完成,完成立即返回,未完成,等待;
【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【865977150】整理了一些個人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。?/blockquote>資料直通車:最新Linux內(nèi)核源碼資料文檔+視頻資料
內(nèi)核學(xué)習(xí)地址:Linux內(nèi)核源碼/內(nèi)存調(diào)優(yōu)/文件系統(tǒng)/進(jìn)程管理/設(shè)備驅(qū)動/網(wǎng)絡(luò)協(xié)議棧
二、EXT文件系統(tǒng)
一切都是文件,Linux通過VFS中間層,支持多種文件系統(tǒng),對APP統(tǒng)一接口,文件系統(tǒng)的本質(zhì)是將用戶數(shù)據(jù)和元數(shù)據(jù)(管理數(shù)據(jù)的數(shù)據(jù)),組織成有序的目錄結(jié)構(gòu)。
2.1EXT2文件系統(tǒng)總體存儲布局
一個磁盤可以劃為多個分區(qū),每個分區(qū)必須先用格式化工具(某種mkfs)格式化成某種格式的文件系統(tǒng),然后才能存儲文件,格式化的過程會在磁盤上寫一些管理存儲布局信息。
一個典型的ext格式化文件系統(tǒng)存儲布局如下:
啟動塊(BootBlock):大小1K,是由PC標(biāo)準(zhǔn)規(guī)定的,用來存儲磁盤分區(qū)信息和啟動信息,任何文件系統(tǒng)都不能使用啟動塊。啟動塊之后才是EXT文件系統(tǒng)的開始;
超級塊(Superblock):描述整個分區(qū)的文件系統(tǒng)信息,比如塊大小,文件系統(tǒng)版本號,上次mount時間等;
超級塊在每個塊組的開頭都有一份拷貝;
塊組描述符表(GDT, Group Descriptor Table)
由很多塊組描述符(Group Descriptor) 組成,整個分區(qū)分成多少個塊組就對應(yīng)多少個GD;
每個GD存儲一個塊組的描述信息,比如這個塊組從哪里開始是inode表,哪里開始是數(shù)據(jù)塊,空閑的inode和數(shù)據(jù)塊還有多少個等。
和超級塊類似,GDT在每個塊組開頭也有一份拷貝;
塊位圖(Block Bitmap)
用來描述整個塊組中那些塊空閑,本身占用一個塊,每個bit代表本塊組的一個塊,bit為1表示對應(yīng)塊被占用,0表示空閑;
tips
df命令統(tǒng)計(jì)整個磁盤空間非??欤?yàn)橹恍枰榭疵總€塊組的塊位圖即可;
du命令查看一個較大目錄會很慢,因?yàn)樾枰阉髡麄€目錄的所有文件;
inode位圖(inode Bitmap)
和塊位圖類似,本身占用一個塊,每個bit表示一個inode是否空閑可用;
inode表(inode Table)
每個文件對應(yīng)一個inode,用來描述文件類型,權(quán)限,大小,創(chuàng)建/修改/訪問時間等信息;
一個塊組中的所有inode組成了inode表;
Inode表占用多少個塊,格式化時就要確定,mke2fs工具默認(rèn)策略是每8K分配一個inode。
就是說當(dāng)文件全部是8K時,inode表會充分利用,當(dāng)文件過大,inode表會浪費(fèi),文件過小,inode不夠用;
硬鏈接指向同一個inode;
數(shù)據(jù)塊(Data Block)
a.常規(guī)文件:
文件的數(shù)據(jù)存儲在數(shù)據(jù)塊中;
b.目錄
該目錄下所有文件名和目錄名存儲在數(shù)據(jù)塊中;
文件名保存在目錄的數(shù)據(jù)塊中,ls –l看到的其他信息保存在該文件的inode中;
目錄也是文件,是一種特殊類型的文件;
c.符號鏈接
如果目標(biāo)路徑名較短,直接保存在inode中以便查找,如果過長,分配一個數(shù)據(jù)塊保存。
d.設(shè)備文件、FIFO和socket等特殊文件
沒有數(shù)據(jù)塊,設(shè)備文件的主,次設(shè)備號保存在inode中。
2.2實(shí)例解析文件系統(tǒng)結(jié)構(gòu):
用一個文件來模擬一個磁盤;
1.創(chuàng)建一個1M文件,內(nèi)容全是0
dd if=/dev/zero of=fs count=256 bs=4k
2.對文件fs格式化
格式化后的fs文件大小依然是1M,但內(nèi)容已經(jīng)不是全零。
3.用dumpe2fs工具查看這個分區(qū)的超級塊和塊組描述表信息
(base) leon\@pc:\~/nfs/linux\$ dumpe2fs fs
dumpe2fs 1.42.13 (17-May-2015)
Filesystem volume name: <none>
Last mounted on: <not available>
Filesystem UUID: a00715b2-528b-4ca6-8c2b-953389a5ab00
Filesystem magic number: 0xEF53
Filesystem revision #: 1 (dynamic)
Filesystem features: ext_attr resize_inode dir_index filetype sparse_super
large_file
Filesystem flags: signed_directory_hash
Default mount options: user_xattr acl
Filesystem state: clean
Errors behavior: Continue
Filesystem OS type: Linux
Inode count: 128
Block count: 1024
Reserved block count: 51
Free blocks: 986
Free inodes: 117
First block: 1
Block size: 1024
Fragment size: 1024
Reserved GDT blocks: 3
Blocks per group: 8192
Fragments per group: 8192
Inodes per group: 128
Inode blocks per group: 16
Filesystem created: Fri Aug 21 16:48:02 2020
Last mount time: n/a
Last write time: Fri Aug 21 16:48:02 2020
Mount count: 0
Maximum mount count: -1
Last checked: Fri Aug 21 16:48:02 2020
Check interval: 0 (<none>)
Reserved blocks uid: 0 (user root)
Reserved blocks gid: 0 (group root)
First inode: 11
Inode size: 128
Default directory hash: half_md4
Directory Hash Seed: e5c519af-d42e-43b5-bc8d-c67c5a79bcbe
Group 0: (Blocks 1-1023)
主 superblock at 1, Group descriptors at 2-2
保留的GDT塊位于 3-5
Block bitmap at 6 (+5), Inode bitmap at 7 (+6)
Inode表位于 8-23 (+7)
986 free blocks, 117 free inodes, 2 directories
可用塊數(shù): 38-1023
可用inode數(shù): 12-128
(base) leon\@pc:\~/nfs/linux\$
塊大小1024字節(jié),一共1024個塊,第0塊是啟動塊,第一個塊開始才是EXT2文件系統(tǒng),
Group0占據(jù)1到1023個塊,共1023個塊。
超級塊在塊1,GDT2,預(yù)留3-5,
塊位圖在塊6,占用一個塊,1024x8=8192bit,足夠表示1023個塊,只需一個塊就夠了;
inode bitmap在塊7
inode表在8-23,占用16個塊,默認(rèn)每8K對應(yīng)一個inode,共1M/8K=128個inode。每個inode占用128字節(jié),128x128=16k
4用普通文件制作的文件系統(tǒng)也可以像磁盤分區(qū)一樣mount到某個目錄
$ sudo mount -o loop fs /mnt/
-o loop選項(xiàng)告訴mount這是一個常規(guī)文件,不是塊設(shè)備,mount會把它的數(shù)據(jù)當(dāng)作分區(qū)格式來解釋;
文件系統(tǒng)格式化后,在根目錄自動生成三個字目錄: ., …, lost+found
Lost+found目錄由e2fsck工具使用,如果在檢查磁盤時發(fā)生錯誤,就把有錯誤的塊掛在這個目錄下。
現(xiàn)在可以在/mnt 讀寫文件,umount卸載后,確保所有改動都保存在fs文件中了。
5.解讀fs二進(jìn)制文件
od –tx1 –Ax fs
開頭行表示省略全零數(shù)據(jù)。
000000開始的1KB是啟動塊,由于不是真正的磁盤,這里全零;
000400到0007ff是1KB的超級塊,對照dumpe2fs輸出信息對比如下:
超級塊:
Ext2各字段按小端存儲。
塊組描述符
整個文件系統(tǒng)1M,每個塊1KB,一共1024個塊,除了啟動塊0,其他1-1023全部屬于group0.
Block1是超級塊,塊位圖Block6,inode位圖Block7,inode表從Block8開始,由于超級塊中指出每個塊組有128inode,每個inode大小128字節(jié),因此共占用16個塊(8-23)從Block24開始就是數(shù)據(jù)塊。
查看塊位圖,6x1024=0x1800
前37位(ff ff ff ff 1f)已經(jīng)被占用,空閑塊是連續(xù)的Block38-1023=986 free blocks
查看inode位圖,7x1024=0x1c00
已用11個inode中,前10個inode是被ext2文件系統(tǒng)保留的,其中第二個inode是根目錄,第11個inode是lost+found目錄。塊組描述符也指出該組有兩個目錄,就是根目錄和lost+found目錄。
解析根目錄的inode,地址Block8*1024+inode2(1*128)=0x2080
st_mode用八進(jìn)制表示,包含了文件類型和權(quán)限,最高位4表示為文件類型目錄,755表示權(quán)限,size是大小,說明根目錄現(xiàn)在只有一個塊。Links=3表示根目錄有三個硬鏈接,分別是根目錄下的
和lost+found字目錄下的,這里的Blockcount是以512字節(jié)為一個塊統(tǒng)計(jì)的,磁盤最小讀寫單位一個扇區(qū)(Sector)512字節(jié),而不是格式化文件系統(tǒng)時指定的塊大小。所以Blockcount是磁盤的物理塊數(shù)量,而不是分區(qū)的邏輯塊數(shù)量。
根據(jù)上圖Block[0]=24塊,在文件系統(tǒng)中24x1024字節(jié)=0x6000,從od命令查找0x6000地址
目錄數(shù)據(jù)塊由許多不定長記錄組成,每條記錄描述該目錄下的一個文件;
記錄1,inode號為2,就是根目錄本身,記錄長12字節(jié),文件名長度1(“.”),類型2;
記錄2,inode號為2,也是根目錄本身,記錄長12字節(jié),文件名長度(“…”),類型2;
記錄3,inode號為11,記錄長1000字節(jié),文件名長度(”lost+found”),類型2;
debugfs命令,不需要mount就可以查看這個文件系統(tǒng)的信息
debugfs fs
stat / cd ls 等
將fs掛載,在根目錄創(chuàng)建一個hello.txt文件,寫入內(nèi)容”hello fs!”,重新解析根目錄
查看塊位圖
可見前38bit被占用,第38塊地址38x1027=0x9800
查看inode位圖,7x1024=0x1c00
由圖知,用掉了12個inode
查看根目錄的數(shù)據(jù)塊內(nèi)容
debug fs查看t.txt屬性
stat t.txt
t.txt文件inode號=12
Inode12的地址=Block8*1024+inode12(11*128)=0x2580
查看t.txt的inode
文件大小10字節(jié)=stlen(“hello fs!”),數(shù)據(jù)塊地址0x26x1024 = 0x9800
查看內(nèi)容
2.3數(shù)據(jù)塊尋址
如果一個文件很大,有多個數(shù)據(jù)塊,這些塊可能不是連續(xù)存放的,那如何尋址所有塊呢?
在上面根目錄數(shù)據(jù)塊是通過inode的索引項(xiàng)Blocks[0]找到的,實(shí)際上這樣的索引項(xiàng)一共有15個,從Blocks[0]到Blocks[14],每個索引項(xiàng)占4字節(jié),前12個索引項(xiàng)都表示塊編號,例如上面Blocks[0]保存塊24,如果塊大小是1KB,這樣就可以表示12KB的文件,剩下的三個索引項(xiàng)Blocks[12]~
Blocks[14],如果也這么用,就只能表示最大15KB文件,這遠(yuǎn)遠(yuǎn)不夠。實(shí)際上剩下的這3個索引項(xiàng)都是間接索引,Blocks[12]所指向的間接尋址塊(Indirect Block),其中存放類似Blocks[0]這種索引,再由索引項(xiàng)指向數(shù)據(jù)塊。假設(shè)塊大小是b,那么一級間接尋址加上之前的12個索引項(xiàng),最大可尋址b/4+12個數(shù)據(jù)塊=1024/4+12=268KB的文件。
同理Blocks[13]作為二級尋址,最大可尋址(b/4)*(b/4)+12=64.26MB,Blocks[14]作為三級尋址,最大可尋址(b/4)*(b/4) *(b/4)+12=16.06GB
可見,這種尋址方式對于訪問不超過12數(shù)據(jù)塊的小文件,是非常快的。訪問任意數(shù)據(jù)只需要兩次讀盤操作,一次讀Inode,一次讀數(shù)據(jù)塊。
而訪問大文件數(shù)據(jù)最多需要5次讀盤操作,inode,一級尋址塊、二級尋址塊、三級尋址塊、數(shù)據(jù)塊。
實(shí)際上磁盤中的inode和數(shù)據(jù)塊往往會被內(nèi)核緩存,讀大文件的效率也不會太低。
在EXT4,支持Extents,其描述連續(xù)數(shù)據(jù)塊的方式,可以節(jié)省元數(shù)據(jù)空間。
2.4文件和目錄操作的系統(tǒng)函數(shù)
Linux提供一些文件和目錄操作的常用系統(tǒng)函數(shù),文件操作命令比如ls,
cp,mv等都是基于這些系統(tǒng)調(diào)用實(shí)現(xiàn)的。
stat:
讀取文件的inode, 把inode中的各種文件屬性填入struct stat結(jié)構(gòu)體返回;
假如讀一個文件/opt/file,其查找順序是:
1.讀出inode表中第2項(xiàng),也就是根目錄的inode,從中找出根目錄數(shù)據(jù)塊的位置
2.從根目錄的數(shù)據(jù)塊中找出文件名為opt的記錄,從記錄中讀出它的inode號
3. 讀出opt目錄的inode,從中找出它的數(shù)據(jù)塊的位置
4. 從opt目錄的數(shù)據(jù)塊中找出文件名為file的記錄,從記錄中讀出它的inode號
5.讀出file文件的inode
還有另外兩個類似stat的函數(shù):fstat(2)函數(shù),lstat(2)函數(shù)
access(2):
檢查執(zhí)行當(dāng)前進(jìn)程的用戶是否有權(quán)限訪問某個文件,access去取出文件inode中的st_mode字段,比較訪問權(quán)限,返回0表示允許訪問,-1不允許。
chmod(2)和fchmod(2):
改變文件的訪問權(quán)限,也就是修改inode中的st_mode字段。
chmod(1)命令是基于chmod(2)實(shí)現(xiàn)的。
chown(2)/fchown(2)/lchown(2):
改變文件的所有者和組,也就是修改inode中的User和Group字段。
utime(2):
改邊文件訪問時間和修改時間,也就是修改inode中的atime和mtime字段。touch(1)命令是基于utime實(shí)現(xiàn)的。
truncate(2)/ftruncate(2):
截?cái)辔募?,修改inode中的Blocks索引項(xiàng)以及塊位圖中的bit.
link(2):
創(chuàng)建硬鏈接,就是在目錄的數(shù)據(jù)塊中添加一條記錄,其中的inode號字段與源文件相同。
syslink(2):
創(chuàng)建符號鏈接,需要創(chuàng)建一個新的inode,其中st_mode字段的文件類型是符號鏈接。指向路徑名,不是inode,替換掉同名文件,符號鏈接依然可以正常訪問。
ln(1)命令是基于link和symlink函數(shù)實(shí)現(xiàn)的。
unlink(2):
刪除一個鏈接,如果是符號鏈接則釋放符號鏈接的inode和數(shù)據(jù)塊,清除inode位圖和塊位圖中相應(yīng)位。如果是硬鏈接,從目錄的數(shù)據(jù)塊中清除文件名記錄,如果當(dāng)前文件的硬鏈接數(shù)已經(jīng)是1,還要刪除它,同時釋放inode和數(shù)據(jù)塊,清除inode位圖和塊位圖相應(yīng)位,這時文件就真的刪除了。
rename(2):
修改文件名,就是修改目錄數(shù)據(jù)塊中的文件名記錄,如果新舊文件名不在一個目錄下,則需要從原目錄數(shù)據(jù)中清楚記錄,然后添加到新目錄的數(shù)據(jù)塊中。mv(1)命令是基于rename實(shí)現(xiàn)的。
readlink(2):
從符號鏈接的inode或數(shù)據(jù)塊中讀出保存的數(shù)據(jù)。
rmdir(2):
刪除一個目錄,目錄必須是空的(只含.和…)才能刪除,釋放它的inode和數(shù)據(jù)塊,清除inode位圖和塊位圖的相應(yīng)位,清除父目錄數(shù)據(jù)塊中的記錄,父目錄的硬鏈接數(shù)減1,rmdir(1)命令是基于rmdir函數(shù)實(shí)現(xiàn)的。
opendir(3)/readdir(3)/closedir(3):
用于遍歷目錄數(shù)據(jù)塊中的記錄。
目錄,是一個特殊的文件,其存放inode號與文件名的映射關(guān)系;
2.5VFS
Linux支持各種文件系統(tǒng)格式,ext2,ext3,ext4,fat,ntfs,yaffs等,內(nèi)核在不同的文件系統(tǒng)格式之上做了一個抽象層,使得文件目錄訪問等概念成為抽象層概念,對APP提供統(tǒng)一訪問接口,由底層驅(qū)動去實(shí)現(xiàn)不同文件系統(tǒng)的差異,這個抽象層叫虛擬文件系統(tǒng)(VFS, Virtual Filesystem)。
File,dentry,inode,super_block這幾個結(jié)構(gòu)體組成了VFS的核心概念。
icache/dcache
訪問過的文件或目錄,內(nèi)核都會做cache;
inode_cachep = kmem_cache_create()
dentry_cache=KMEM_CACHE()
這兩個函數(shù)申請的slab可以回收,內(nèi)存自動釋放;
Linux配置回收優(yōu)先級
(1).free pagecache:
echo 1 >> /proc/sys/vm/drop_caches
(2)free reclaimable slab objects (includes dentries and inodes)
echo 2 >> /proc/sys/vm/drop_caches
(3)free slab objects and pagecache:
echo 3 >> /proc/sys/vm/drop_caches
fuse
Linux支持用戶空間實(shí)現(xiàn)文件系統(tǒng),fuse實(shí)際上是把內(nèi)核空間實(shí)現(xiàn)的VFS支持接口,放到用戶層實(shí)現(xiàn)。
三、文件系統(tǒng)一致性
3.1掉電與文件系統(tǒng)一致性
由上一節(jié)文件系統(tǒng)的布局分析可知,當(dāng)操作一個文件時,比如往/a目錄下添加一個b,即添加/a/b文件,需要修改inode bitmap, inode table, block bitmap, data block。
這一系列的操作是非原子的,假如任何一個環(huán)節(jié)掉電,造成某些步驟丟失,就會造成數(shù)據(jù)的不完整,文件將無法正常訪問。
3.2append一個文件的全流程
而硬件是不可能原子執(zhí)行的,因此會造成不一致性。
3.3 模擬文件系統(tǒng)不一致性案例
(1) 做一個image,用來模擬磁盤
dd if=/dev/zero of=image bs=1024 count=4096
(2).格式化為ext4文件系統(tǒng)
mkfs.ext4 -b 4096 image
(3).mount到test目錄,寫入一個ok.txt文件
sudo mount -o loop image test/
cd test/
sudo touch ok.txt
cd ..
sudo umount test
(4).查看磁盤詳細(xì)信息
(base) leon\@pc:\~/io\$ dumpe2fs image
dumpe2fs 1.42.13 (17-May-2015)
Filesystem volume name: <none>
Last mounted on: /home/leon/io/test
Filesystem UUID: 759835e3-9508-4c57-b511-9c4f7e13f0ad
Filesystem magic number: 0xEF53
Filesystem revision #: 1 (dynamic)
…
Inode count: 1024
Block count: 1024
Reserved block count: 51
Free blocks: 982
Free inodes: 1012
First block: 0
Block size: 4096
Fragment size: 4096
Blocks per group: 32768
Fragments per group: 32768
Inodes per group: 1024
…
First inode: 11
Inode size: 128
Default directory hash: half_md4
Directory Hash Seed: 9cf91d57-8528-4d39-b6ba-5f8e2e86fcb7
Group 0: (Blocks 0-1023) [ITABLE_ZEROED]
Checksum 0x240a, unused inodes 1012
主 superblock at 0, Group descriptors at 1-1
Block bitmap at 2 (+2), Inode bitmap at 18 (+18)
Inode表位于 34-65 (+34)
982 free blocks, 1012 free inodes, 2 directories, 1012個未使用的inodes
可用塊數(shù): 8-17, 19-33, 67-1023
可用inode數(shù): 13-1024
(base) leon\@pc:\~/io\$
可以看到inode bitmap在18個塊。
(5).查看inodebitmap塊
dd if=image bs=4096 skip=18 | hexdump -C -n 32
由于ext4默認(rèn)用掉11個inode,新創(chuàng)建的ok.txt文件后,inode bitmap用掉12位。
(6).現(xiàn)在模擬掉電,修改inode bitmap
vim -b image
:%!xxd –g 1
:%!xxd –r
找到inode bitmap塊對應(yīng)地址4096*18=0x12000
將bitmap改為ff 07
電導(dǎo)致的不一致性,會出現(xiàn)各種奇怪的問題,甚至都無法修復(fù);
任何軟件的手段只能保持一致性,無法保證不丟失數(shù)據(jù)。
fsck
人為破壞data block,用fsck修復(fù),修復(fù)原理,掃描bitmap和inode table的一致性。早期Linux/Windows系統(tǒng)異常掉電后啟動,都用fsck修復(fù)磁盤,速度很慢,為提高速度,新系統(tǒng)都采用日志系統(tǒng)方式。
3.4文件系統(tǒng)的日志
將要修改的行為,記錄為一個日志,若操作磁盤過程掉電,開機(jī)根據(jù)日志回放,將磁盤操作全部重做一遍。磁盤操作完成,刪除日志。
優(yōu)點(diǎn):保持文件系統(tǒng)的一致性,也提高速度。
EXT2/3/4都采用日志系統(tǒng)。
日志的幾個階段:
完整的日志方式,相當(dāng)于每個數(shù)據(jù)都寫了兩遍,讓系統(tǒng)變很慢,實(shí)際工程上會根據(jù)數(shù)據(jù)情況,做部分日志,即日志方式分為三種:速度遞增,安全性遞減
data=journal: 完整日志;
data = ordered: 只寫元數(shù)據(jù),且先寫完數(shù)據(jù)塊,再寫元數(shù)據(jù)
data=writeback 只寫元數(shù)據(jù),循序不確定;ubuntu默認(rèn)方式;
這樣日志就分為5個階段:
3.5文件系統(tǒng)的調(diào)試工具
創(chuàng)建一個文件t.txt,df –h
(base) leon@pc:~/io$ **sudo debugfs -R 'stat /home/leon/io/t.txt' /dev/sda7**
sudo: 無法解析主機(jī):pc: 連接超時
debugfs 1.42.13 (17-May-2015)
Inode: 13896517 Type: regular Mode: 0664 Flags: 0x80000
Generation: 507799365 Version: 0x00000000:00000001
User: 1000 Group: 1000 Size: 17
File ACL: 0 Directory ACL: 0
Links: 1 Blockcount: 8
Fragment: Address: 0 Number: 0 Size: 0
ctime: 0x5f40e439:2182bba0 -- Sat Aug 22 17:24:09 2020
atime: 0x5f40e43b:c9589800 -- Sat Aug 22 17:24:11 2020
mtime: 0x5f40e439:2182bba0 -- Sat Aug 22 17:24:09 2020
crtime: 0x5f40e439:208e98b8 -- Sat Aug 22 17:24:09 2020
Size of extra inode fields: 32
EXTENTS:
(0):55636171
(END)
得到文件的數(shù)據(jù)塊55636171
查看數(shù)據(jù)塊內(nèi)容
sudo **blkcat** /dev/sda7 55636171
sudo dd if=/dev/sda of=1 skip=$((55636171*8+824123392)) bs=512c count=1
sudo debugfs -R 'icheck 55636171' /dev/sda7
debugfs 根據(jù)塊號查inode號
sudo debugfs -R 'icheck 55636171' /dev/sda7
根據(jù)inode號,查文件路徑
sudo debugfs -R 'ncheck 13896517' /dev/sda7
3.6Copy On Write文件系統(tǒng): btrfs
不用日志,實(shí)現(xiàn)文件系統(tǒng)一致性。每次寫磁盤時,先將更新數(shù)據(jù)寫入一個新的block,當(dāng)新數(shù)據(jù)寫入成功之后,再更新相關(guān)的數(shù)據(jù)結(jié)構(gòu)指向新block。
COW穩(wěn)健性系統(tǒng)的實(shí)現(xiàn)方式,有利于實(shí)現(xiàn)子卷和snapshot,類似git的思想:
四、塊I/O流程與I/O調(diào)度器
4.1一個塊IO的一生
從page cache到bio到request,當(dāng)APP打開一個文件,內(nèi)核為文件維護(hù)一個pagecache(磁盤的一個副本);
讀寫文件時如果page cache命中,只會讀寫內(nèi)存不操作磁盤;沒有命中,才操作磁盤。
在內(nèi)核用file結(jié)構(gòu)體表示,可見其包含一個inode結(jié)構(gòu)體,一個地址空間;
相關(guān)的幾個結(jié)構(gòu)體在內(nèi)核對應(yīng)關(guān)系如下:
可見,當(dāng)多個進(jìn)程同時打開同一個文件時,不同的file結(jié)構(gòu)體對應(yīng)同一個inode和同一個地址空間,地址空間是由一顆radixtree維護(hù)(即pagecache),讀寫文件時,查看對應(yīng)內(nèi)存頁在page cache中是否命中,若命中直接從內(nèi)存空間讀寫;若不命中,申請一個內(nèi)存頁,從磁盤讀入數(shù)據(jù),掛到page cache的radix tree中。
另外,page cache與磁盤的同步由地址空間操作函數(shù)readpage/writepage完成
對磁盤訪問,有兩種方法:
a.裸磁盤直接訪問;
b.通過文件系統(tǒng)訪問;
它們在內(nèi)核page cached對應(yīng)關(guān)系如下:
一個address_space對應(yīng)一個inode
free命令統(tǒng)計(jì)的buffer/cached,只是統(tǒng)計(jì)上的區(qū)別;
buffer=操作裸分區(qū)的地址空間+文件系統(tǒng)的元數(shù)據(jù)地址空間;
cached=文件系統(tǒng)的地址空間(page cached)緩存;
但是對同一個磁盤,裸磁盤和文件系統(tǒng)兩種方式同時操作的時候,同一個數(shù)據(jù)塊會被映射到不同的address_space,會產(chǎn)生同步的問題;
在用dd的時候,最好不要操作文件系統(tǒng)數(shù)據(jù):
4.2O_DIRECT和O_SYNC
直接操作裸磁盤分區(qū)用O_DIRECT,內(nèi)核沒有任何cache,直接操作磁盤;用戶可以根據(jù)數(shù)據(jù)特點(diǎn),在用戶空間做cache。O_DIRECT申請內(nèi)存要用posix_memalign接口;
而O_SYNC依然通過page cache,但是會立即寫入同步磁盤;
App通過page cache訪問文件和直接操作裸磁盤的模型,與CPU通過cache訪問內(nèi)存和DMA直接訪問內(nèi)存的模型非常類似;
這里page cache是內(nèi)存,file是磁盤分區(qū)數(shù)據(jù)塊。當(dāng)有一個進(jìn)程啟用O_DIRECT模式,其他進(jìn)程最好也用O_DIRECT;
4.3BIO 流程blktrace
對于一個pagecache地址空間,指向的是page頁,文件系統(tǒng)ext4讀取inode,將page轉(zhuǎn)化為磁盤數(shù)據(jù)塊,變成BIO請求;
BIO最終要轉(zhuǎn)化成request,然后request被塊設(shè)備驅(qū)動程序調(diào)用完成;
Bio經(jīng)過三個隊(duì)列變成request,三進(jìn)三出
step1:原地蓄勢
把bio轉(zhuǎn)化為request,把request放入進(jìn)程本進(jìn)程的plug隊(duì)列;蓄勢多個request后,再進(jìn)行泄洪。
可能合并相鄰bio為一個request;
Bio數(shù)量>=request數(shù)量;
多個進(jìn)程,泄洪request到電梯;
step2.電梯排序
進(jìn)程本地的plug隊(duì)列的request進(jìn)入到電梯隊(duì)列,進(jìn)行再次的合并、排序,執(zhí)行QoS的排隊(duì),之后按照QoS的結(jié)果,分發(fā)給dispatch隊(duì)列。電梯內(nèi)部的實(shí)現(xiàn),可以有各種各樣的隊(duì)列。
比如兩個進(jìn)程需要讀鄰近的數(shù)據(jù)塊,可以合并為一個request
電梯調(diào)度層可以做QoS,設(shè)定進(jìn)程訪問IO的優(yōu)先級;
step3.分發(fā)執(zhí)行dispatch
電梯分發(fā)的request,被設(shè)備驅(qū)動的request_fn()挨個取出來,派發(fā)真正的硬件讀寫命令到硬盤。這個分發(fā)的隊(duì)列,一般就是我們在塊設(shè)備驅(qū)動里面見到的request_queue了。request_queue完成真正的硬件操作;
4.4工具ftrace
do.sh
#!/bin/bash
debugfs=/sys/kernel/debug
echo nop > $debugfs/tracing/current_tracer
echo 0 > $debugfs/tracing/tracing_on
echo `pidof read` > $debugfs/tracing/set_ftrace_pid
echo function_graph > $debugfs/tracing/current_tracer
echo vfs_read > $debugfs/tracing/set_graph_function
echo 1 > $debugfs/tracing/tracing_on
執(zhí)行./read讀文件
查看trace過程
sudo cat /sys/kernel/debug/tracing/trace > t.txt
用VIM查看t.txt,用.funcgrahp.vim插件打開可以合并/展開對應(yīng)函數(shù)調(diào)用
vim -S ~/.funcgrahp.vim
4.5IO調(diào)度算法,CFQ和ionice
查看當(dāng)前系統(tǒng)的IO調(diào)度算法
(base) leon\@pc:/sys/block/sda/queue\$ cat scheduler
noop deadline [cfq]
修改調(diào)度算法:
sudo echo cfg > scheduler
CFQ調(diào)度算法:類似進(jìn)程調(diào)度的CFS算法
ionice –help
ionice –c 2 –n 0 dd if=/dev/sda of=/dev/null & //設(shè)置nice值=0
ionice –c 2 –n 8 dd if=/dev/sda of=/dev/null & //設(shè)置nice值=8
iotop查看IO讀寫速度,有明顯差異
改成deadline策略
echo deadline > scheduler
此時盡管優(yōu)先級不同,但兩個進(jìn)程IO速度相近了;
4.6cgroup與IO
cd /sys/fs/cgroup/blkio
a.創(chuàng)建群組A,B
Mkdir A;makedir B
cat blkiolweight //默認(rèn)是500
分別把兩個進(jìn)程放到A和B執(zhí)行
cgexec -g blkio:A dd if=/dev/sda of=/dev/null iflag=direct &
cgexec -g blkio:B dd if=/dev/sda of=/dev/null iflag=direct &
查看io占用iotop
默認(rèn)是相近
b.修改群權(quán)重
echo 50 > blkio.weight
兩個進(jìn)程優(yōu)先級一樣,但I(xiàn)O速度差別很大;
c.cgroup還可以控制閥門,限制最大讀速度:
8:0磁盤的主次設(shè)備號
echo "8:0 1048576" > /sys/fs/cgroup/blkio/A/blkio.throttle.read_bps_device
帶cache讀寫,這里的限速不起作用;direct方式才生效;
cgroup v2版本支持帶cache限速
4.6IO性能調(diào)試:iotop, iostat
blktrace跟蹤硬盤的各個讀寫點(diǎn)
blktrace –d /dev/sda –o - |blkparse –I –
dd if=main.c of=t.txt oflag=sync
debugfs –R 'ickeck xxxx’ /dev/sda1
debugfs –R 'nckeck inode’ /dev/sda1
blkcat /dev/sda1 xxx