用戶空間和內核空間通訊之【proc文件系統】
今天我們介紹還有一種用戶內核空間通信的方法:proc文件系統。
proc文件系統作為linux提供的一種虛擬文件系統並不占用實際外圍存儲空間,它僅存在於內存中。系統斷電即消失。
proc文件系統最開始的設計主要是為滿足內核向用戶態進程報告其狀態而設計,並沒有為輸入做規定和說明。
隨著發展,如今的proc文件系統已經演變成一個“用戶-內核”空間半雙工的通信方式了(盡管眼下已經開始有點混亂了,但某些早期開發的軟件代碼中還在繼續使用這個文件系統)。
用戶不但能夠從proc文件系統中讀取內核的相關狀態信息,還能夠向當中寫入數據以改變內核的某些行為狀態。
/proc文件夾裏主要存放由內核控制的狀態信息,通常會動態改變。假設你對/proc
這些文件的解釋和意義例如以下:
cmdline:系統啟動時輸入給內核命令行參數
|
/proc文件夾下常見的就是上述幾個文件和文件夾。須要格外註意的就是三個黃色的文件夾:net、scsi和sys。
sys文件夾是可寫的,能夠通過它來訪問或改動內核的某些控制參數。sysctl命令接口會用到/proc/sys文件夾。這裏我就不展開了。在介紹sysctl章節時具體討論。而net和scsi則依賴於內核配置,協議棧和我們前面介紹過的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)
name和parent的意義同proc_mkdir,mode指明了待創建文件的權限,即是否可讀可寫。或哪些用戶可讀,哪些用戶可寫。這兩個函數均返回一個struct proc_dir_entry{}結構體的實例,該結構體定義在linux-2.6.21\include\linux\proc_fs.h文件裏,例如以下:
點擊(此處)折疊或打開
- struct proc_dir_entry {
- … …
- const struct inode_operations *proc_iops;
- const struct file_operations *proc_fops;
- … …
- read_proc_t *read_proc;
- write_proc_t *write_proc;
- … …
- };
比較重要的兩個成員函數是read_proc和write_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文件的應用演示樣例:
點擊(此處)折疊或打開
- /* myproctest.c*/
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/stat.h>
- #include <linux/kernel.h>
- #include <linux/proc_fs.h>
- #include <asm/uaccess.h>
- MODULE_AUTHOR("Koorey Wung");
- MODULE_DESCRIPTION("procfs test module.");
- MODULE_LICENSE("GPL");
- #define PROCNAME "mytest"
- static struct proc_dir_entry * myproc_entry = NULL;
- static char msg[512]={0};
- static int my_read(char *page, char **start, off_t off,int count, int *eof, void *data)
- {
- int len = strlen(msg);
- if(off >= len)
- return 0;
- if(count > len-off)
- count = len-off;
- memcpy(page+off,msg+off,count);
- return off+count;
- }
- static int my_write(struct file *file, const char __user *buffer,unsigned long count, void *data)
- {
- unsigned long len = sizeof(msg);
- if(count >= len)
- count = len -1;
- if(copy_from_user(msg,(void*)buffer,count))
- return -EFAULT;
- msg[count]=‘\0‘;
- return count;
- }
- static int __init procTest_init(void)
- {
- myproc_entry = create_proc_entry(PROCNAME,0666,NULL);
- if(!myproc_entry){
- printk(KERN_ERR "can‘t create /proc/mytest \n");
- return -EFAULT;
- }
- myproc_entry->read_proc = my_read;
- myproc_entry->write_proc = my_write;
- return 0;
- }
- static void __exit procTest_exit(void)
- {
- remove_proc_entry(PROCNAME,NULL);
- }
- module_init(procTest_init);
- module_exit(procTest_exit);
編程生成myproctest.ko模塊後。測試結果例如以下:
在上面的樣例中我們看到read_proc的回調函數中我們確實沒有調用copy_to_user()函數,結果還是正確誤區地返回給用戶。procfs既然屬於一種特殊的文件系統,那我們我們能否夠像操作普通文件那樣,對其進行擴充呢?答案是肯定,我們對上面的樣例稍加改造,使用內核提供的文件系統的機制來實現對proc文件的讀寫操作,改動後的代碼例如以下:
點擊(此處)折疊或打開
- /* myproctest.c*/
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/stat.h>
- #include <linux/kernel.h>
- #include <linux/proc_fs.h>
- #include <asm/uaccess.h>
- MODULE_AUTHOR("Koorey Wung");
- MODULE_DESCRIPTION("procfs test module.");
- MODULE_LICENSE("GPL");
- #define PROCNAME "mytest"
- static struct proc_dir_entry * myproc_entry = NULL;
- static char msg[512]={0};
- static int my_file_read(struct file * file,char *data,size_t len,loff_t *off)
- {
- if(*off > 0)
- return 0;
- if(copy_to_user(data,msg,strlen(msg)))
- return -EFAULT;
- *off += strlen(msg);
- return strlen(msg);
- }
- static int my_file_write(struct file *file, const char *data,size_t len,loff_t *off)
- {
- if(copy_from_user(msg,(void*)data,len))
- return -EFAULT;
- msg[len]=‘\0‘;
- return len;
- }
- static struct file_operations my_file_test_ops = {
- .read = my_file_read,
- .write = my_file_write,
- };
- static int __init procTest_init(void)
- {
- myproc_entry = create_proc_entry(PROCNAME,0666,NULL);
- if(!myproc_entry){
- printk(KERN_ERR "can‘t create /proc/mytest \n");
- return -EFAULT;
- }
- myproc_entry->proc_fops = & my_file_test_ops;
- return 0;
- }
- static void __exit procTest_exit(void)
- {
- remove_proc_entry(PROCNAME,NULL);
- }
- module_init(procTest_init);
- 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文件系統】