Linux中的緩沖區(qū)和文件系統(tǒng)詳解
一、FILE結(jié)構(gòu)
1、fd
FILE是在C中封裝起來的一個(gè)結(jié)構(gòu)體,那我們訪問文件的時(shí)候都是通過fd訪問的,自然在FILE中是封裝了fd的,F(xiàn)ILE結(jié)構(gòu)體中,int _file存放的就是fd,其他的成員基本都是與緩沖區(qū)有關(guān)的

2、緩沖區(qū)
(一)有換行有return全部打印
看下面一段代碼:


(二)無換行無return的C接口打印
很顯然的,我們打印出了所有我們需要的內(nèi)容,我們再看下一段


理想狀態(tài)下我們應(yīng)該是打印出結(jié)果后然后進(jìn)行while一直循環(huán),實(shí)際上是一只不會(huì)打印,這是為什么呢?是的,待在緩沖區(qū)里
首先我們要知道,緩沖區(qū)的大概位置,我們上面貼了一張F(tuán)ILE結(jié)構(gòu)體的結(jié)構(gòu)圖,我們可以很清楚地看到緩沖區(qū)是FILE的成員指針指向的一塊位置,也就是說緩沖區(qū)一定在用戶空間而不是內(nèi)核空間

(三)無換行無return的系統(tǒng)調(diào)用接口打印
我們在調(diào)用上面三個(gè)函數(shù)的時(shí)候,都是調(diào)用的C接口,自然都待在緩沖區(qū)里了,我們再看下一個(gè)程序


在這個(gè)程序中我們直接調(diào)用系統(tǒng)調(diào)用接口write,所以它不會(huì)經(jīng)過C語言的緩沖區(qū),而是直接打印
(四)有換行無return的C接口打印
我們再來看一組程序


這個(gè)程序和(二)程序的區(qū)別就只有換行,這告訴我們,C語言緩沖區(qū)對于顯式器是行緩沖的,C語言標(biāo)準(zhǔn)庫的文件流有三種緩沖模式,分別是全緩沖、行緩沖和無緩沖
- 全緩沖 _IOFBF :通常用于對磁盤文件的操作,數(shù)據(jù)會(huì)先被存儲在緩沖區(qū)中,直到緩沖區(qū)被填滿或者調(diào)用 fflush 函數(shù)、關(guān)閉文件(fclose)時(shí),才會(huì)將緩沖區(qū)中的數(shù)據(jù)寫入實(shí)際的文件,在全緩沖模式下,不會(huì)因?yàn)橛龅綋Q行符而自動(dòng)刷新緩沖區(qū)
- 行緩沖 _IOLBF :常見于標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出等終端設(shè)備相關(guān)的流,當(dāng)遇到換行符(\n)時(shí),會(huì)自動(dòng)刷新緩沖區(qū),將緩沖區(qū)中的數(shù)據(jù)寫入對應(yīng)的設(shè)備或文件,某些情況下即使沒有換行符,緩沖區(qū)滿時(shí)也會(huì)刷新
- 無緩沖 _IONBF :標(biāo)準(zhǔn)錯(cuò)誤輸出通常默認(rèn)是無緩沖的,確保錯(cuò)誤信息能夠立即顯示,在無緩沖模式下,數(shù)據(jù)會(huì)立即寫入對應(yīng)的設(shè)備或文件,不會(huì)進(jìn)行緩沖,因此不存在行刷新的概念
(五)無換行有return的C接口打印



進(jìn)程退出return的時(shí)候,也會(huì)對緩沖區(qū)進(jìn)行刷新
(六)深入理解緩沖區(qū)在用戶空間



我們打印在顯示器上的內(nèi)容和打印在文件中的內(nèi)容不一致,只有write打印了一遍,其他是按照順序打印了兩遍,我們當(dāng)然能看出來這是fork的鍋,接下來我們就深入理解談一談緩沖區(qū)
首先我們分析第一張結(jié)果圖,因?yàn)轱@示器是行緩沖的,所以我們C接口的打印放到緩沖區(qū)中一行就會(huì)被打印到屏幕上一行,三條語句執(zhí)行完之后緩沖區(qū)是空的,然后write再往上寫,所以整個(gè)打印出來的順序也是按照代碼中來的
然后我們分析最后一張圖,第一個(gè)我們可以肯定的是,打印到文件一定不是行緩沖,那就更不是無緩沖,實(shí)際上,由于文件是在存儲硬件當(dāng)中的,由于我們的效率問題,對于這種存儲類的緩沖條件都是全緩沖,把緩沖區(qū)塞滿再寫入存儲硬件中比塞一點(diǎn)寫一點(diǎn)效率高得多,所以前三句C接口調(diào)用的打印全部在緩沖區(qū)中,然后write將自己打印,然后我們就碰到了fork,創(chuàng)建子進(jìn)程,父子進(jìn)程此時(shí)共享代碼段和數(shù)據(jù)段,因?yàn)樗鼈兌紱]有做修改,然后我們就碰到了return 0,前面我們提到:進(jìn)程結(jié)束也是要清空緩沖區(qū)的,此時(shí)父或子進(jìn)程某一個(gè)先結(jié)束(由調(diào)度器決定),其中一個(gè)進(jìn)程清空緩沖區(qū)的行為會(huì)引起另一個(gè)進(jìn)程的寫實(shí)拷貝,此時(shí)我們就有兩份緩沖區(qū),兩個(gè)進(jìn)程都結(jié)束都要清空緩沖區(qū),自然在緩沖區(qū)中的內(nèi)容要打印兩份了(在這里要注意了,不只是子進(jìn)程修改數(shù)據(jù)會(huì)引起子進(jìn)程的寫時(shí)拷貝,父進(jìn)程對數(shù)據(jù)做修改時(shí)父進(jìn)程也要發(fā)生寫時(shí)拷貝,被寫時(shí)拷貝的數(shù)據(jù)再再發(fā)生修改就直接修改了,不發(fā)生寫實(shí)拷貝)
二、文件系統(tǒng)
文件一般存儲在硬盤當(dāng)中,我們已經(jīng)學(xué)習(xí)了動(dòng)態(tài)的文件,也就是進(jìn)程打開文件訪問文件的過程,現(xiàn)在我們來學(xué)習(xí)一下靜態(tài)的文件,我們來直接學(xué)習(xí)一下固態(tài)硬盤
1、固態(tài)硬盤
固態(tài)硬盤是一種基于NAND閃存的存儲單元,我們常用的筆記本上的固態(tài)硬盤存儲單元類型一般都是TLC的,三層單元,每個(gè)單元存儲3bits,壽命較短成本較低,它通過電荷存儲數(shù)據(jù),通過高低電平區(qū)分0/1
NAND閃存的寫入操作只能在已擦除的塊上進(jìn)行,擦除的最小單位就是塊,通常為128KB-4MB,寫入的最小單位是頁,通常為4KB,所以它讀的速度特別快,可以到微秒級,因?yàn)樾枰炔脸龎K,寫的速度較慢,只能到毫秒級,每個(gè)塊的擦寫次數(shù)有限,超過后就會(huì)失效,一般TLC的擦鞋上限在500-1500次
這樣的性質(zhì)會(huì)帶來一些不太好的結(jié)果,比如我們要寫的內(nèi)容很小,假設(shè)為4KB,那么我們先要擦除高達(dá)4MB的塊才能進(jìn)行寫入,所以我們通過算法,將寫入分散到所有塊,避免某些塊因?yàn)槎啻尾脸?/p>
固態(tài)硬盤控制器中的核心邏輯叫做FTL ( Flash Translation Layer ) Flash翻譯層,負(fù)責(zé)將文件系統(tǒng)的邏輯地址映射到物理地址,是不是有點(diǎn)像進(jìn)程地址通過頁表映射到物理地址呢
接下來我們要學(xué)習(xí)文件系統(tǒng)的邏輯地址LBA,因?yàn)槲覀兒芮宄﨔TL映射到物理地址的過程是與頁表映射是相似的,而邏輯地址的組織方式與進(jìn)程地址可是不同的,雖然是有相似之處的~
2、邏輯地址LBA
LBA 從 0 開始,按照連續(xù)的整數(shù)順序依次為存儲設(shè)備中的每個(gè)數(shù)據(jù)塊編號,存儲設(shè)備中的每個(gè)數(shù)據(jù)塊都對應(yīng)一個(gè)唯一的 LBA 值,比如第一個(gè)數(shù)據(jù)塊的 LBA 是 0,第二個(gè)是 1,依此類推,我們對應(yīng)的數(shù)據(jù)塊有Super Block、Group Descriptor Table、Block Bitmap、inode Bitmap、inode Table、Data blocks六個(gè),最理想的情況下它們的LBA按照我上面寫的順序從0到5
我們把一塊固態(tài)硬盤,我們筆記本上有一些品牌比如說某L開頭的品牌,在我們購買的時(shí)候是默認(rèn)給你帶1T固態(tài)的,一般的品牌就是512G,我們拿到筆記本之后會(huì)對電腦進(jìn)行分區(qū),C盤作為系統(tǒng)盤分到最多的內(nèi)存,G盤作為游戲盤給到300多G,然后D盤用來學(xué)習(xí)寫代碼,留個(gè)200G,EF盤用來存一些其他的東西,這樣一套流程下來我們就分好盤了,我們說對硬盤做管理當(dāng)然也是先描述后組織,Block Group就是組織和管理磁盤空間的一種重要結(jié)構(gòu)

在n個(gè)Block Group之前有一個(gè)叫做Boot Block(引導(dǎo)塊)的區(qū)域,在計(jì)算機(jī)啟動(dòng)過程中起著至關(guān)重要的作用,它是計(jì)算機(jī)啟動(dòng)過程的起點(diǎn),沒有引導(dǎo)塊中的引導(dǎo)代碼,計(jì)算機(jī)就無法知道如何加載操作系統(tǒng),也就無法正常啟動(dòng)
我們按照知識理解易難順序倒著往前來說
(一)數(shù)據(jù)塊 Data Blocks
用來存儲數(shù)據(jù)的塊,NAND FLASH 內(nèi)部的數(shù)據(jù)塊由多個(gè)page組成,通常大小為4KB(現(xiàn)在也有8KB和16KB),這個(gè)page就是我們前面提到的最小寫入單位:頁

(二)inode表 inode Table
inode全稱為索引節(jié)點(diǎn),是一種數(shù)據(jù)結(jié)構(gòu),用于存儲單個(gè)文件的全部屬性,一般來說每個(gè)文件都有一個(gè)inode
struct inode
{
//inode編號
//文件類型
//權(quán)限
//引用計(jì)數(shù)
//擁有者
//所屬組
// 直接塊指針
unsigned long i_block[NUM];
// 一次間接塊指針
unsigned long i_ind_block;
// 二次間接塊指針
unsigned long i_dind_block;
// 三次間接塊指針
unsigned long i_tind_block;
}其中inode編號每個(gè)文件都是不同的,我們主要說說數(shù)據(jù)塊指針
(1)直接塊指針
直接塊指針的NUM一般是12,它指向的位置是我們可以直接用來存儲的位置,如果我們內(nèi)容比較?。?code>12*4KB = 48KB以內(nèi)),那么直接塊指針可以直接訪問這些數(shù)據(jù)
(2)一次間接塊指針
如果內(nèi)容大于48KB,就需要一次間接塊指針,一次間接塊指針指向一個(gè)間接塊,這個(gè)間接塊存儲中存儲著多個(gè)指向數(shù)據(jù)塊的指針,如我們的內(nèi)容在(4KB/4b)*4KB = 4MB以內(nèi),通過一次間接塊指針和直接塊指針就可以訪問這些數(shù)據(jù)
(3)二次間接塊指針
二次間接塊指針指向一個(gè)二次間接塊,這個(gè)二次間接塊存儲中存儲著多個(gè)指向間接塊的指針,與一次間接塊指針類似,這樣我們存儲的范圍就達(dá)到了(4KB/4b)*(4KB/4b)*4KB = 4GB
(4)三次間接塊指針
同上,最終我們最大的存儲范圍達(dá)到了 (4KB/4b)*(4KB/4b)*(4KB/4b)*4KB = 4TB

(三)inode位圖 inode Bitmap
我們通過位圖來和inode一一對應(yīng),位圖上對應(yīng)的比特位為0,那么該inode就沒有被使用,可以被分配,如果為1則被占用,當(dāng)從1變?yōu)?時(shí),該inode又可以被分配了,其實(shí)這個(gè)過程就是一個(gè)刪除的過程,一旦一個(gè)文件的inode無了,那么這文件是真的無了
在文件系統(tǒng)中,標(biāo)識文件不是看它的名字,而是看它的inode,一旦inode和文件取消綁定了,那么操作系統(tǒng)就找不到這個(gè)文件了,再次寫入其他內(nèi)容的時(shí)候也就會(huì)被擦除覆蓋了,換而言之,刪除恒等于可以被覆蓋
(四)塊位圖 Block Bitmap
我們通過位圖來和數(shù)據(jù)塊page一一對應(yīng),位圖上對應(yīng)的比特位為0,那么該頁page就沒有被使用,可以被分配,如果為1則被占用,當(dāng)從1變?yōu)?時(shí),該page又可以被分配了,如果我們要?jiǎng)h除一塊空間,只需要將它的對應(yīng)的位置0,到再次被寫入的時(shí)候就會(huì)消失了,當(dāng)然我們的NAND閃存是定期擦除的,過一段時(shí)間它自己就被擦除了

(五)塊組描述符表 Group Descriptor Table
記錄了該塊組的詳細(xì)信息,包括塊位圖的位置、inode 位圖的位置、inode 表的起始位置等,用于定位和管理塊組內(nèi)的各種數(shù)據(jù)結(jié)構(gòu)
(六)超級塊 Super Block
超級塊是文件系統(tǒng)的核心,記錄了文件系統(tǒng)的全局信息,如塊大小、inode 數(shù)量、空閑塊數(shù)量等,為了防止超級塊損壞導(dǎo)致文件系統(tǒng)無法使用,每個(gè)塊組中可能會(huì)包含超級塊的副本,不過并非所有塊組都有
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
.htaccess rewrite 規(guī)則詳細(xì)說明
用Apache虛擬主機(jī)的朋友很多,apache提供的.htaccess模塊可以為每個(gè)虛擬主機(jī)設(shè)定rewrite規(guī)則,這對網(wǎng)站SEO優(yōu)化相當(dāng)有用,同時(shí)也改善了用戶體驗(yàn)2016-04-04
解決因文件權(quán)限導(dǎo)致git fetch命令執(zhí)行失敗的問題
最近在工作中遇到一個(gè)問題,就是在在一個(gè)基于 git 的發(fā)布系統(tǒng)中拉取代碼,發(fā)現(xiàn)無法拉取最新的提交記錄,查找相關(guān)資料發(fā)現(xiàn)是因?yàn)槲募?quán)限的問題,所以這篇文章主要介紹了關(guān)于解決因文件權(quán)限導(dǎo)致git fetch命令執(zhí)行失敗的問題,需要的朋友可以參考下。2017-04-04
Linux系統(tǒng)重啟后MySQL數(shù)據(jù)丟失問題的解決步驟
今天分享一個(gè)在Linux系統(tǒng)中經(jīng)常遇到的問題:系統(tǒng)重啟后發(fā)現(xiàn)MySQL無法啟動(dòng),而且數(shù)據(jù)似乎丟失了,這個(gè)問題可能會(huì)讓人驚慌失措,但別擔(dān)心,通常情況下這只是因?yàn)閿?shù)據(jù)盤沒有正確掛載導(dǎo)致的,現(xiàn)在我們將深入探討這個(gè)問題的原因、解決方法以及如何預(yù)防它的再次發(fā)生2024-09-09
詳解CentOS7 安裝 MariaDB 10.2.4的方法
這篇文章主要介紹了CentOS7 安裝 MariaDB 10.2.4的方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-11-11

