本文只討論執行"mount none /mnt/huge -t hugetlbfs"命令后,mount系統調用的執行過程(基於Linux-3.4.51),不涉及進程相關的細節。
mount系統調用的內核實現:
1 SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, 2 char __user *, type, unsigned long, flags, void __user *, data) 3 { 4 int ret; 5 char *kernel_type; 6 char *kernel_dir; 7 char *kernel_dev; 8 unsigned long data_page; 9 10 /* 從用戶空間copy文件系統類型,文件系統類型字符串長度不能大於PAGE_SIZE,即,不能大與4K。 11 * kernel_type = "hugetlbfs" 12 */ 13 ret = copy_mount_string(type, &kernel_type); 14 if (ret < 0) 15 goto out_type; 16 17 /* 從用戶空間獲取掛載點, kerne_dir = "/mnt/huge" */ 18 kernel_dir = getname(dir_name); 19 if (IS_ERR(kernel_dir)) { 20 ret = PTR_ERR(kernel_dir); 21 goto out_dir; 22 } 23 24 /*從用戶空間獲取掛載設備,kernel_dev = "none"*/ 25 ret = copy_mount_string(dev_name, &kernel_dev); 26 if (ret < 0) 27 goto out_dev; 28 29 /*獲取mount命令的其它參數*/ 30 ret = copy_mount_options(data, &data_page); 31 if (ret < 0) 32 goto out_data; 33 34 /*處理具體的mount細節*/ 35 ret = do_mount(kernel_dev, kernel_dir, kernel_type, flags, 36 (void *) data_page); 37 38 free_page(data_page); 39 out_data: 40 kfree(kernel_dev); 41 out_dev: 42 putname(kernel_dir); 43 out_dir: 44 kfree(kernel_type); 45 out_type: 46 return ret; 47 }
相關參數處理完之后,具體的Mount操作由do_mount()函數實現,do_mount()主要分為兩部分來實現,一是找到裝載點的dentry項,二是創建hugetlbfs的super_block、vfsmount、已經掛載點dentry等相關數據結構之間的關聯。
一、kern_path()查找掛載點
path_init():查找掛載點路徑名(即,/mnt/huge)的搜索起點的根目錄,保存在數據結構struct nameidata中。
nd->root = current->fs->root;
nd->path = nd->root;
nd->inode = nd->path.dentry->d_inode;
link_path_walk():該函數由一個大循環組成,逐分量處理文件名或路徑名。名稱在循環內分為各個分量(各分量通過一個或多個“/”分割)。每個分量表示一個目錄名,最后一個分量是文件名。在每一個循環周期中,直至指定的文件名或目錄名處理完畢並找到匹配的inode。
具體如下:
1、逐字符掃描路徑名,斜線“/”會被跳過。
2、may_lookup()判斷當前inode是否定義了permission方法,來采用不同的方法判斷當前進程是否允許進入該目錄。
3、判斷當前分量是"."或者“..”,如果是“..”,則設置標志LAST_DOTDOT,最后調用walk_component()--->handle_dots()--->follow_dotdot()處理。如果是".",則設置標志LAST_DOT。
4、如果是普通分量,則調用walk_component()--->do_lookup()處理。do_lookup()處理實際的查找工作,查找分量對應的dentry。首先從上一級目錄的dentry中查找inode,並調用d_revalidate()檢查該緩存項是否有效(即,是否和實際文件系統的中的數據一致),如果有效,則返回;如果無效,則調用__lookup_hash()繼續查找,首先調用lookup_dcache()查找緩存中是否存在,如果不存在,繼續調用lookup_real(),調用具體的文件系統的lookup函數查找。
do_lookup()也處理跟蹤掛載點的工作,也需要判斷下級目錄是否也掛載文件系統,__follow_mount_rcu()負責具體處理,在這里不做討論。
5、nested_symlink()判斷分量是否是符號鏈接。只有用於表示符號鏈接的inode,其inode_operations中的lookup函數指針才有具體的值,否則為NULL。
complete_walk():該函數做一些相關檢查,確認是否需再次調用d_revalidate()判斷緩存項是否有效。
二、do_new_mount()裝載文件系統
do_new_mount()分為兩個部分:do_kern_mount()和do_add_mount()。
do_kern_mount():首先調用get_fs_type()獲取對應的已注冊的文件系統類型。對於hugetlbfs來說,對應的內核文件系統類型是hugetlbfs_fs_type。獲取對應的文件系統類型后,首先調用alloc_vfsmnt()分配並初始化mount數據結構,再調用mount_fs(),進一步調用hugetlbfs_mount(),分配掛載點的super_block、dentry、inode並建立相關映射。最后建立mount、super_block、dentry之間的映射。具體的映射關系如http://www.cnblogs.com/MerlinJ/p/4053689.html文中最后的圖表所示。
hugetlbfs_mount()做了如下工作:
1、申請並初始化一個super_block。
2、調用set_anon_super(),獲得一個未使用的此設備號dev,然后用主設備號0和次設備號dev設置新超級塊的s_dev字段。
3、將該super_block掛到全局super_blocks鏈表中,同時掛到hugetlbfs_fs_type->fs_supers鏈表中。
4、調用hugetlbfs_fill_super(),創建dentry、inode,並建立dentry、inode、super_block之間的映射。
do_add_mount():首先判斷文件系統是否重復裝載,相同文件系統不能掛載到相同掛載點。再調用attach_recursive_mnt(),將掛載點加入到全局目錄樹,即,將do_kern_mount()創建的mount數據結構,掛到全局mount_hashtable鏈表中,掛到命名空間的mnt_list鏈表中,同時掛到父掛載點的mnt_mounts鏈表中。
只是大體走了一下流程,很多具體細節並沒有涉及到,還有待補充。
參考:
http://blog.csdn.net/chenjin_zhong/article/details/8448862
http://blog.csdn.net/dndxhej/article/details/7434521