利用proc 实现内核和用户态交换数据
最近寫程序需要內核得到用戶態的參數,比較苦逼幸福的是雖然ioctrl 用不了,可以用proc實現,proc文件系統提供了一種內核和用戶態交互的方法。
proc文件系統的詳細接口看<linux/proc_fs.h>
主要需要關注的是這幾個函數:
?
函數 proc_mkdir用于在proc文件系統下創建文件夾, proc_mkdir的第一個參數是要創建的目錄的名字,第二個參數是指向父目錄結構的指針。需要注意一下幾點:
1. 創建過程沒有對于名字的檢查,完全可以調用proc_mkdir 創建出一堆同樣名字的文件(檢查一下會死啊!)
2. 其實名字可以包含路徑,例如“dev/new_proc”, 如果存在dev目錄,函數會在dev目錄下創建new_proc目錄。
3. 如果第二個參數是NULL,會在根目錄也就是/proc/目錄下創建目錄
函數的返回值就是 新創建目錄對應的proc_dir_entry, 保存這個就可以用來在此目錄下創建文件啦,其實,即使不保存,利用上面介紹的第二點性質也可以在目錄下創建文件,而且刪除目錄只用知道路徑就可以了~
?
函數create_proc_entry用來創建文件,mode 參數如果為NULL的話默認的文件訪問權限是 755,其他的參數與proc_mkdir 類似
?
函數remove_proc_entry用來刪除創建的目錄或者文件,有意思的是,這個函數只需要知道名字和父目錄就可以刪除了。
?
說了這么多,怎么交換數據尼?
需要了解下proc_dir_entry 的結構了
struct proc_dir_entry {unsigned int low_ino;unsigned short namelen;const char *name;mode_t mode;nlink_t nlink;uid_t uid;gid_t gid;loff_t size;struct inode_operations * proc_iops;const struct file_operations * proc_fops;get_info_t *get_info;struct module *owner;struct proc_dir_entry *next, *parent, *subdir;void *data;read_proc_t *read_proc;write_proc_t *write_proc;atomic_t count; /* use count */int deleted; /* delete flag */void *set; };其他的參數可以忽略,這里需要注意的是兩個成員變量:
read_proc 和 write_proc
這兩個變量的類型如下:
typedef int (read_proc_t)(char *page, char **start, off_t off, int count, int *eof, void *data); typedef int (write_proc_t)(struct file *file, const char __user *buffer, unsigned long count, void *data);?
對proc文件的讀寫操作,最終將轉化為對read_proc和write_proc的調用。
看到這里明白了吧,只要在內核里注冊proc文件,實現read_proc 和write_proc函數,然后設置proc_dir_entry對應成員變量的值,在用戶態進行讀寫就可以和內核交互了
?
解釋這兩個函數的參數:
對于read_proc_t
1. 第一個參數:為啥叫page?答案就是如果對proc文件調用讀操作,內核會分配一個頁大小的緩沖區。如何輸出大于一個頁的數據呢,這得依賴于第二個和第三個參數了。
為了理解第二三個參數,回憶下與文件操作相關的系統調用:
int open(const char *pathname, int flags); off_t lseek(int fildes, off_t offset, int whence); ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count);對于proc文件,一次read操作最多只能讀取一個page的數據,如果需要讀取大于一個頁的數據需要保存read的返回值,然后使用lseek設定offset,然后再次調用read。回到參數的說明:2. start和off參數:off對應于lseek里面的offset(lseek whence為SEEK_END,offset為負 的情況下,傳進來的off為零,具體原因待考古)。
如果不設置*start的值,off的取值只能在[0, count - 1]之間,且能夠讀取的數據大小為:count - off。可以理解系統拷貝了 [page + off, page+count - 1]之間的數據到用戶的buffer里。如果off的取值超出范圍,read將讀不到數據。
如果設置了*start的值,系統認為*start指向的地址就是off指定的地址,off的值會被忽略,系統會拷貝[start, start+count-1]之間的數據到用戶空間。當然我們在實現start地址的定位時,可能會需要off的值。
3. count 參數與read中的count一致
4. eof參數,設置了這個參數表明不想再提供數據了,神馬意思呢?
如果不設置這個參數,對于上面說的start為空的情況,read_proc返回后,系統如果發現 (count - off) < count 會接著下發寫請求,讀取off大小的數據。
例如:有這樣的讀取操作:
lseek(fd, 2, SEEK_SET); read(fd, buff_r, 30)第一次調用read_proc, off=2, count=30, 由于我們沒有設置start的值,將讀取28字節的數據,由于沒有設置*eof, 系統會再次下發read_proc
第二次調用read_proc, off=30, count=2, 在參數start的說明里提到,對于這個調用,系統默認讀不到數據(怨念啊,為啥要下發)
于是read返回28
如果設置了這個參數就不會有第二次的下發了。
5. data參數,這個是給驅動程序預留的參數。
對于write_proc函數,參數是很簡單的,需要說明的只有一點,就是write_proc的第二個參數buffer是用戶態的地址,需要用copy_from_user從用戶態把數據拷到內核態的緩沖區里。
作者:ziziwu 發表于2011-10-20 14:40:08 原文鏈接 閱讀:7 評論:0 查看評論轉載于:https://www.cnblogs.com/ziziwu/archive/2011/10/20/2218975.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的利用proc 实现内核和用户态交换数据的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 平台服务器测试3—接口测试工具实现
- 下一篇: hdu 1022 Train Probl