最近比較懶,仍是加班寫點東西吧,否則過段時間又把這些整理的東西弄丟了。linux
寫什麼呢?寫一些跟工做相關的吧!由於筆者從事多媒體錄像相關的開發工做,所以經常涉及到優化寫卡策略、提高寫卡性能相關的方面的事情。此話怎講呢?如行車記錄儀類的錄像產品,錄像可能持續多日,越日後寫卡速度會愈來愈慢,直觀感覺是取出視頻文件進行回放時,時間約日後的視頻文件卡頓愈來愈嚴重。算法
怎樣解決呢?一種方案從硬件解決,換一張好卡!可是這不能一勞永逸解決問題,由於錄着錄着寫卡速度又掉下來了。另一種方案從軟件層面解決,就是卡速變慢了後,將卡格式化,可是這種方案對於用戶來說不太友好(有些用戶可能不知道這個功能,或者文件刪除前備份不方便)。還有一種方案,也是從軟件層面解決問題,就是優化寫卡策略。優化寫卡策略,有一些可行的方案,例如文件預分配、待寫數據進行緩衝寫、編碼與封裝解耦,直寫(DirectIO)。下面內容介紹預分配的內容。數據庫
linux man手冊說明:服務器
fallocate即預分配,英文爲preallocate。什麼意思呢?還往文件中沒寫數據,可是已經給文件分配了足額的物理空間來存儲數據。建立了文件,再調用這個接口預分配了必定量的空間後,後續就能夠往這個文件中寫數據了。async
另一點須要注意,這個接口須要文件系統的支持。經常使用TF卡錄像,而卡的文件系統類型通常爲fat32,就須要fat32文件系統相關的實現才能使用該功能。函數
再有,這是一個不可移植的linux專用系統調用,用於確保文件空間被提早分配,成功執行後,能夠確保寫卡速度較快,也能保證不會由於磁盤空間不足而出現寫失敗。性能
函數原型優化 |
int fallocate(int fd, int mode, off_t offset, off_t len);編碼 |
fdspa |
文件句柄 |
mode |
建立模式 |
offset |
偏移 |
len |
文件大小 |
其中,在建立了文件後和寫數據前,須要調用該接口進行預分配,第二個參數mode通常設置爲1,第三個參數設置爲0,第四個參數填上指望預分配值。
應用場景:持續寫卡場景,例如行車記錄儀、運動相機。
目標:減小磁盤碎片化,提升寫卡速度。
其餘說明:錄像設備的瓶頸經常是寫卡,由於要隨時將視頻文件記錄下來。而且,對持續寫卡速度要求較高,由於錄像設備工做週期多是以day爲單位,不只要求錄像剛啓動時寫卡正常,並且要求工做了幾天寫卡速度也不能掉太多。至於每秒鐘寫入的數據量,視編碼器輸出碼率和幾路錄像而定,對於單路1080p錄製,視頻碼率設置爲10mbps,那麼卡速至少要保證2MB/s,這裏面還不包括寫log以及錄像中拍照所用的。
雖然目前時間節點上(2019年底),市面上卡都是C10(10MB/s)及其以上,可是若是寫策略不合理或卡中太多零碎文件,寫速度可能很低。很常見的一個例子,拷貝一個視頻文件到T卡的速度,要遠遠大於拷貝一樣大小的源文件包。另外一個例子是,一個剛格式化的T卡與一個內部已經存在了不少文件的T卡(卡品牌、容量、速度等參數都同樣),拷貝一樣大小的文件,剛格式化的那張卡速度更快。
TF卡(TransCard)和SSD(SolidStateDisk)做爲常見的存儲設備,內部組成很是相似,都主要由controler和nand flash組成。對於任何存儲設備,咱們都最關心三個參數:容量、讀/寫速度、壽命。
「容量」這個參數勿用介紹,「讀速度」也不介紹,下面主要說下「壽命」和「寫速度」這兩個參數。介紹這兩個參數後,再來介紹預分配。
壽命主要由存儲介質決定,即nand flash這種介質的可擦寫次數,nand flash介質類型的發展經歷了slc、mlc、tlc、qlc(目前市面上還較少)幾個階段,單位面積的容量也愈來愈大,由於介質類型反映了存儲密度。小小的TF卡,就目前2019年底的這個時間節點上,市面上已經出現了512GB容量的TF卡,存儲多個圖書館書籍的文字信息應該毫無壓力!可是,凡事有利有弊,隨着容量的提高,TF內部的最小存儲單元的可擦寫次數也愈來愈少。
SLC(SingleLevelCell)出現最先,可擦寫次數10多萬次;後來出現的MLC(MultiLevelCell)可擦寫次數3000-10000次左右,目前主流的TLC的可擦寫次數在500-1000次左右。在某東上隨便查看了lexar的某款500GB 容量的SSD,其參數以下:
從中看到閃存類型爲TLC,還有TBW=250T這個參數,這個是什麼以及怎麼得來的呢?
TBW,即TeraBytesWritten,以TB爲單位的寫入的數據量。這個值這樣算:總容量*可寫次數,即500GB*500 = 250TB。其中的500表明平都可寫次數爲500,是根據閃存類型TLC來估算的。通常企業級的用的sdd,價格較民用的高很多,例如編譯/數據庫服務器,相同容量的TBW值一般是以PBW(=1024TBW)爲單位的,不太追求讀寫速度,但很是看重壽命和可靠性,畢竟數據是無價的。
寫速度是個比較玄乎的東西,由許多因素綜合致使,例如,閃存類型、主控算法(固件磨損平衡算法)、文件系統寫策略、卡的碎片化程度、卡的文件系統類型和block大小、內部是否帶Cache以及其大小,等等諸多因素。
可是,針對肯定下來的一張卡,咱們須要找到一些方法,來提升寫卡速度。其中一種方法就是預分配——fallocate。
接下來先介紹文件存儲相關的內容後,再來介紹這個預分配接口的做用。
對於fat32的文件系統,存儲設備中的某文件,其內容主要包括兩部分:一部分是屬性信息metadata(建立/修改時間、文件名稱、文件大小等),另外一部分是真正的數據內容。經常使用的fdatasync操做只會強制將真正的數據內容刷新到存儲設備中,而fsync會將兩部份內容都刷新到設備中。對於真正的數據內容那部分,有一個鏈表來管理各個塊內容所在的SectorId,即以sector鏈表的形式來完整表述數據內容。所以,某文件的存儲物理地址多是某連續sector區所在的一整片區域,也可能分佈於多個不連續的物理區域。
存儲設備的碎片化與內存碎片化很是相似,即某文件但願儘量利用連續的物理存儲空間來存儲數據,可是因爲卡已處於高度碎片化狀態,當真正寫入完這個文件時,這個文件在物理空間上是「支離破碎」的。即便是一個剛剛格式化的卡,當兩個線程同時分別寫兩個不一樣文件時,在物理空間上(內部連續的物理block或sector),這兩個文件可能處於交織狀態(交錯),英文爲interleave。作過音頻開發的同事也能夠回想一下alsa-lib在打開設備進行參數配置時,針對雙聲道pcm數據採集,有interleave和non-interleave的配置,這個選擇決定了左右聲道pcm數據在一個period內如何排列,相似對比,卡中存儲的多個文件,對於物理block就是這個意思。
設想一種寫文件場景,使用正常fopen-fwrite-fclose的操做流程,只寫一路,當每次將kernel cache中的數據刷到卡中前,須要現場去找(相似於寫磁盤時的尋道)哪一個物理sector是available的,當發現某個block中的某個sector是可用的,可是其餘sector是其餘文件佔用的,那麼接下來的策略就是copy-modify-write,即出現了「寫放大」(WriteAmplification)。
爲何出現這個情況,須要瞭解閃存的基本組成:頁page(也稱sector,大小4KB) -> 塊block(一般64或128個page組成一個block) -> 面plane(多個block組成) –> die(plane就是一個die) -> 閃存片(多個die組成) –> SSD或TF(多顆閃存片組成)。
下面描述下寫放大過程:先把整個block中的數據徹底拷貝到ddr,再將某個sector中的數據修改成指望寫入的數據,擦掉ssd中這個block的內容,而後再總體將ddr中的已修改好的數據寫入到ssd中這個block位置。爲何要這樣作?由於寫入是按block爲最基本單位進行的。因此寫入一筆數據,涉及了屢次基本操做,不只減慢了寫速度,並且減小了壽命。然而,當進行了預分配後,提早爲某文件劃分了「勢力範圍」,標定某些位置已經被佔用,能夠減小後續的寫放大和尋找可用空間的過程。
介紹了文件存儲結構的相關內容後,對於預分配的功能咱們就有了大體的猜想!fallocate這個接口,其要實現的目的,就是在數據內容還未寫入到設備前,提早爲文件分配好若干大小的空間,而且使這個空間儘量是物理連續的,這樣能夠減小後續寫放大的出現頻率,以及不需在寫入過程當中尋找可用空間,更不會出現寫數據時磁盤空間不足的問題!
使用預分配一個最大的問題是——磁盤空間利用率不高!這個如何提及?文件剛建立還未寫入數據,咱們就搶先爲文件設置了文件的大小並佔用了固定大小的物理空間,但一般可能未寫入那麼大size的數據量就fclose了這個文件,那麼這個文件內未寫入的空間就不能被其餘文件利用了。一個文件預分配了100MB,即便只寫入1MB就關閉,那麼就有99MB的空間浪費。可是,使用預分配對於行車記錄儀類產品是個較優的選擇,由於文件切換是定時切換的,若是編碼器輸出碼率是相對穩定的,就能夠預估最終文件大小,預分配的大小再留些餘量就能夠了。