Linux文件讀寫機制及優化方式

Linux文件讀寫機制及優化方式Linux文件讀寫機制及優化方式

緩存node

緩存是用來減小高速設備訪問低速設備所需平均時間的組件,文件讀寫涉及到計算機內存和磁盤,內存操做速度遠遠大於磁盤,若是每次調用read,write都去直接操做磁盤,一方面速度會被限制,一方面也會下降磁盤使用壽命,所以不論是對磁盤的讀操做仍是寫操做,操做系統都會將數據緩存起來。linux

Page Cacheapi

頁緩存(Page Cache)是位於內存和文件之間的緩衝區,它實際上也是一塊內存區域,全部的文件IO(包括網絡文件)都是直接和頁緩存交互,操做系統經過一系列的數據結構,好比inode, address_space, struct page,實現將一個文件映射到頁的級別,這些具體數據結構及之間的關係咱們暫且不討論,只需知道頁緩存的存在以及它在文件IO中扮演着重要角色,很大一部分程度上,文件讀寫的優化就是對頁緩存使用的優化緩存

Dirty Page網絡

頁緩存對應文件中的一塊區域,若是頁緩存和對應的文件區域內容不一致,則該頁緩存叫作髒頁(Dirty Page)。對頁緩存進行修改或者新建頁緩存,只要沒有刷磁盤,都會產生髒頁數據結構

查看頁緩存大小函數

linux上有兩種方式查看頁緩存大小,一種是free命令性能

$ free 
             total       used       free     shared    buffers     cached 
Mem:      20470840    1973416   18497424        164     270208    1202864 
-/+ buffers/cache:     500344   19970496 
Swap:            0          0          0

cached那一列就是頁緩存大小,單位Byte測試

另外一種是直接查看/proc/meminfo,這裏咱們只關注兩個字段優化

Cached:          1202872 kB
 Dirty:                52 kB

Cached是頁緩存大小,Dirty是髒頁大小

髒頁回寫參數

Linux有一些參數能夠改變操做系統對髒頁的回寫行爲

$ sysctl -a 2>/dev/null | grep dirty
vm.dirty_background_ratio = 10
vm.dirty_background_bytes = 0
vm.dirty_ratio = 20
vm.dirty_bytes = 0
vm.dirty_writeback_centisecs = 500
vm.dirty_expire_centisecs = 3000

vm.dirty_background_ratio是內存能夠填充髒頁的百分比,當髒頁總大小達到這個比例後,系統後臺進程就會開始將髒頁刷磁盤(vm.dirty_background_bytes相似,只不過是經過字節數來設置);vm.dirty_ratio是絕對的髒數據限制,內存裏的髒數據百分比不能超過這個值。若是髒數據超過這個數量,新的IO請求將會被阻擋,直到髒數據被寫進磁盤;vm.dirty_writeback_centisecs指定多長時間作一次髒數據寫回操做,單位爲百分之一秒;vm.dirty_expire_centisecs指定髒數據能存活的時間,單位爲百分之一秒,好比這裏設置爲30秒,在操做系統進行寫回操做時,若是髒數據在內存中超過30秒時,就會被寫回磁盤.

這些參數能夠經過 sudo sysctl -w vm.dirty_background_ratio=5 這樣的命令來修改,須要root權限,也能夠在root用戶下執行 echo 5 > /proc/sys/vm/dirty_background_ratio 來修改

文件讀寫流程

在有了頁緩存和髒頁的概念後,咱們再來看文件的讀寫流程

讀文件

1.用戶發起read操做
2.操做系統查找頁緩存
  a.若未命中,則產生缺頁異常,而後建立頁緩存,並從磁盤讀取相應頁填充頁緩存
  b.若命中,則直接從頁緩存返回要讀取的內容
3.用戶read調用完成

寫文件

1.用戶發起write操做
2.操做系統查找頁緩存
  a.若未命中,則產生缺頁異常,而後建立頁緩存,將用戶傳入的內容寫入頁緩存
  b.若命中,則直接將用戶傳入的內容寫入頁緩存
3.用戶write調用完成
4.頁被修改後成爲髒頁,操做系統有兩種機制將髒頁寫回磁盤
5.用戶手動調用fsync()
6.由pdflush進程定時將髒頁寫回磁盤

頁緩存和磁盤文件是有對應關係的,這種關係由操做系統維護,對頁緩存的讀寫操做是在內核態完成,對用戶來講是透明的

文件讀寫的優化思路

不一樣的優化方案適應於不一樣的使用場景,好比文件大小,讀寫頻次等,這裏咱們不考慮修改系統參數的方案,修改系統參數老是有得有失,須要選擇一個平衡點,這和業務相關度過高,好比是否要求數據的強一致性,是否容忍數據丟失等等。優化的思路有如下兩點:

1.最大化利用頁緩存

2.減小系統api調用次數

第一點很容易理解,儘可能讓每次IO操做都命中頁緩存,這比操做磁盤會快不少,第二點提到的系統api主要是read和write,因爲系統調用會從用戶態進入內核態,而且有些還伴隨這內存數據的拷貝,所以在有些場景下減小系統調用也會提升性能

readahead

readahead是一種非阻塞的系統調用,它會觸發操做系統將文件內容預讀到頁緩存中,而且立馬返回,函數原型以下

ssize_t readahead(int fd, off64_t offset, size_t count);

在一般狀況下,調用readahead後立馬調用read並不會提升讀取速度,咱們一般在批量讀取或在讀取以前一段時間調用readahead,假設以下場景,咱們須要連續讀取1000個1M的文件,有以下兩個方案,僞代碼以下

直接調用read函數

char* buf = (char*)malloc(10*1024*1024);
for (int i = 0; i < 1000; ++i)
{
    int fd = open_file();
    int size = stat_file_size();
    read(fd, buf, size);
    // do something with buf
    close(fd);
}

先批量調用readahead再調用read

int* fds = (int*)malloc(sizeof(int)*1000);
int* fd_size = (int*)malloc(sizeof(int)*1000);
for (int i = 0; i < 1000; ++i)
{
    int fd = open_file();
    int size = stat_file_size();
    readahead(fd, 0, size);
    fds[i] = fd;
    fd_size[i] = size;
}
char* buf = (char*)malloc(10*1024*1024);
for (int i = 0; i < 1000; ++i)
{
    read(fds[i], buf, fd_size[i]);
    // do something with buf
    close(fds[i]);
}

感興趣的能夠寫代碼實際測試一下,須要注意的是在測試前必須先回寫髒頁和清空頁緩存,執行以下命令

sync && sudo sysctl -w vm.drop_caches=3

可經過查看/proc/meminfo中的Cached及Dirty項確認是否生效

經過測試發現,第二種方法比第一種讀取速度大約提升10%-20%,這種場景下是批量執行readahead後立馬執行read,優化空間有限,若是有一種場景能夠在read以前一段時間調用readahead,那將大大提升read自己的讀取速度

這種方案其實是利用了操做系統的頁緩存,即提早觸發操做系統將文件讀取到頁緩存,而且操做系統對缺頁處理、緩存命中、緩存淘汰都由一套完善的機制,雖然用戶也能夠針對本身的數據作緩存管理,但和直接使用頁緩存比並無多大差異,並且會增長維護代價

mmap

mmap是一種內存映射文件的方法,即將一個文件或者其它對象映射到進程的地址空間,實現文件磁盤地址和進程虛擬地址空間中一段虛擬地址的一一對映關係,函數原型以下

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

實現這樣的映射關係後,進程就能夠採用指針的方式讀寫操做這一段內存,而系統會自動回寫髒頁面到對應的文件磁盤上,即完成了對文件的操做而沒必要再調用read,write等系統調用函數。以下圖所示

 

Linux文件讀寫機制及優化方式Linux文件讀寫機制及優化方式

mmap除了能夠減小read,write等系統調用之外,還能夠減小內存的拷貝次數,好比在read調用時,一個完整的流程是操做系統讀磁盤文件到頁緩存,再從頁緩存將數據拷貝到read傳遞的buffer裏,而若是使用mmap以後,操做系統只須要將磁盤讀到頁緩存,而後用戶就能夠直接經過指針的方式操做mmap映射的內存,減小了從內核態到用戶態的數據拷貝

 

mmap適合於對同一塊區域頻繁讀寫的狀況,好比一個64M的文件存儲了一些索引信息,咱們須要頻繁修改並持久化到磁盤,這樣能夠將文件經過mmap映射到用戶虛擬內存,而後經過指針的方式修改內存區域,由操做系統自動將修改的部分刷回磁盤,也能夠本身調用msync手動刷磁盤

免費提供最新Linux技術教程書籍,爲開源技術愛好者努力作得更多更好:http://www.linuxprobe.com/

相關文章
相關標籤/搜索