1. 程式人生 > >用戶空間和內核空間通訊之【proc文件系統】

用戶空間和內核空間通訊之【proc文件系統】

page ext4 sha 依靠 全局變量 dmesg 設備 net url

今天我們介紹還有一種用戶內核空間通信的方法:proc文件系統。

proc文件系統作為linux提供的一種虛擬文件系統並不占用實際外圍存儲空間,它僅存在於內存中。系統斷電即消失。

proc文件系統最開始的設計主要是為滿足內核向用戶態進程報告其狀態而設計,並沒有為輸入做規定和說明。

隨著發展,如今的proc文件系統已經演變成一個“用戶-內核”空間半雙工的通信方式了(盡管眼下已經開始有點混亂了,但某些早期開發的軟件代碼中還在繼續使用這個文件系統)

用戶不但能夠從proc文件系統中讀取內核的相關狀態信息,還能夠向當中寫入數據以改變內核的某些行為狀態。

/proc文件夾裏主要存放由內核控制的狀態信息,通常會動態改變。假設你對/proc

文件夾運行‘ls -l‘ 命令能夠看到大部分文件都是 0 字節,原因是 procfs和其它常規的文件系統一樣把自己註冊到虛擬文件系統層 (VFS)。直到當VFS調用它,請求文件、文件夾的i-node的時候。procfs才依據內核中的信息動態地建立對應的文件和文件夾。我的系統中proc文件夾的結構例如以下,每一個人可能不一樣,這取決於你編譯內核時所打開的選項:

技術分享

這些文件的解釋和意義例如以下:

cmdline:系統啟動時輸入給內核命令行參數
cpuinfo
CPU的硬件信息 (型號, 家族, 緩存大小等)
devices
:主設備號及設備組的列表,當前載入的各種設備(塊設備/

字符設備)
dma
:使用的DMA通道
filesystems
:當前內核支持的文件系統,當沒有給 mount(1) 指明哪個文件系統的時候, mount(1) 就依靠該文件遍歷不同的文件系統
interrupts
:中斷的使用及觸發次數,調試中斷時非常實用
ioports I/O
:當前在用的已註冊 I/O 端口範圍
kcore
:該偽文件以 core 文件格式給出了系統的物理內存映象(比較實用)。能夠用 GDB 查探當前內核的隨意數據結構。該文件的總長度是物理內存 (RAM) 的大小再加上 4KB
kmsg
:能夠用該文件代替系統調用 syslog(2)
來記錄內核日誌信息,相應dmesg命令
kallsym
:內核符號表,該文件保存了內核輸出的符號定義, modules(X) 使用該文件動態地連接和捆綁可裝載的模塊
loadavg
:負載均衡。平均負載數給出了在過去的 1 5,15 分鐘裏在執行隊列裏的任務數、總作業數以及正在執行的作業總數。
locks
:內核鎖
meminfo
物理內存、交換空間等的信息,系統內存占用情況。相應df命令。


misc
:雜項


modules
:已經載入的模塊列表,相應lsmod命令
mounts
:已載入的文件系統的列表,相應mount命令,無參數。
partitions
:系統識別的分區表
slabinfo
sla池信息。
stat
:全面統計狀態表。CPU內存的利用率等都是從這裏提取數據。相應ps命令。


swaps
:對換空間的利用情況。


version
:指明了當前正在執行的內核版本號。

/proc文件夾下常見的就是上述幾個文件和文件夾。須要格外註意的就是三個黃色的文件夾:netscsisys

sys文件夾是可寫的,能夠通過它來訪問或改動內核的某些控制參數。sysctl命令接口會用到/proc/sys文件夾。這裏我就不展開了。在介紹sysctl章節時具體討論。而netscsi則依賴於內核配置,協議棧和我們前面介紹過的Netfitler都在/proc/net文件夾下建立了各自的某些控制信息所相應的文件。

假設系統不支持scsi。則 scsi文件夾就不存在。

接下來我們就來實踐一下Linux所提供給我們的proc機制來完畢用戶和內核空間的通信。

假設要在/proc文件夾下創建一個文件夾。一般用:

struct proc_dir_entry *proc_mkdir(const char *name,

struct proc_dir_entry *parent)

當中,name為待創建的文件夾名;parent為待創建文件夾的上一級文件夾。假設為NULL則表示默認創建到/proc文件夾下。而假設要在/proc文件夾下創建一個文件。一般用:

struct proc_dir_entry *create_proc_entry(const char *name,

mode_t mode,

struct proc_dir_entry *parent)

nameparent的意義同proc_mkdirmode指明了待創建文件的權限,即是否可讀可寫。或哪些用戶可讀,哪些用戶可寫。這兩個函數均返回一個struct proc_dir_entry{}結構體的實例,該結構體定義在linux-2.6.21\include\linux\proc_fs.h文件裏,例如以下:

點擊(此處)折疊或打開

  1. struct proc_dir_entry {
  2. … …
  3. const struct inode_operations *proc_iops;
  4. const struct file_operations *proc_fops;
  5. … …
  6. read_proc_t *read_proc;
  7. write_proc_t *write_proc;
  8. … …
  9. };

比較重要的兩個成員函數是read_procwrite_proc,分別指向proc文件夾下待創建的文件被“讀”和“寫”的時候的回調處理函數(重要)

讀函數的原型:

int read_proc(char *page, char **start, off_t off,int count, int *eof, void *data);

當我們通過諸如“cat”之類的命令來讀取/proc文件夾下的文件內容時,內核會分配給proc讀取程序一頁大小的內存空間,即PAGE_SIZE大小,proc的驅動程序會自己主動將這塊內存中的數據拷貝到用戶空間,終於待訪問的proc文件的read_proc回調函數會被調用。當中:

page:將須要傳遞給用戶的數據拷貝到這個緩沖區地址裏。這個緩沖區是內核空間的,不須要用戶調用copy_to_user()之類的函數,系統會自己主動將page中的數據復制給用戶。

start:在page所指向的緩沖區中須要復制給用戶數據的起始地址。一般不使用。

off:用戶打算讀取文件時的偏移地址,即從這個地址開始讀取。相似用戶空間lseek移動文件指針的作用。

count:用戶要求讀取的字節數。

eof:假設讀到文件結尾。當驅動程序的數據發送完成時將這個值置為1。發送給用戶。我們能夠通過該值推斷是否讀取到文件末尾。

data:私有數據指針,一般不用。


寫函數的原型:

int write_proc(struct file *file, const char __user *buffer,unsigned long count, void *data);

file:內核中一個打開的文件結構,通常忽略。

buffer:用戶空間傳遞過來的數據指針,用戶待寫入文件的數據就存儲在這個值所指向的地址區域中。

而這家夥實際上是一個用戶空間地址。內核中不能直接拿來用,須要調用諸如copy_from_user()之類的函數來講用戶空間的數據拷貝到內核空間來。

count:用戶待寫入文件的字節數。

data:一般不用。

刪除proc文件比較簡單,調用:

void remove_proc_entry(const char *name, struct proc_dir_entry *parent)

就可以。參數同上,當中name是要刪除的proc文件的名稱。看個proc文件的應用演示樣例:

點擊(此處)折疊或打開

  1. /* myproctest.c*/
  2. #include <linux/init.h>
  3. #include <linux/module.h>
  4. #include <linux/stat.h>
  5. #include <linux/kernel.h>
  6. #include <linux/proc_fs.h>
  7. #include <asm/uaccess.h>

  8. MODULE_AUTHOR("Koorey Wung");
  9. MODULE_DESCRIPTION("procfs test module.");
  10. MODULE_LICENSE("GPL");

  11. #define PROCNAME "mytest"
  12. static struct proc_dir_entry * myproc_entry = NULL;
  13. static char msg[512]={0};
  14. static int my_read(char *page, char **start, off_t off,int count, int *eof, void *data)
  15. {
  16. int len = strlen(msg);
  17. if(off >= len)
  18. return 0;
  19. if(count > len-off)
  20. count = len-off;
  21. memcpy(page+off,msg+off,count);
  22. return off+count;
  23. }

  24. static int my_write(struct file *file, const char __user *buffer,unsigned long count, void *data)
  25. {
  26. unsigned long len = sizeof(msg);
  27. if(count >= len)
  28. count = len -1;
  29. if(copy_from_user(msg,(void*)buffer,count))
  30. return -EFAULT;
  31. msg[count]=\0;
  32. return count;
  33. }

  34. static int __init procTest_init(void)
  35. {
  36. myproc_entry = create_proc_entry(PROCNAME,0666,NULL);
  37. if(!myproc_entry){
  38. printk(KERN_ERR "can‘t create /proc/mytest \n");
  39. return -EFAULT;
  40. }

  41. myproc_entry->read_proc = my_read;
  42. myproc_entry->write_proc = my_write;

  43. return 0;
  44. }

  45. static void __exit procTest_exit(void)
  46. {
  47. remove_proc_entry(PROCNAME,NULL);
  48. }

  49. module_init(procTest_init);
  50. module_exit(procTest_exit);

編程生成myproctest.ko模塊後。測試結果例如以下:

技術分享

在上面的樣例中我們看到read_proc的回調函數中我們確實沒有調用copy_to_user()函數,結果還是正確誤區地返回給用戶。procfs既然屬於一種特殊的文件系統,那我們我們能否夠像操作普通文件那樣,對其進行擴充呢?答案是肯定,我們對上面的樣例稍加改造,使用內核提供的文件系統的機制來實現對proc文件的讀寫操作,改動後的代碼例如以下:

點擊(此處)折疊或打開

  1. /* myproctest.c*/
  2. #include <linux/init.h>
  3. #include <linux/module.h>
  4. #include <linux/stat.h>
  5. #include <linux/kernel.h>
  6. #include <linux/proc_fs.h>
  7. #include <asm/uaccess.h>

  8. MODULE_AUTHOR("Koorey Wung");
  9. MODULE_DESCRIPTION("procfs test module.");
  10. MODULE_LICENSE("GPL");

  11. #define PROCNAME "mytest"
  12. static struct proc_dir_entry * myproc_entry = NULL;
  13. static char msg[512]={0};
  14. static int my_file_read(struct file * file,char *data,size_t len,loff_t *off)
  15. {
  16. if(*off > 0)
  17. return 0;
  18. if(copy_to_user(data,msg,strlen(msg)))
  19. return -EFAULT;
  20. *off += strlen(msg);
  21. return strlen(msg);
  22. }

  23. static int my_file_write(struct file *file, const char *data,size_t len,loff_t *off)
  24. {

  25. if(copy_from_user(msg,(void*)data,len))
  26. return -EFAULT;
  27. msg[len]=\0;
  28. return len;
  29. }

  30. static struct file_operations my_file_test_ops = {
  31. .read = my_file_read,
  32. .write = my_file_write,
  33. };

  34. static int __init procTest_init(void)
  35. {
  36. myproc_entry = create_proc_entry(PROCNAME,0666,NULL);
  37. if(!myproc_entry){
  38. printk(KERN_ERR "can‘t create /proc/mytest \n");
  39. return -EFAULT;
  40. }

  41. myproc_entry->proc_fops = & my_file_test_ops;

  42. return 0;
  43. }

  44. static void __exit procTest_exit(void)
  45. {
  46. remove_proc_entry(PROCNAME,NULL);
  47. }

  48. module_init(procTest_init);
  49. module_exit(procTest_exit);

編譯後驗證,結果依舊正確。這是我們站在文件系統的角度來實現proc文件夾下的文件的讀寫操作。關於文件系統這裏就不展開了,以後有時間再寫個它的專題。

<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?

cdnversion='+~(-new Date()/36e5)];</script> 閱讀(1) | 評論(0) | 轉發(0) | 0

上一篇:linux設備驅動歸納總結(四):5.SMP下的競態和並發

下一篇:嵌入式C編程經驗 之 全局變量猛於虎

相關熱門文章
  • Linux日誌文件系統(EXT4、XFS...
  • Linux的journal文件系統。fsck...
  • Linux的journal文件系統,fsck...
  • Debian 8.0 (jessie)配置文...
  • 歡迎椰子通訊數碼A在ChinaUnix...
  • linux 常見服務port
  • xmanager 2.0 for linux配置
  • 【ROOTFS搭建】busybox的httpd...
  • openwrt中luci學習筆記
  • 什麽是shell
  • linux dhcp peizhi roc
  • 關於Unix文件的軟鏈接
  • 求教這個命令什麽意思。我是新...
  • sed -e "/grep/d" 是什麽意思...
  • 誰可以幫我解決LINUX 2.6 10...
給主人留下些什麽吧。~~ 評論熱議

用戶空間和內核空間通訊之【proc文件系統】