創(chuàng)建屬于自己的系統(tǒng)信息,往proc中留下一個(gè)腳印
點(diǎn)擊上方「嵌入式大雜燴」,「星標(biāo)公眾號(hào)」第一時(shí)間查看嵌入式筆記!
上一篇:《文件系統(tǒng)有很多,但這幾個(gè)最為重要》介紹了procfs(進(jìn)程文件系統(tǒng)的縮寫),包含一個(gè)偽文件系統(tǒng)(啟動(dòng)時(shí)動(dòng)態(tài)生成的文件系統(tǒng)),用于通過內(nèi)核訪問進(jìn)程信息。這個(gè)文件系統(tǒng)通常被掛載到 /proc 目錄, /proc中不僅僅放了進(jìn)程相關(guān)信息,也存放著很多系統(tǒng)相關(guān)的信息。
這些信息都是內(nèi)核開放給用戶的,/proc 就是用戶與內(nèi)核直接交互的一個(gè)入口。從內(nèi)核的角度看,內(nèi)核是通過怎么樣的方式把這些信息暴露給用戶呢?這篇筆記我們來學(xué)習(xí)一下:
內(nèi)核創(chuàng)建proc節(jié)點(diǎn)的例子
我們先來看一個(gè)例子(Linux-4.9.88\fs\proc\cpuinfo.c):
這就是創(chuàng)建/proc下cpuinfo這個(gè)節(jié)點(diǎn)的相關(guān)代碼,有了cpuinfo節(jié)點(diǎn),我們就可以通過訪問這個(gè)節(jié)點(diǎn)來得到cpu的一些信息:
從以上代碼中,我們可以看到,其用proc_create這個(gè)函數(shù)來創(chuàng)造相關(guān)節(jié)點(diǎn)的,這個(gè)函數(shù)是一個(gè)內(nèi)聯(lián)函數(shù),存放在Linux-4.9.88\include\linux\proc_fs.h下:
static?inline?struct?proc_dir_entry?*proc_create(
?const?char?*name,?umode_t?mode,?struct?proc_dir_entry?*parent,
?const?struct?file_operations?*proc_fops)
{
?return?proc_create_data(name,?mode,?parent,?proc_fops,?NULL);
}
知識(shí)點(diǎn):什么是內(nèi)聯(lián)函數(shù)?
內(nèi)聯(lián)函數(shù)簡(jiǎn)單來說就是編譯器將指定的函數(shù)體插入并取代每一處調(diào)用該函數(shù)的地方上下文,從而節(jié)省了每次調(diào)用函數(shù)帶來的額外時(shí)間開支。
一般用于能夠快速執(zhí)行的函數(shù),因?yàn)樵谶@種情況下函數(shù)調(diào)用的時(shí)間消耗顯得更為突出。這種方法對(duì)于很小的函數(shù)也有空間上的益處,并且它也使得一些其他的優(yōu)化成為可能。
這么一看,似乎與宏有點(diǎn)相似?與宏有何不同?
宏調(diào)用并不執(zhí)行類型檢查,甚至連正常參數(shù)也不檢查,但是函數(shù)調(diào)用卻要檢查。 C語(yǔ)言的宏使用的是文本替換,可能導(dǎo)致無法預(yù)料的后果,因?yàn)樾枰匦掠?jì)算參數(shù)和操作順序。 在宏中的編譯錯(cuò)誤很難發(fā)現(xiàn),因?yàn)樗鼈円玫氖菙U(kuò)展的代碼,而不是程序員鍵入的。 許多結(jié)構(gòu)體使用宏或者使用不同的語(yǔ)法來表達(dá)很難理解。內(nèi)聯(lián)函數(shù)使用與普通函數(shù)相同的語(yǔ)言,可以隨意的內(nèi)聯(lián)和不內(nèi)聯(lián)。 內(nèi)聯(lián)代碼的調(diào)試信息通常比擴(kuò)展的宏代碼更有用。
以上介紹摘選自百度百科,關(guān)于內(nèi)聯(lián)更詳細(xì)的介紹可自行查閱。
接著上面,proc_create函數(shù)有四個(gè)參數(shù),分別為:
name:要?jiǎng)?chuàng)建的文件名。
mode:文件的訪問權(quán)限。
parent:父文件夾的proc_dir_entry指針。
proc_fops:改文件的操作函數(shù)。
看到這個(gè)函數(shù),有沒有感到很熟悉?我們?cè)趯W(xué)習(xí)驅(qū)動(dòng)基礎(chǔ)的時(shí)候,有用到了device_create函數(shù)來創(chuàng)建節(jié)點(diǎn):
device_create創(chuàng)建的設(shè)備節(jié)點(diǎn)存放于/dev目錄下,而proc_create函數(shù)創(chuàng)建的與系統(tǒng)信息相關(guān)的節(jié)點(diǎn)存放于/proc目錄下。既然它們這么相似,下面我們就模仿編寫驅(qū)動(dòng)的方式來編寫我們關(guān)于proc的測(cè)試代碼。
proc實(shí)踐
我們模仿字符設(shè)備驅(qū)動(dòng)的編寫方式,來編寫基于proc的“驅(qū)動(dòng)”。首先需要?jiǎng)?chuàng)建一個(gè)文件操作結(jié)構(gòu)體hello_proc_operations,創(chuàng)建一些hello_proc_open、hello_proc_close、hello_proc_read、hello_proc_write填到這個(gè)操作表里:
左右滑動(dòng)查看全部代碼>>>
#include?
#include?
#include?
#include?
#include?
static?char?kernel_buf[1024];
#define?MIN(a,?b)?(a?
static?int?hello_proc_open(struct?inode?*node,?struct?file?*file)
{
?printk("%s?%s?line?%d\n",?__FILE__,?__FUNCTION__,?__LINE__);
?return?0;
}
static?ssize_t?hello_proc_read(struct?file?*file,?char?__user?*buf,?size_t?size,?loff_t?*offset)
{
?int?err;
?printk("%s?%s?line?%d\n",?__FILE__,?__FUNCTION__,?__LINE__);
?err?=?copy_to_user(buf,?kernel_buf,?MIN(1024,?size));
?return?MIN(1024,?size);
}
static?ssize_t?hello_proc_write(struct?file?*file,?const?char?__user?*buf,?size_t?size,?loff_t?*offset)
{
?int?err;
?printk("%s?%s?line?%d\n",?__FILE__,?__FUNCTION__,?__LINE__);
?err?=?copy_from_user(kernel_buf,?buf,?MIN(1024,?size));
?return?MIN(1024,?size);
}
static?int?hello_proc_close(struct?inode?*node,?struct?file?*file)
{
?printk("%s?%s?line?%d\n",?__FILE__,?__FUNCTION__,?__LINE__);
?return?0;
}
static?const?struct?file_operations?hello_proc_operations?=?
{
?.owner??=?THIS_MODULE,
?.open??=?hello_proc_open,
?.read????=?hello_proc_read,
?.write???=?hello_proc_write,
?.release?=?hello_proc_close,
};
static?int?__init?hello_proc_init(void)
{
?proc_create("hello_proc",?0,?NULL,?&hello_proc_operations);
?return?0;
}
static?void?__exit?hello_proc_exit(void)
{
?remove_proc_entry("hello_proc",?NULL);
}
module_init(hello_proc_init);
module_exit(hello_proc_exit);
MODULE_DESCRIPTION("proc?test");
MODULE_LICENSE("GPL");
最上邊的那個(gè)例子中用了一個(gè)fs_initcall宏,這與module_init的底層是差不多相同的(有宏參數(shù)不一樣):
① module_init -> _initcall ?-> ?device_initcall -> ?_define_initcall
② fs_initcall-> _define_initcall
為了方便,我們直接用module_init 。
Makefile文件:
左右滑動(dòng)查看全部代碼>>>
KERN_DIR?=?/home/book/100ask_imx6ull-sdk/Linux-4.9.88
#??-C?表示將當(dāng)前的工作目錄切換到指定目錄中,M?表示模塊源碼目錄,?modules?表示編譯模塊
all:
?make?-C?$(KERN_DIR)?M=`pwd`?modules?
clean:
?make?-C?$(KERN_DIR)?M=`pwd`?modules?clean
?rm?-rf?modules.order
#?obj-m?表示將?proc_test.c?這個(gè)文件編譯為?proc_test.ko?模塊
obj-m?+=?proc_test.o
編譯:
傳到板子里測(cè)試:
可以看到,我們已經(jīng)成功地在proc中留下了一個(gè)hello_proc小腳印??梢钥吹?,我們創(chuàng)建的基于/proc下的“驅(qū)動(dòng)”與創(chuàng)建基于/dev下的真實(shí)的設(shè)備驅(qū)動(dòng)的思路及套路是很相似的。這些都是屬于內(nèi)核的范疇,都是屬于內(nèi)核的東西,內(nèi)核把想給我們能直接使用的東西(文件)都放于/proc、/dev等目錄下,我們?cè)趹?yīng)用端就可以很方便地訪問這些文件開發(fā)我們的應(yīng)用。
如果文章中有錯(cuò)誤,歡迎指出。
碼字不易,如果覺得文章有用,期待你的點(diǎn)贊、在看與轉(zhuǎn)發(fā),給小編更多的動(dòng)力分享更多的東西。
猜你喜歡
Linux下應(yīng)用開發(fā)基礎(chǔ)
【Linux筆記】LED驅(qū)動(dòng)實(shí)驗(yàn)(總線設(shè)備驅(qū)動(dòng)模型)
【Linux筆記】設(shè)備樹實(shí)例分析
學(xué)習(xí)STM32的一些經(jīng)驗(yàn)分享
我的單片機(jī)轉(zhuǎn)嵌入式Linux之路
STM32的map文件學(xué)習(xí)筆記
基于RT-Thread的智慧路燈案例實(shí)驗(yàn)分享
C語(yǔ)言、嵌入式中幾個(gè)非常實(shí)用的宏技巧
1024G 嵌入式資源大放送!包括但不限于C/C++、單片機(jī)、Linux等。在公眾號(hào)聊天界面回復(fù)1024,即可免費(fèi)獲??!
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問題,請(qǐng)聯(lián)系我們,謝謝!