1. 程式人生 > >Linux檔案系統學習(四)之read open系統呼叫

Linux檔案系統學習(四)之read open系統呼叫

open的執行過程:v2.6.30 

Open

Sys_open

|do_sys_open()

|get_unused_fd_flags ()//得到一個可用的檔案描述符;通過該函式,可知檔案描述符

                                                                              //實質是程序開啟檔案列表中對應某個檔案物件的索引值;

|do_filp_open()//開啟檔案,返回一個file物件,代表由該程序開啟的一個文

                                                                        //

件;程序通過這樣的一個數據結構對物理檔案進行讀寫操作。

|if(!(flag & O_CREAT))//不是要建立

|path_lookup_open()//根據檔案路徑名查詢檔案,並初始化struct nameidata物件

| do_path_lookup()

|goto OK;

|else //建立一個新的檔案

|do_path_lookup()//查詢父目錄

|__open_namei_create

                                        |vfs_create()//建立inodevfs_create()裡的一句核心語句dir->i_op->create(dir, dentry, mode, nd)

 可      知它呼叫了具體的檔案系統所提供的建立索引節點的方法。注意:這邊的索引節點的概念,還只是位於記憶體之中,它和磁碟上的物理的索引節點的關係就像位於記憶體中和位於磁碟中的檔案一樣。此時新建的索引節點還不能完全標誌一個物理檔案的成功建立,只有當把索引節點回寫到磁碟上才是一個物理檔案的真正建立。想想我們以新建的方式開啟一個檔案,對其讀寫但最終沒有儲存而關閉,則位於記憶體中的索引節點會經歷從新建到消失的過程,而磁碟卻始終不知道有人曾經想過建立一個檔案,這是因為索引節點沒有回寫的緣故。

                                           |may_open()//

檢查是否可以開啟該檔案;一些檔案如連結檔案和

只有寫許可權的目錄是不能被開啟的,先檢查nd->dentry->inode所指的檔案是否是這一類檔案,是的話則錯誤返回。還有一些檔案是不能以TRUNC的方式開啟的,若nd->dentry->inode所指的檔案屬於這一類,則顯式地關閉TRUNC標誌位。接著如果有以TRUNC方式開啟檔案的,則更新nd->dentry->inode的資訊

                            |nameidata_to_filp()//nameidata轉換為一個openfile filp

|__dentry_open()//將呼叫實際檔案的操作方法賦值給file物件,這樣當最後通過統一的系統呼叫處理file物件的時候,就會呼叫正確的實際檔案系統方法。

?end do_filp_open

|fd_install()//建立檔案描述符與file物件的聯絡,即把file物件賦值到fd陣列中,以後程序對檔案的讀寫就可以通過操縱該檔案描述符而進行。

           ?end do_sys_open

?end open

通過以上的過程可以把open過程總結如下:

1首先獲得一個未使用的檔案描述符

2然後通過把路徑解析為各個遞進的目錄項物件(如把/home/test/a.txt解析為”/””home””test””a.txt”),來查詢實際的檔案(也可以是路徑)是否存在,該過程就是為了獲得檔案的inode節點,並最終賦值給可操作的file物件

查詢的過程首先判斷該目錄(如”/”,由d_hash計算查詢)是否存在於dentry cache當中,如果存在則不需要再建立該目錄項物件,直接得到目錄項物件(此時說明inode也存在於inode cache中)所以就可以直接得到inode節點,然後開啟該檔案(這裡就是”/”目錄),然後依次類推,直接找到a.txtinode節點為止。如果該目錄對應的目錄項物件不存在於dentry cache中,則先建立一個目錄項物件,然後再在磁碟中查詢該目錄項物件對應的inode節點是否存在,如果存在則快取到cache中,並查詢下一級目錄,如果不存在並且falg標誌為O_CREAT的話,則建立一個inode節點。因為只有得到inode節點,才能知道檔案的所在磁碟位置,以及相應的操作方法。

3 建立檔案描述符與file物件的聯絡

下來看一下read的系統呼叫過程如下圖:

read的呼叫過程  

上圖描述了從使用者空間的read()呼叫到資料從磁碟讀出的整個流程。當在使用者應用程式呼叫檔案I/O read()操作時,系統呼叫sys_read()被激發,sys_read()找到檔案所在的具體檔案系統,把控制權傳給該檔案系統,最後由具體檔案系統與物理介質互動,從介質中讀出資料。

對檔案進行讀操作時,需要先開啟它。在開啟一個檔案(open)時,會在記憶體組裝一個檔案物件,最後對該檔案執行的操作方法已在檔案物件設定好。所以對檔案進行讀操作時,VFS在做了一些簡單的轉換後(由檔案描述符得到其對應的檔案物件;其核心思想是返回current->files->fd[fd]所指向的檔案物件),就可以通過語句file->f_op->read(file, buf, count, pos)輕鬆呼叫實際檔案系統的相應方法對檔案進行讀操作了。


系統呼叫在核心空間中的處理層次

上圖顯示了 read 系統呼叫在核心空間中所要經歷的層次模型。從圖中看出:對於磁碟的一次讀請求,首先經過虛擬檔案系統層(vfs layer),其次是具體的檔案系統層(例如 ext2),接下來是 cache 層(page cache 層)、通用塊層(generic block layer)、IO 排程層(I/O scheduler layer)、塊裝置驅動層(block device driver layer),最後是物理塊裝置層(block device layer)。

·虛擬檔案系統層的作用:遮蔽下層具體檔案系統操作的差異,為上層的操作提供一個統一的介面。正是因為有了這個層次,所以可以把裝置抽象成檔案,使得操作裝置就像操作檔案一樣簡單。

·在具體的檔案系統層中,不同的檔案系統(例如 ext2 NTFS)具體的操作過程也是不同的。每種檔案系統定義了自己的操作集合。關於檔案系統的更多內容,請參見參考資料。

·引入 cache 層的目的是為了提高 linux 作業系統對磁碟訪問的效能。 Cache 層在記憶體中快取了磁碟上的部分資料。當資料的請求到達時,如果在 cache 中存在該資料且是最新的,則直接將資料傳遞給使用者程式,免除了對底層磁碟的操作,提高了效能。

·通用塊層的主要工作是:接收上層發出的磁碟請求,並最終發出 IO 請求。該層隱藏了底層硬體塊裝置的特性,為塊裝置提供了一個通用的抽象檢視。

·IO 排程層的功能:接收通用塊層發出的 IO 請求,快取請求並試圖合併相鄰的請求(如果這兩個請求的資料在磁碟上是相鄰的)。並根據設定好的排程演算法,回撥驅動層提供的請求處理函式,以處理具體的 IO 請求。

·驅動層中的驅動程式對應具體的物理塊裝置。它從上層中取出 IO 請求,並根據該 IO 請求中指定的資訊,通過向具體塊裝置的裝置控制器傳送命令的方式,來操縱裝置傳輸資料。

裝置層中都是具體的物理裝置。定義了操作具體裝置的規範。