/proc 文件系統
在Linux中有額外的機制可以為內核和內核模塊將信息發送給進程 -- /proc 文件系統。最初設計的目的是允許更方便的對進程信息進行訪問(因此得名),現在它被每一個有有趣的東西報告的內核使用,例如 /proc/modules 有模塊的列表 /proc/meminfo 有內存使用的統計表。
使用proc 文件系統的方法和使用設備驅動程序非常相似--創建一個 /proc 文件需要的所有信息的結構,包括任何處理函數的指針(在我們的例子中只有一個,當某人試圖從 /proc 文件讀時調用的那一個)。然後,init_module 在內核中登記該結構而cleanup_module 注銷它。
我們使用 proc_register_dynamic(這是在 2.0 版中的情況,在 2.2 版中如果我們將節點設置為0系統將自動為我們做到) 的原因是我們不想預先決定我們的文件的節點數字,而是為防止沖突而由內核決定它。通常的文件系統存在於磁盤上而不是內存中(/proc 在內存中),在這中情況下,節點數是是指向文件的索引節點所在的磁盤位置的指針。節點包含文件的信息(例如文件的存取權限)和指向磁盤位置或文件數據可以被找到的幾個位置的指針。
因為當文件被打開或關閉的時候不能得到調用,所以在這個模塊中沒有地方放置 MOD_INC_USE_COUNT 和 MOD_DEC_USE_COUNT,並且,如果文件被打開隨後模塊被移除,我們沒有辦法避免後果。在下一 章我們會看到一個艱難的但更靈活的可以處理/proc文件的實現方式,它也可以讓我們防止那個問題。
范例 procfs.c
/* procfs.c - create a "file" in /proc * Copyright (C) 1998-1999 by Ori Pomerantz */ /* 必要的頭文件 */ /* 內核模塊標准頭文件 */ #include /* 內核工作 */ #include /* 明確指定是模塊 */ /* 處理CONFIG_MODVERSIONS */ #if CONFIG_MODVERSIONS==1 #define MODVERSIONS #include #endif /* 使用proc 文件系統所必要的 */ #include /* 在 2.2.3 版/usr/include/linux/version.h 中包含這個宏 * 但 2.0.35 版不包含 - 因此我在此加入這個以防需要。 */ #ifndef KERNEL_VERSION #define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c)) #endif /* 將數據放入 proc 文件 參數 ==== 1. 如果你決定使用緩沖區,數據應該被插入它。 2. 字符指針的指針。如果你不想使用由內核分配的緩沖區時這將有用。 3. 文件的當前位置。 4. 第一個參數中的緩沖區的大小。 5. Zero (為將來使用?)。 用法和返回值 ============ 如果你像我那樣使用自己的緩沖區,將它放在第二個參數的位置並返回在那個緩沖區中使用的字節數。 返回值為0 意味著這次沒有更多信息(文件尾)。負數返回值是錯誤條件。 更多信息 ======== 我不是通過讀文檔而發現用這個函數去做什麼的,而是通過讀使用它的代碼。我只想看看什麼使用了 proc_dir_entry的get_info成員(如果你有興趣的話,我使用了一個查找和搜索的組合),我發現它被 <內核源文件目錄>/fs/proc/array.c使用。 如果有什麼關於內核的事情不清楚,這是一個通常的辦法。在 Linux 中我們有自由的獲取源代碼的巨大優勢--使用它。 */ int procfile_read(char *buffer, char **buffer_location, off_t offset, int buffer_length, int zero) { int len; /* The number of bytes actually used */ /* 這是靜態的,因此當我們離開這個函數時它還會待在內存中 */ static char my_buffer[80]; static int count = 1; /* 我們將所有的信息放在一個裡面,所以如果用戶問是否有更多的信息,答案總是“沒有”。 * * 這是很重要的,因為來自庫的標准讀函數將持續發布讀系統調用直到內核答復沒有更多信息 * 或它的緩沖區被填滿。 */ if (offset > 0) return 0; /* 填充緩沖區並得到長度 */ len = sprintf(my_buffer, "For the %d%s time, go away!\n", count, (count % 100 > 10 && count % 100 < 14) ? "th" : (count % 10 == 1) ? "st" : (count % 10 == 2) ? "nd" : (count % 10 == 3) ? "rd" : "th" ); count++; /* 告訴調用函數緩沖區在哪兒*/ *buffer_location = my_buffer; /* 返回長度 */ return len; } struct proc_dir_entry Our_Proc_File = { 0, /* 節點數 - 忽略,它將被 proc_register[_dynamic] 填充*/ 4, /* 文件名長度 */ "test", /* 文件名*/ S_IFREG | S_IRUGO, /* 文件模式 - 這是一個可以被擁有者,用戶組和其他所有的用戶讀取的 * 普通文件 */ 1, /* 連接數 (文件被引用的目錄數) */ 0, 0, /* 文件的UID和GID - 我們將它賦予 root */ 80, /* 用ls報告的文件大小。 */ NULL, /* 使用節點的函數(連接,刪除,等等)--我們不支持 */ procfile_read, /* 對這個文件的讀函數,當某人試圖蔥它讀入什麼時被調用。 */ NULL /* 我們能夠在這兒有一個填充文件節點的函數,允許我們修改權限和擁有權,等等。 */ }; /* 初始化模塊--登記 proc 文件 */ int init_module() { /* proc_register[_dynamic] 成功則成功,否則失敗。 */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0) /* 在 2.2版中,如果在這個結構中它為0則 proc_register 自動的分配一個動態的節點數 * 因此不再需要proc_register_dynamic */ return proc_register(&proc_root, &Our_Proc_File); #else return proc_register_dynamic(&proc_root, &Our_Proc_File); #endif /* proc_root 是 proc文件系統的根目錄。這是我們想讓我們的文件所處的位置 */ } /* 清除 - 從 /proc中注銷我們的文件 */ void cleanup_module() { proc_unregister(&proc_root, Our_Proc_File.low_ino); }