今天,我們要接續介紹 proc 檔案系統。
proc 檔案系統是一個特殊的虛擬檔案系統,通常會掛載在 /proc
路徑下。透過 proc 檔案系統,我們可以與 kernel 互動,了解各個 process 的資訊,甚至進行某些特殊操作。
當我們查看 /proc
目錄時,可以看到如下所示的內容:
> ls /proc
1 2 3 4 filesystems sys
...
> ls /proc/1
comm mem fd ns ...
...
在 /proc
資料夾中,每個 process 會有一個以 PID(Process ID)為名稱的資料夾。此外,還有一些特殊的檔案或資料夾對應到某些 kernel 功能。在 /proc/<pid>
資料夾中,包含了該 process 的各式資訊的檔案與資料夾,這些資訊涵蓋執行檔案名稱、工作目錄、記憶體分配狀況等。
在深入了解 VFS(Virtual File System)系統後,我們可以推測 proc 檔案系統是如何實現的。首先,proc 檔案系統定義了自己的檔案系統驅動。當我們訪問 /proc
資料夾時,proc 檔案系統驅動會存取 kernel 中 task_struct
的列表,藉此知道有哪些 process,並動態生成名稱為 PID 的 dentry。
在對 VFS 系統有了一個完整了解後,我們可以直接猜到 proc 檔案系統是怎麼實現的,首先 proc 檔案系統定義了自己的檔案系統驅動。當我們訪問 proc 資料夾時,proc 檔案系統驅動會存取 kernel 資料結構 task_struct的列表,了解到有哪些process,動態生成名稱為pid 的 dentry。
當我們執行 cat /proc/<pid>/comm
指令讀取 process 1 的執行檔案名稱時,read system call 會執行由 proc 檔案系統定義的特殊 read inode_operation。該 read 函數會讀取該 process 的 task_struct 並將其資訊返回。
// proc/base.c
static const struct pid_entry tgid_base_stuff[] = {
DIR("task", S_IRUGO|S_IXUGO, proc_task_inode_operations, proc_task_operations),
DIR("fd", S_IRUSR|S_IXUSR, proc_fd_inode_operations, proc_fd_operations),
DIR("map_files", S_IRUSR|S_IXUSR, proc_map_files_inode_operations, proc_map_files_operations),
DIR("fdinfo", S_IRUGO|S_IXUGO, proc_fdinfo_inode_operations, proc_fdinfo_operations),
DIR("ns", S_IRUSR|S_IXUGO, proc_ns_dir_inode_operations, proc_ns_dir_operations),
#ifdef CONFIG_NET
DIR("net", S_IRUGO|S_IXUGO, proc_net_inode_operations, proc_net_operations),
#endif
REG("comm", S_IRUGO|S_IWUSR, proc_pid_set_comm_operations),
...
從這個程式碼片段可以觀察出,在 /proc/<pid>
目錄下的檔案與資料夾,其實是在程式碼中靜態定義的。例如我們前面提到的 /proc/<pid>/comm
,根據程式碼定義,它是一個一般檔案(REG),使用的 file_operations
為 proc_pid_set_comm_operations
。
// proc/base.c
static const struct file_operations proc_pid_set_comm_operations = {
.open = comm_open,
.read = seq_read,
.write = comm_write,
.llseek = seq_lseek,
.release = single_release,
};
上面這段程式碼中,proc_pid_set_comm_operations
定義了該檔案的操作方式,包括 open
、read
、write
等。這裡的 read
函數使用的是 seq_read
,這是一種由 kernel 提供的特殊用法。當 application 進行 read 操作時,proc 檔案系統會先將要讀取的內容寫到一個虛擬的 seq_file
中,再由 seq_read
輸出。
當檔案被打開時,會呼叫 comm_open
函數,而在 comm_open
中又會呼叫 comm_show
:
static int comm_open(struct inode *inode, struct file *filp)
{
return single_open(filp, comm_show, inode);
}
static int comm_show(struct seq_file *m, void *v)
{
struct inode *inode = m->private;
struct task_struct *p;
p = get_proc_task(inode); // 取得目標 process 的 task_struct
if (!p)
return -ESRCH;
proc_task_name(m, p, false); // 從 task_struct 拿到 comm
seq_putc(m, '\n'); // 寫入 seq_file
put_task_struct(p);
return 0;
}
comm_show
函數會從指定的 process 的 task_struct
中取得執行檔案名稱(comm),然後將其寫入 seq_file
。當應用程式呼叫 read 時,最終會從 seq_file
中讀取並返回該資訊。
proc 檔案系統 是 Linux kernel 提供的非常重要的工具,在對 VFS 系統有深入的了解後,我們已經可以很容易了解 proc 檔案系統的原理了。