使用/proc文件系統和內核打交道(1)-初步建立關係

/proc文件系統是Linux內核的一個虛擬文件系統,說它虛擬,是因爲和磁盤分區上的文件系統不同,它只有在內核啓動以後,工作起來的時候纔會被動態創建。

/proc下面的內容會隨着內核的配置和工作的狀態在變化,比如/proc/meminfo這個文件,裏面的內容是內存信息,不同的機器自然不同;還有就是/proc目錄下面用數字命名的子目錄,每個子目錄的名字也就是數字,對應當前系統正在運行的進程,而子目錄裏面的內容就是對應進程的信息。

/proc裏面還有其他許多信息,這裏就不一一描述了,有興趣可以谷歌一下。

Linux內核經過多年發展,/proc目錄下的林林總總,已經是非常多了,雖然已經開發出了新的/sys文件系統替代/proc,但是,許多傳統的程序仍然使用/proc文件系統作爲內核與用戶程序的接口。

如何讓內核更高效,那是內核開發人員的事情。作爲一個使用內核的開發人員,掌握/proc文件系統的使用方法也是很有用的,和/sys文件系統組織嚴密的結構相比,用/proc有時候還是很方便的。

不多說廢話了,先入個門,介紹/proc的最基本操作。

這一集我們就介紹操作/proc文件系統的三個最基本函數:

代碼片段1 定義在內核代碼的<linux/proc_fs.h>

  1. struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,  
  2.       struct proc_dir_entry *parent);  
  3.  
  4. struct proc_dir_entry *proc_mkdir(const char *name,
  5. struct proc_dir_entry *parent);  
  6.  
  7. void remove_proc_entry(const char *name, struct proc_dir_entry *parent);  
  8.  

先插一句,本例所有代碼使用內核版本2.6.38.2,系統是ubuntu 11.04 amd64,要注意哦。

這三個函數,從函數名稱就可以理解出來意思,第一個create_proc_entry()函數用做創建一個節點,也就是創建一個文件;

第二個proc_mkdir()函數建立一個目錄;

第三個remove_proc_entry()函數是刪除一個節點,實際上,不僅能刪除節點,也可以刪除proc_mkdir()函數創建的目錄。

需要注意的是,proc相關的函數,操作的目標都是在/proc目錄下。好,繼續看函數參數:

create_proc_entry()函數:
name 是節點的名稱,一個字符串;
mode 訪問權限,可以使用八進制表示,chmod命令使用的權限一樣;
parent 父目錄,注意是一個struct proc_dir_entry結構,如果寫NULL表示/proc目錄。

proc_mkdir()函數:
name 子目錄名稱,一個字符串;
parent 父目錄,和上面解釋一樣。

remove_proc_entry()函數:
參數同上面一樣。

函數介紹完畢。有這三個函數就可以開工了。廢話少說,貼代碼:

 代碼片段2 自己寫的小模塊,演示如何建立刪除/proc節點

  1. #include <linux/module.h>  
  2. #include <linux/init.h>  
  3. #include <linux/version.h>  
  4. #include <linux/proc_fs.h>  
  5.  
  6. MODULE_LICENSE("Dual BSD/GPL");  
  7. MODULE_AUTHOR("<[email protected]>");  
  8.  
  9. #define USER_ROOT_DIR "pt"  
  10. #define USER_ENTRY1   "pt_entry1"  
  11.  
  12. // user defined directory  
  13. static struct proc_dir_entry *pt_root;  
  14. static struct proc_dir_entry *pt_entry1;  
  15.  
  16. static int proc_test_init(void)  
  17. {  
  18.   // Create user root dir under /proc  
  19.   pt_root = proc_mkdir(USER_ROOT_DIR, NULL);  
  20.   if (NULL==pt_root)  
  21.   {  
  22.     printk(KERN_ALERT "Create dir /proc/%s error!\n",  
  23.         USER_ROOT_DIR);  
  24.     return -1;  
  25.   }  
  26.   printk(KERN_INFO "Create dir /proc/%s\n", USER_ROOT_DIR);  
  27.  
  28.   // Create a test entry under USER_ROOT_DIR  
  29.   pt_entry1 = create_proc_entry(USER_ENTRY1,  
  30.       0666, pt_root);  
  31.   if (NULL == pt_entry1)  
  32.   {  
  33.     printk(KERN_ALERT "Create entry %s under /proc/%s error!\n",  
  34.         USER_ENTRY1, USER_ROOT_DIR);  
  35.     goto err_out;  
  36.   }  
  37.   printk(KERN_INFO "Create /proc/%s/%s\n",  
  38.       USER_ROOT_DIR, USER_ENTRY1);  
  39.  
  40.   pt_entry1->read_proc = NULL;  
  41.   pt_entry1->write_proc = NULL;  
  42.  
  43.   return 0;  
  44.  
  45. err_out:  
  46.   remove_proc_entry(USER_ROOT_DIR, pt_root);  
  47.   return -1;  
  48. }  
  49.  
  50. static void proc_test_exit(void)  
  51. {  
  52.   // Remove all entries  
  53.   remove_proc_entry(USER_ENTRY1, pt_root);  
  54.   remove_proc_entry(USER_ROOT_DIR, NULL);  
  55.   printk(KERN_INFO "All Proc Entry Removed!\n");  
  56. }  
  57.  
  58. module_init(proc_test_init);  
  59. module_exit(proc_test_exit);  

一個標準的2.6內核模塊寫法。proc_test_init()函數是入口,proc_test_exit()函數是出口。

最開始引入了4個頭文件,其中3個是內核模塊標配的頭文件,第4個是主角,proc_fs.h,前面說了,操作/proc的函數就定義在裏面。

第9行,USER_ROOT_DIR定義了我們要在/proc目錄下建立的子目錄名稱,也是用戶的根目錄;第10行,USER_ENTRY1定義了欲在USER_ROOT_DIR下面建立的節點名稱。

第13、14行分別定義了USER_ROOT_DIR和USER_ENTRY1對應的目錄結構pt_root和pt_entry1。

操作的數據介紹好了,說下流程吧。

proc_test_init()函數:

此函數是加載模塊的時候執行的入口函數,會被內核自動調用。第19行,函數一開始就調用proc_mkdir()函數,在/proc目錄下建立一個子目錄,注意proc_mkdir()函數的第二個參數,NULL表示以/proc作爲根目錄。

子目錄USER_ROOT_DIR建立成功後,緊接着調用create_proc_entry()函數建立一個文件節點,注意函數的第三個參數parent,不是NULL,而是pt_root,表示以USER_ROOT_DIR作爲根目錄。

文件建立成功後,需要設置pt_entry1的讀寫回調函數爲NULL,這樣做的目的是該文件節點讀寫操作都不處理。

proc_test_exit()函數:

用戶調用rmmod卸載內核模塊的時候被內核調用。此函數刪除已經建立的節點,要注意的是remove_proc_entry()函數的parent實參,這裏容易出錯,導致內核報錯。

最後貼一下Makefile:

  1. obj-m += proc_test1.o  
  2.  
  3. all:  
  4.     make -C /usr/src/linux-headers-2.6.38-8-generic M=`pwd` modules  
  5.  
  6. clean:  
  7.     make -C /usr/src/linux-headers-2.6.38-8-generic M=`pwd` clean 

看看執行的效果:

下面是加載模塊後列出文件節點

  1. root@iscsia:~/proc_test# ll /proc/pt/  
  2. total 0  
  3. -rw-rw-rw- 1 root root 0 2011-07-01 15:04 pt_entry1 

下面是讀文件節點/proc/pt/pt_entry1

  1. root@iscsia:~/proc_test# cat /proc/pt/pt_entry1   
  2. root@iscsia:~/proc_test#  

什麼結果都沒有,正常,因爲回調函數都給設置爲NULL了。

下面是寫文件節點/proc/pt/pt_entry1

  1. root@iscsia:~/proc_test# echo "hello,entry" > /proc/pt/pt_entry1  
  2. -bash: echo: write error: Input/output error 

看來寫操作不是很理想,報錯了,pt_entry1不是很合作啊,也是,前面都給人家設置成NULL了,也不能全怪人家了。哈哈。下次繼續介紹如何進行自己的讀寫操作。

 原始出處

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章