read系統調用的處理分爲用戶空間和內核空間處理兩部分。其中,用戶空間處理只是經過0x80中斷陷入內核,接着調用其中斷服務例程,即sys_read以進入內核處理流程。node
對於read系統調用在內核的處理,如上圖所述,通過了VFS、具體文件系統,如ext二、頁高速緩衝存層、通用塊層、IO調度層、設備驅動層、和設備層。其中,VFS主要是用來屏蔽下層具體文件系統操做的差別,對上提供一個統一接口,正是由於有了這個層次,因此能夠把設備抽象成文件。具體文件系統,則定義了本身的塊大小、操做集合等。引入cache層的目的,是爲了提升IO效率。它緩存了磁盤上的部分數據,當請求到達時,若是在cache中存在該數據且是最新的,則直接將其傳遞給用戶程序,免除了對底層磁盤的操做。通用塊層的主要工做是,接收上層發出的磁盤請求,並最終發出IO請求(BIO)。IO調度層則試圖根據設置好的調度算法對通用塊層的bio請求合併和排序,回調驅動層提供的請求處理函數,以處理具體的IO請求。驅動層的驅動程序對應具體的物理設備,它從上層取出IO請求,並根據該IO請求中指定的信息,經過向具體塊設備的設備控制器發送命令的方式,來操縱設備傳輸數據。設備層都是具體的物理設備。算法
內核函數sys_read是read系統調用在該層的入口點。緩存
它根據文件fd指定的索引,從當前進程描述符中取出相應的file對象,並調用vfs_read執行文件讀取操做。函數
vfs_read會調用與具體文件相關的read函數執行讀取操做,file->f_op.read。spa
而後,VFS將控制權交給了ext2文件系統。(ext2在此做爲示例,進行解析)指針
經過ext2_file_operations結構知道,上述函數最終會調用到do_sync_read函數,它是系統通用的讀取函數。因此說,do_sync_read纔是ext2層的真實入口。orm
該層入口函數 do_sync_read 調用函數 generic_file_aio_read ,後者判斷本次讀請求的訪問方式,若是是直接 io (filp->f_flags 被設置了 O_DIRECT 標誌,即不通過 cache)的方式,則調用 generic_file_direct_IO 函數;若是是 page cache 的方式,則調用 do_generic_file_read 函數。它會判斷該頁是否在頁高速緩存,若是是,直接將數據拷貝到用戶空間。若是不在,則調用page_cache_sync_readahead函數執行預讀(檢查是否能夠預讀),它會調用mpage_readpages。若是仍然未能命中(可能不容許預讀或者其它緣由),則直接跳轉readpage,執行mpage_readpage,從磁盤讀取數據。對象
在mpage_readpages(一次讀多個頁)中,它會將連續的磁盤塊放入同一個BIO,並延緩BIO的提交,直到出現不連續的塊,則直接提交BIO,再繼續處理,以構造另外的BIO。排序
圖5顯示了一個文件的 page cache 結構。文件被分割爲一個個以 page 大小爲單元的數據塊,這些數據塊(頁)被組織成一個多叉樹(稱爲 radix 樹)。樹中全部葉子節點爲一個個頁幀結構(struct page),表示了用於緩存該文件的每個頁。在葉子層最左端的第一個頁保存着該文件的前4096個字節(若是頁的大小爲4096字節),接下來的頁保存着文件第二個4096個字節,依次類推。樹中的全部中間節點爲組織節點,指示某一地址上的數據所在的頁。此樹的層次能夠從0層到6層,所支持的文件大小從0字節到16 T 個字節。樹的根節點指針能夠從和文件相關的 address_space 對象(該對象保存在和文件關聯的 inode 對象中)中取得。
mpage處理機制就是page cache層要處理的問題。
在緩存層處理末尾,執行mpage_submit_bio以後,會調用generic_make_request函數。這是通用塊層的入口函數。
它將bio傳送到IO調度層進行處理。
對bio進行合併、排序,以提升IO效率。而後,調用設備驅動層的回調函數,request_fn,轉到設備驅動層處理。
request函數對請求隊列中每一個bio進行分別處理,根據bio中的信息向磁盤控制器發送命令。處理完成後,調用完成函數end_bio以通知上層完成。