一. 預讀算法觸發條件
預讀觸發也就是什麼時候進行預讀算法判别?下面代碼2.6.32核心,分析代碼可見預讀算法觸發條件為兩種:
頁面缺失(頁緩存沒有找到)和讀取含預讀标志的頁面。
static void do_generic_file_read(struct file *filp, loff_t *ppos,
read_descriptor_t *desc, read_actor_t actor)
{
for (;;) {
page = find_get_page(mapping, index);
if (!page) {//頁緩存缺失(頁緩存沒有此頁面)
//進行同步預讀,意思使用者程序必須等待頁面讀入
page_cache_sync_readahead(mapping,
ra, filp,
index, last_index - index);
page = find_get_page(mapping, index);
if (unlikely(page == NULL))
goto no_cached_page;
}
if (PageReadahead(page)) { //頁緩存命中(頁面已在頁緩存中),此頁面含預讀标志,觸發異步預讀,所謂異步預讀就是使用者程序不必等待預讀i/o磁盤操作。
page_cache_async_readahead(mapping,
ra, filp, page,
index, last_index - index);
}
//頁緩存命中(頁面已在頁緩存中),此不頁面含預讀标志,不必觸發預讀操作,隻讀取此頁面内容即可
。。。。。。。。。。。。。
}
二. 預讀算法
預讀狀态的資料結構
struct file_ra_state {
pgoff_t start;
unsigned int size;
unsigned int async_size;
unsigned int ra_pages;
unsigned int mmap_miss;
loff_t prev_pos;
};
start 和 size 構成一個預讀視窗,記錄了最近一次預取請求的位置和大小;
async_size 訓示了異步預取的位置提前量,即還剩餘多少未通路的預取頁面時啟動下一次預取 I/O;
prev_pos 記錄了最後的讀位置,可用于順序性測試。
預取算法核心内容預讀視窗設定,也就是多大合适?2.6.32核心預取視窗設定根據檔案讀操作方式來設定,檔案讀操作分為兩種順序讀和随機讀。随機讀無預取視窗而言。對于順序讀而言分為依次判斷是否初始化讀,順序讀,交織讀和其他方式讀來設定。
預讀視窗設定,可有三元組表示(start,size, async_size)
讀請求可有二進制組表示(offset,req_size)(注意區分讀請求和預讀請求)
預讀視窗根據讀請求而設定的
1).初始化讀:
就是從檔案頭開始讀取檔案,即檔案偏移量為0。預讀視窗設定:
initial_readahead:
ra->start = offset; //預讀視窗開始
ra->size = get_init_ra_size(req_size, max);//預讀視窗大小
ra->async_size = ra->size > req_size ? ra->size - req_size : ra->size; //預讀提前量,還有多頁開始預讀
假設讀請求為(0,1),那麼預讀視窗為(0,4,3)
2).順序讀:
所謂順序讀基于上一次讀請求而言,上一次讀請求為(0,1),接下來話順序讀為(1,-)。
如果偏移量等于最大預讀視窗或者等于需要預讀線,設定預讀視窗為
上一次預讀視窗為(0,4,3),本次預讀視窗為(4,8,8)
if ((offset == (ra->start + ra->size - ra->async_size) ||
offset == (ra->start + ra->size))) {
ra->start += ra->size;
ra->size = get_next_ra_size(ra, max);
ra->async_size = ra->size;
goto readit;
}
3).交織讀:
在交織的順序讀中,在同一fd, 并發流使互相預讀狀态無效。判斷條件:檔案頁緩存存在空洞
if (hit_readahead_marker) {
pgoff_t start;
rcu_read_lock();
start = radix_tree_next_hole(&mapping->page_tree, offset+1,max);
rcu_read_unlock();
if (!start || start - offset > max)
return 0;
ra->start = start;
ra->size = start - offset;
ra->size += req_size;
ra->size = get_next_ra_size(ra, max);
ra->async_size = ra->size;
goto readit;
}
其他方式:
4). 大檔案讀:也被當作順序讀,啟動預讀視窗設定
判斷條件:讀取檔案大小大于預取最大視窗
if (req_size > max)
goto initial_readahead;
5). 非對齊讀:
檔案讀請求(offset )的機關是位元組,當一個讀請求不是正好開始或結束于頁面邊界的時候,它就構成了一個非對齊讀。 非對齊讀識别條件上次結束與本次開始正好小于一個頁面。
判斷條件:offset - (ra->prev_pos >> PAGE_CACHE_SHIFT) <= 1UL
if (offset - (ra->prev_pos >> PAGE_CACHE_SHIFT) <= 1UL)
goto initial_readahead;
6). 丢失的順序讀:
查詢頁面緩存,分析長期運作的程式的曆史緩存情況,是否存在順序讀,存在話設定預讀視窗。
判斷條件:已檔案頁緩基樹,是否有順序讀情況。
if (try_context_readahead(mapping, ra, offset, req_size, max))
goto readit;
7). 不是上述情況,最後就是随機讀:
return __do_page_cache_readahead(mapping, filp, offset, req_size, 0);
static unsigned long
ondemand_readahead(struct address_space *mapping,
struct file_ra_state *ra, struct file *filp,
bool hit_readahead_marker, pgoff_t offset,
unsigned long req_size)
{
unsigned long max = max_sane_readahead(ra->ra_pages);
if (!offset)
goto initial_readahead;
if ((offset == (ra->start + ra->size - ra->async_size) ||
offset == (ra->start + ra->size))) {
ra->start += ra->size;
ra->size = get_next_ra_size(ra, max);
ra->async_size = ra->size;
goto readit;
}
if (hit_readahead_marker) {
pgoff_t start;
rcu_read_lock();
start = radix_tree_next_hole(&mapping->page_tree, offset+1,max);
rcu_read_unlock();
if (!start || start - offset > max)
return 0;
ra->start = start;
ra->size = start - offset;
ra->size += req_size;
ra->size = get_next_ra_size(ra, max);
ra->async_size = ra->size;
goto readit;
}
if (req_size > max)
goto initial_readahead;
if (offset - (ra->prev_pos >> PAGE_CACHE_SHIFT) <= 1UL)
goto initial_readahead;
if (try_context_readahead(mapping, ra, offset, req_size, max))
goto readit;
return __do_page_cache_readahead(mapping, filp, offset, req_size, 0);
initial_readahead:
ra->start = offset;
ra->size = get_init_ra_size(req_size, max);
ra->async_size = ra->size > req_size ? ra->size - req_size : ra->size;
readit:
if (offset == ra->start && ra->size == ra->async_size) {
ra->async_size = get_next_ra_size(ra, max);
ra->size += ra->async_size;
}
return ra_submit(ra, mapping, filp);
}