1. 程式人生 > >linux檔案系統初始化過程(5)---載入initrd(下)

linux檔案系統初始化過程(5)---載入initrd(下)

一、目的

    linux把檔案分為常規檔案、目錄檔案、軟連結檔案、硬連結檔案、特殊檔案(裝置檔案、管道檔案、socket檔案等)幾種型別,分別對應不同的新建函式sys_open()sys_mkdir()sys_symlink()sys_link()sys_mknod()

    系統初始化階段成功載入initrd後,呼叫這些介面函式建立各種檔案,因此這些函式在linux檔案系統初始化過程中起到了重要作用,本文將詳細描述這些介面函式的實現過程。

    這些介面函式主要在fs/namei.cfs/open.c檔案中實現,可以在這兩個檔案中找到對應的原始碼。

二、快速路徑查詢

    在以上系統呼叫的實現中,需要解決一個關鍵問題:如何根據待新建檔案的路徑名,快速找到父目錄的位置。得到父目錄的位置後,才能建立待新建檔案的目錄,以及分配

inode節點。

    例如:新建/tmp/test.txt檔案,首先需要根據”/tmp/test.txt”路徑名查詢到test.txt父目錄tmpVFS中的位置,然後基於tmp的位置才能為test.txt檔案新建目錄及分配inode節點。

    由於快速路徑查詢使用比較頻繁,所以對查詢效率要求較高,否則會影響系統性能。do_path_lookup(intdfs, const char *name, unsigned int flags, struct nameidata*nd)函式負責實現該功能,其中name引數是路徑名,nd引數是structnameidata結構體,ndpath成員記錄了父目錄的位置,

last成員記錄了待新建檔案的檔名。例如:path記錄了tmpVFS中的位置,last記錄”test.txt”檔名。

    該函式的實現比較複雜,不方便對照原始碼講述,因此下面重點描述了該函式的主要操作。但是為了讀者方便閱讀原始碼,給出如下關鍵函式的呼叫關係:do_path_lookup()->path_lookupat()->path_init()->link_path_walk()->walk_component()->lookup_fast()->__d_look_rcu()->__follow_mount_rcu()

    do_path_lookup()

在實現過程中,按照以下三種情形來操作:

    1、路徑名在單檔案系統中:從根目錄或當前目錄開始,根據目錄拓撲結構,遞迴查詢父目錄;

    2、路徑名在多檔案系統中:在遞迴查詢時,需要從當前檔案系統的掛載點切換到最終檔案系統的目錄拓撲結構中,然後繼續查詢;

    3、路徑名在掛載點重複掛載多檔案系統的情況:在遞迴查詢時,需要從當前檔案系統的掛載點切換到最終檔案系統的目錄拓撲結構中,然後繼續查詢;

    為了理解以上三種情形的具體差異,舉例說明:待新建檔案路徑名為”/usr/tmp/log/new.txt”。

情形1

    如下圖所示,路徑名只在單個檔案系統ext3中存在,所以根據目錄拓撲結構,遞迴查詢父目錄即可;查詢完成後,使用nd.path記錄父目錄log的位置,nd.last記錄了字串常量”new.txt”


情形2

    在情形2中,路徑名橫跨了兩個檔案系統,minix掛載在ext3tmp目錄上;當成功掛載minix後,設定掛載點tmp為已掛載狀態,並且將tmp指向minix檔案系統。

    路徑查詢程式根據路徑名”/usr/tmp/log/new.txt”查詢到tmp目錄時,發現tmp為已掛載狀態,所以從掛載點tmp切換到minix根目錄;然後,確認minix根目錄不是已掛載狀態後,在minix檔案系統中繼續查詢剩下的路徑名”log/new.txt”;查詢完成後,使用nd記錄查詢結果。


情形3

    在情形3中,ext3tmp目錄重複掛載了兩個檔案系統minixnfs;當成功掛載minix後,設定掛載點tmp為已掛載狀態;當在同一掛載點tmp掛載nfs時,發現tmp已經是掛載狀態,所以從掛載點tmp切換到minix根目錄,在該根目錄掛載nfs檔案系統,並且將minix根目錄設定為已掛載狀態。linux支援在同一掛載點掛載多個檔案系統的操作,但是隻有最後被掛載的檔案系統才是可見的,所以使用ls命令只能看到nfs檔案系統的內容。

    路徑查詢程式根據路徑名”/usr/tmp/log/new.txt”查詢到tmp目錄時,發現tmp為已掛載狀態,所以從掛載點tmp切換到minix根目錄;然後發現minix根目錄也是已掛載狀態,所以繼續切換到nfs根目錄;最後,確認nfs根目錄不是已掛載狀態後,在nfs檔案系統中繼續查詢剩下的路徑名”log/new.txt”;查詢完成後,使用nd記錄查詢結果。


    在以上操作中,由__follow_mount_rcu()函式負責目錄掛載狀態檢測和切換操作。

三、新建檔案系統呼叫

    do_path_lookup()函式返回的structnameidata *nd資料結構,記錄了待新建檔案父目錄的位置,所以根據nd記錄的資訊,在VFS樹中新建檔案就變得相對簡單了。

    由於檔案系統的操作比較複雜,因此不對原始碼進行詳解,主要介紹系統呼叫的主要功能(圖中綠色部分),但是給出了關鍵函式呼叫路徑,便於讀者查閱細節內容。

3.1、新建常規檔案系統呼叫sys_open()

    注:這裡重點介紹sys_open()的新建功能,忽略開啟功能(開啟功能比較複雜也與主題不符),所以讀者不能片面認為sys_open()的功能只是新建檔案。

    關鍵函式呼叫路徑如下:


    主要功能總結:

    1get_unused_fd_flags()新建檔案描述符;

    2do_filp_open()建立常規檔案的file結構體、目錄項、inode節點,並將三者關聯起來;

       2.1get_empty_filp()建立常規檔案的file結構體;

       2.2lookup_open()呼叫lookup_dcache()建立常規檔案的目錄專案;

       2.3lookup_open()呼叫vfs_create()建立常規檔案的inode節點;

    3fd_install()將檔案描述符指向file結構體。

    具體操作流程如下圖所示:


3.2、新建硬連結檔案系統呼叫sys_link()

    關鍵函式呼叫路徑如下:


    主要功能總結:

1user_path_at()返回硬連結目標檔案的目錄項位置;

2user_path_create()建立硬連結檔案的目錄項;

2.1do_path_lookup()返回硬連結檔案的父目錄位置

2.2lookup_hash()根據父目錄位置,建立硬連結檔案的目錄項;

3vfs_link()將硬連結檔案指向硬連結目標檔案的inode節點。

    具體操作流程如下圖所示:


3.3、新建目錄檔案系統呼叫sys_mkdir()

    關鍵函式呼叫路徑如下:


    主要功能總結:

    1user_path_create()建立目錄檔案的目錄項

       1.1do_path_lookup()返回目錄檔案的父目錄位置;

       1.2lookup_hash()根據父目錄位置,建立目錄檔案的目錄項;

    2vfs_mkdir()建立目錄檔案的inode節點;

    具體操作流程如下圖所示:


3.4、新建軟連結檔案系統呼叫sys_symlink()

    關鍵函式呼叫路徑:


    主要功能總結:

    1user_path_create()建立軟連結檔案的父目錄位置;

       1.1do_path_lookup()返回軟連結檔案的父目錄位置;

       1.2lookup_hash()根據父目錄位置,建立軟連結檔案的目錄項;

    2vfs_symlink()建立軟連結檔案的inode節點,並且inode節點記錄了軟連結目標檔案的位置;簡單來說,軟連結檔案的內容就是軟連結目標檔案的路徑。

    具體操作流程如下圖所示:


3.5、新建特殊檔案系統呼叫sys_mknod()

    關鍵函式呼叫路徑:


    主要功能總結:

    1user_path_create()建立特殊檔案的父目錄位置;

       1.1do_path_lookup()返回特殊檔案的父目錄位置;

       1.2lookup_hash()根據父目錄位置,建立特殊檔案的目錄項;

    2vfs_mknod()建立特殊檔案的inode節點,初始化inodei_rdev成員,並且將i_fop成員指向預設檔案操作;

    注:當用戶使用sys_open()開啟特殊檔案時,會呼叫預設檔案操作的open成員,把裝置檔案掛接到inode的裝置連結串列中,並且將i_fop成員重新指向該裝置的檔案操作(即裝置驅動)。

    具體操作流程如下圖所示:


    通過以上介紹,可以看出快速路徑查詢函式do_path_lookup()在新建檔案系統呼叫中起到了基礎性的作用,因此有必要掌握該函式的用法。

四、VFS全景圖

    到目前為止,initrd的載入過程就全部結束了;最後,為了更清晰的理解VFS此時的全貌,給出如下VFS全景圖。

    從圖中看出以下特點:

    1sysfs檔案系統目前還沒有掛載到rootfs的某個掛載點上,後續init程式會把sysfs掛載到rootfssys掛載點上;

    2、系統打開了/dev/console裝置,說明系統已經可以使用該裝置,列印資訊也可以正常輸出;

    3rootfs檔案系統的根目錄下已經準備好了init程式,核心後續會啟動該程式完成剩下的初始化操作。


五、總結

    目前為止,linux成功載入了initrd檔案後,在核心中構建了一個基於記憶體的檔案系統rootfsVFS再也不是隻有根目錄、結構簡單的小樹苗了;現在,它已經成長為擁有目錄、常規檔案、連結檔案、裝置檔案等多種檔案型別、結構複雜的參天大樹了。

    原創作品,如非商業性轉載,請註明出處;如商業性轉載出版,請與作者聯絡。