linux檔案讀取中的Readahead預讀機制

2021-10-06 19:46:56 字數 4187 閱讀 5556

由於記憶體的速度比磁碟速度快很多,如果每一次訪問檔案資料都要從磁碟讀取一次資料,就會導致非常嚴重的時延。因此linux為了提高效能,通過page cache機制,將多個使用者資料快取在記憶體當中,從而避免多次再磁碟讀取。readahead預讀機制正是將使用者資料快取到記憶體的方法之一。

readahead預讀機制是linux針對順序讀的效能優化機制。它的核心思想是當使用者訪問連續多個page的時候,一次性將多個連續的頁從磁碟讀取到記憶體中,從而避免多次與磁碟互動,降低效能。

預讀行為的在generic_file_buffered_read函式做了具體實現,如下:

static ssize_t generic_file_buffered_read(struct kiocb *iocb,

struct iov_iter *iter, ssize_t written)

if (pagereadahead(page))

}...

}

從上面可以知道,當find_get_page函式無法在page cache中找到該index對應的page的例項,就會呼叫page_cache_sync_readahead函式進行同步預讀,預讀完的資料就會加入到page cache中,再一次呼叫find_get_page函式就可以獲取出來。

page index=1的頁以及非同步預讀出來的最後乙個頁會包含特殊標誌,這個標誌可以通過pagereadahead函式進行判斷,從而判斷是否需要呼叫page_cache_async_readahead進行非同步預讀。

同步預讀以及非同步預讀都會呼叫同乙個函式ondemand_readahead,只是輸入引數不一樣,如下所示:

struct file_ra_state *ra, struct file *filp,

pgoff_t offset, unsigned long req_size)}

struct file_ra_state *ra, struct file *filp,

struct page *page, pgoff_t offset,

unsigned long req_size)

根據**,同步預讀和非同步預讀傳入ondemand_readahead函式的第四個引數不一樣,乙個是false乙個是true。為了搞清楚差異,繼續分析ondemand_readahead函式的原始碼。

static unsigned long

struct file_ra_state *ra, struct file *filp,

bool hit_readahead_marker, pgoff_t offset,

unsigned long req_size)

/** 非同步預讀的時候會進入這個判斷,更新ra的值,然後預讀特定的範圍的頁

* 非同步預讀的呼叫表示readahead出來的頁連續命中

*/if (hit_readahead_marker)

if (req_size > max_pages)

goto initial_readahead;

prev_offset = (unsigned long long)ra->prev_pos >> page_shift;

if (offset - prev_offset <= 1ul)

goto initial_readahead;

goto readit;

// 這個函式執行具體的從磁碟讀取的流程

initial_readahead:

ra->start = offset;

/* get_init_ra_size初始化第一次預讀的頁的個數,一般情況下第一次預讀是4個頁 */

ra->size = get_init_ra_size(req_size, max_pages);

ra->async_size = ra->size > req_size ? ra->size - req_size : ra->size;

readit:

if (offset == ra->start && ra->size == ra->async_size) else

}/* 

* 經過一點處理以後,會呼叫__do_page_cache_readahead函式,執行具體的從磁碟讀取的流程 

* 區別在於它是基於ra->start ra->async_size等資訊進行讀取

* */

}

當第乙個頁(page index=0)傳入函式時,跳到initial_readahead部分,初始化ra->start、ra->size以及ra->async_size等資訊,然後呼叫ra_submit進行讀取。

當第乙個頁以外傳入函式時,需要根據hit_readahead_marker判斷同步預讀還是非同步預讀,同步則根據offset和req_size進行預讀,如果是非同步則通過ra->start以及ra->async_size進行預讀。

ondemand_readahead函式的核心是__do_page_cache_readahead函式,它會根據傳入的引數,從磁碟讀取特定範圍的資料:

struct file *filp, pgoff_t offset, unsigned long nr_to_read,

unsigned long lookahead_size)

// 如果不存在,則建立乙個page cache結構

page = __page_cache_alloc(gfp_mask);

if (!page)

break;

// 設定page cache的index

page->index = page_offset;

// 加入到list當中

list_add(&page->lru, &page_pool);

// !!! 注意計算值,給這乙個頁加上readahead的標誌

if (page_idx == nr_to_read - lookahead_size)

setpagereadahead(page);

nr_pages++;}/*

* 如果nr_pages大於0,則表示有頁要進行讀取

* 執行read_pages從磁碟進行讀取

*/if (nr_pages)

out:

return nr_pages;

}下面通過乙個順序讀的例子說明linux預讀機制的執行:

使用者需要連續訪問某個檔案連續32個頁(page index=0~31)的資料,那麼它在預讀機制下的訪問行為是:

使用者訪問第乙個頁,page index=0,觸發同步預讀機制,一次性從磁碟讀取4個頁,即第1~4個頁(page index=0~3)

使用者訪問第二個頁,page index=1,含有特殊標誌,觸發非同步預讀機制,一次性從磁碟讀取8個頁,即第5~12個頁(page index=4~11)

使用者訪問第三個頁,page index=2,命中,直接返回給使用者

使用者訪問第四個頁,page index=3,命中,直接返回給使用者

使用者訪問第五個頁,page index=4,含有特殊標誌,觸發非同步預讀機制,一次性從磁碟讀取16個頁,即第13~28個頁(page index=12~27)

使用者訪問第六個頁,page index=5,命中,直接返回給使用者

使用者訪問第七個頁,page index=6,命中,直接返回給使用者

使用者訪問第八個頁,page index=7,命中,直接返回給使用者

使用者訪問第九個頁,page index=8,命中,直接返回給使用者

使用者訪問第十個頁,page index=9,命中,直接返回給使用者

使用者訪問第十乙個頁,page index=10,命中,直接返回給使用者

使用者訪問第十二個頁,page index=11,命中,直接返回給使用者

使用者訪問第十三個頁,page index=12,含有特殊標誌,觸發非同步預讀機制,一次性從磁碟讀取32個頁,即第12~42個頁(page index=13~43),但是由於page index=12~27的頁上一次預讀就將頁讀入了page cache,因此會跳過,實際上只會從磁碟讀取page index=28~43頁。

…以此類推

當訪問第乙個頁時,跳到initial_readahead部分,初始化ra->start=0、ra->size=4以及ra->async_size=3,因此預讀取了page index=0~3的頁,並且給page index=1的頁加上了標誌。

訪問第二個頁(page index=1)時,由於有預讀標誌,因此進行非同步預讀。非同步預讀會增大預讀頁數,將預讀頁數由4個頁增大到8個頁,以次類推。

C 通過Read函式讀取檔案

通過read函式將檔案中的資料按照一定的長度讀取出來並且存放在新的陣列中。函式原型istream read char s,streamsize n 用來暫存內容的陣列 必須是char 型 以及流的長度比如我們要讀取txt檔案中的內容ifstream in test.txt 先通過檔案流將流與txt檔...

使用while和read命令讀取檔案內容

1 準備資料檔案 cat a.txt 200 2 300 3 400 4 500 5 2 用while 迴圈從檔案中讀取資料 bin ksh while read line doecho line done a.txt 執行shell 結果如下 200 2 300 3 400 4 500 5 3 使...

Linux 檔案管理 read 命令詳解

read命令從鍵盤讀取變數的值,通常用在shell指令碼中與使用者進行互動的場合。該命令可以一次讀取多個變數的值,變數和輸入的值都需要使用空格隔開。在read命令後面,如果沒有指定變數名,讀取的資料將被自動賦值給特定的變數reply read 選項 引數 引數說明 下面的列表給出了read命令的常用...