核心proc檔案系統與seq介面(3)---核心proc檔案底層結構淺析

國服第一趙雲發表於2020-12-03

 前面講了proc檔案系統的程式設計,其實在使用API的時候注意一下proc檔案系統的實現也是很有益處的。在學習使用proc的時候我順便看了一下proc的底層實現,發現原理非常簡單,畢竟是一個基於記憶體的檔案系統。

    首先眾所周知,所有的檔案系統都位於VFS的層,proc也不例外。在核心啟動的時,start_kernel在完成了VFS的初始化不久就呼叫了:

#ifdef CONFIG_PROC_FS
    proc_root_init();
#endif
用於初始化proc檔案系統:
 
fs/proc/root.c
void __init proc_root_init(void)
{
    struct vfsmount *mnt;
    int err;

    proc_init_inodecache();
    err = register_filesystem(&proc_fs_type);		//向VFS註冊procfs
    if (err)
        return;
    mnt = kern_mount_data(&proc_fs_type, &init_pid_ns);
    if (IS_ERR(mnt)) {
        unregister_filesystem(&proc_fs_type);
        return;
    }

    init_pid_ns.proc_mnt = mnt;
    proc_symlink("mounts", NULL, "self/mounts");	//建立“mounts”符號連線檔案

    proc_net_init();					//建立“net”符號連線及內部目錄樹結構

#ifdef CONFIG_SYSVIPC
    proc_mkdir("sysvipc", NULL);			//建立“sysvipc”目錄
#endif
    proc_mkdir("fs", NULL);				//建立“fs”目錄
    proc_mkdir("driver", NULL);				//建立“driver”目錄
    proc_mkdir("fs/nfsd", NULL); /* somewhere for the nfsd filesystem to be mounted */
#if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE)
    /* just give it a mountpoint */
    proc_mkdir("openprom", NULL);
#endif
    proc_tty_init();					//建立“tty”目錄及內部結構
#ifdef CONFIG_PROC_DEVICETREE
    proc_device_tree_init();
#endif
    proc_mkdir("bus", NULL);				//建立“bus”目錄
    proc_sys_init();					//建立“sys”目錄並初始化
}
 在這之後,許多核心元件和模組就可以向proc檔案系統及其子目錄新增目錄和檔案了。其中最重要的資料結構體就是:struct proc_dir_entry,她在《核心proc檔案系統與seq介面(2)---核心proc檔案系統程式設計介面》介紹過。她相當於proc檔案系統中的資料節點,procfs中許多檔案、符號連線和目錄檔案都是由她表示的。但是請注意,並不是左右的proc檔案都對應這樣的資料結構,對於/proc/sys、/proc/${PID}/等特定目錄下的檔案時需要時動態生成的,有其自己的內部實現。而這個proc_dir_entry只對普通的proc檔案實現,及我們自己建立的proc檔案。
 
其根目錄的表示:
fs/proc/root.c
/*
 * This is the root "inode" in the /proc tree..
 */
struct proc_dir_entry proc_root = {
    .low_ino    = PROC_ROOT_INO,
    .namelen    = 5,
    .name        = "/proc",
    .mode        = S_IFDIR | S_IRUGO | S_IXUGO,
    .nlink        = 2,
    .count        = ATOMIC_INIT(1),
    .proc_iops    = &proc_root_inode_operations,
    .proc_fops    = &proc_root_operations,
    .parent        = &proc_root,
};
  除了這個節點的結構體在程式碼中靜態定義的之外,其他所有的節點都是系統和模組初始化產生的。也除了這個節點的mane字串是在核心映像的只讀資料段中,其他的節點的name字串都是在建立的時候緊跟在其struct proc_dir_entry之後的,如下圖:
 
     
    而看了原始碼(fs/proc/generic.c)你就會很快的瞭解這個檔案系統的資料連線方式:用*next指標通過單向連結串列連線同目錄下檔案;用*parent指標指向父目錄的proc_dir_entry結構體;用*subdir指標指向本目錄中的檔案(如果這個檔案目錄的話),總體底層結構示意圖如下所示:
 
 
    上圖只是一個結構示意圖,一些指標的指向不確定的,比如proc_root的*subdir肯定不是指向sys,因為初始化之後還有很多目錄和檔案要產生。但是/root目錄下單向連結串列的最後一個應該是mounts,因為他是最早註冊的(起碼在3.0上是這樣)。上圖同時示意了符號連線註冊後的一般情況。
 
    對於一個目錄註冊後的一般如下,主要是注意*proc_iops inode操作函式和*proc_fops 檔案操作函式的註冊:
 
    
    在《核心proc檔案系統與seq介面(2)---核心proc檔案系統程式設計介面》中曾經介紹過,對於建立 /proc 入口,有兩種選擇:
(1)使用proc的預設操作函式集proc_file_operations,但必須實現回撥函式*read_proc和*write_proc,示意圖如下:
 
(2)使用自己實現的file_operations,以下用/proc/config.gz檔案的實現做示意:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    以上的解析只是對procfs的淺析,有興趣的朋友可以自己看看fs/proc/generic.c,非常簡單的檔案系統實現,是一個很好的學習樣本。RTFSC – Read The Fucking Source Code。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

相關文章