请善用工具审核您的内核代码:)
在寫內(nèi)核代碼時(shí)。代碼風(fēng)格(coding style)是一個(gè)非常重要的部分,否則內(nèi)核代碼將變的混亂不堪。
那么什么樣的代碼算美麗的代碼?什么樣的代碼符合c99這種標(biāo)準(zhǔn)?此外,程序?qū)懲曛?#xff0c;有什么工具可以幫我們檢查代碼有沒有指針錯(cuò)誤?客官且隨我看看這三個(gè)工具:
1. 代碼風(fēng)格篇
想開發(fā)一個(gè)內(nèi)核程序?你的電腦有內(nèi)核源代碼么?無論是以前用來編譯內(nèi)核或者你自己查閱資料,假設(shè)您的電腦上有內(nèi)核源代碼,好的,本節(jié)將介紹一個(gè)非常多人都不知道的強(qiáng)大的工具 -- checkpatch。
So, where is it ?
ok ,打開內(nèi)核代碼,cd 到 “ scripts?”文件夾下,查看有木有checkpatch.pl 文件?
How to use ? Yup, very easy ! Please use " patch-to-kernel/scripts/checkpatch.pl ?--file yourcode.c?" !
還不明確?來看看我怎么用:
~/kernel/linux-3.12.1/scripts/checkpatch.pl --file ../net_drive/netdump.c
那么這個(gè)工具有什么好?請(qǐng)看以下這個(gè)代碼:
/** file : netdump.c* (C) 2014 Yunlong Zhou <reaper888@yeah.net>* Under licence GPL** Introduction :* This modules will scan netdevices and report them via printk ** Useage: 1. make -- you should make the module firstly(with Makefile )* 2. su -- use root * 3. insmod netdump.ko -- install the module* 4. dmesg | tail -- check the status that module print!* 5. rmmod netdump -- after use ,please don't forget rmmove the module **/#include <linux/module.h> /* MODULE* */ #include <linux/kernel.h> /* printk */ #include <linux/netdevice.h> /* dev_get_by_index */static int __init hello_init(void) {printk("netscan module enter\n");struct net_device *dev;struct rtnl_link_stats64 temp;int idx=1; /* first netdevice if it exists */do{dev = dev_get_by_index(&init_net,idx);if (dev==NULL) {printk("Last netdevice index %d\n",idx-1);}else {const struct rtnl_link_stats64 *stats = dev_get_stats(dev,&temp);printk("%s: ifindex %d\n",dev->name,dev->ifindex);// more in this struct than reported here ! printk("This is the current status jus get !\n");printk("packets:%llu/%llu bytes: %llu/%llu errors:%llu dropped:%llu\n\n",stats->tx_packets,stats->rx_packets,stats->tx_bytes,stats->rx_bytes,stats->rx_errors,stats->rx_dropped);}idx++;}while(dev!=NULL);return 0; }static void __exit hello_exit(void) {printk("netscan module exit\n"); }module_init(hello_init); module_exit(hello_exit);MODULE_AUTHOR("Zhou Yunlong <reaper888@yeah>"); MODULE_DESCRIPTION("scan netdevices and report them via printk"); MODULE_LICENSE("GPL");
寫過內(nèi)核模塊的童鞋能輕易的分辯。這是個(gè)內(nèi)核模塊。再有經(jīng)驗(yàn)的童鞋,可以看出來這個(gè)模塊的主要工作都在 init 時(shí)做了(也即insmod 模塊時(shí))。
那么做了什么工作呢?事實(shí)上非常easy。就是讀取網(wǎng)卡設(shè)備的狀態(tài)然后顯示出來,比方說發(fā)/收多少數(shù)據(jù)包,多少字節(jié)等。并且因?yàn)榇a圖簡(jiǎn)便,通過 printk 輸出,所以信息僅僅能通過 dmesg查看!
對(duì)于有經(jīng)驗(yàn)的童鞋,會(huì)在編譯模塊的Makefile 文件里加入 -Wall 標(biāo)志(W 即warning,all即全部,所以加入 -Wall 標(biāo)志位會(huì)打印出全部編譯時(shí)的警告)。對(duì)于這段代碼:
$ make make -Wall -C /lib/modules/`uname -r`/build M=`pwd` modules make[1]: Entering directory `/home/long/kernel/linux-3.12.1-rtpatched'CC [M] /tmp/netdump.o /tmp/netdump.c: In function ‘hello_init’: /tmp/netdump.c:24:5: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]Building modules, stage 2.MODPOST 1 modulesCC /tmp/netdump.mod.oLD [M] /tmp/netdump.ko make[1]: Leaving directory `/home/long/kernel/linux-3.12.1-rtpatched' $ sudo insmod netdump.ko $ dmesg .... [ 8300.686085] netscan module enter [ 8300.686095] lo: ifindex 1 [ 8300.686097] This is the current status jus get ! [ 8300.686101] packets:888/888 bytes: 282809/282809 errors:0 dropped:0 [ 8300.686101] [ 8300.686105] eth1: ifindex 2 [ 8300.686107] This is the current status jus get ! [ 8300.686110] packets:945987/2384507 bytes: 77162255/3264031681 errors:0 dropped:35 [ 8300.686110] [ 8300.686115] eth3: ifindex 3 [ 8300.686117] This is the current status jus get ! [ 8300.686119] packets:0/0 bytes: 0/0 errors:0 dropped:0 [ 8300.686119] [ 8300.686123] sit0: ifindex 4 [ 8300.686125] This is the current status jus get ! [ 8300.686128] packets:0/0 bytes: 0/0 errors:0 dropped:0 [ 8300.686128] [ 8300.686131] Last netdevice index 4
我們能夠看到,程序編譯時(shí)也僅僅提示了一個(gè)“ ISO C90 forbids mixed declarations and code?”錯(cuò)誤,對(duì)于有經(jīng)驗(yàn)的童鞋,能夠非常輕松的排除這個(gè)錯(cuò)誤,就是把提示警告的函數(shù)中全部的聲明部分放在函數(shù)最前面,而其它代碼放在聲明后面。
那么這種代碼在您平時(shí)編程中是不是堪稱完美?編譯器不報(bào)錯(cuò)(上述簡(jiǎn)單的警告。我們能夠輕松排除)。程序執(zhí)行正常。
那么這樣一段程序?qū)τ?checkpatch 來說是什么樣的?我們能夠看看:
$ ~/kernel/linux-3.12/linux-3.12.1/scripts/checkpatch.pl --file netdump.c > log
打開log 文件:
ERROR: trailing whitespace #7: FILE: netdump.c:7: + * This modules will scan netdevices and report them via printk $WARNING: line over 80 characters #9: FILE: netdump.c:9: + * Useage: 1. make -- you should make the module firstly(with Makefile ) ... total: 22 errors, 16 warnings, 63 lines checkedNOTE: whitespace errors detected, you may wish to use scripts/cleanpatch orscripts/cleanfilenetdump.c has style problems, please review.
最后一行,checkpatch 工具非常輕柔的告訴我們,netdump.c 文件有代碼風(fēng)格問題,請(qǐng)改正吧!
“ total: 22 errors, 16 warnings, 63 lines checked?”!63行的代碼。有22個(gè)錯(cuò)誤,16個(gè)警告!我們能夠先看看ERROR部分(由于ERROR部分是必需要改的,重要的錯(cuò)誤):
$ grep "ERROR" log | sort | uniq ERROR: code indent should use tabs where possible --- 代碼行前面的空白處應(yīng)該使用TAB 而不是空格 ERROR: do not use C99 // comments --- 不能使用C99中的"//"型凝視,須要使用 "/**/"型 ERROR: space required before the open brace '{' --- 對(duì)于 for,if,while等有涉及到代碼段時(shí),使用 { 和 } 時(shí)。須要在{ 之前和}之后(假設(shè)后面有東西的話。否則就成了代碼行末尾空白)加空格,比方 if (cond) { ... } else { ... } ERROR: space required after that close brace '}' ERROR: space required after that ',' (ctx:VxO) --- 帶參時(shí),比方foo(a,b),在a,后b之前須要空格,所以正確使用方法是: foo(a, b) ERROR: space required after that ',' (ctx:VxV) ERROR: space required before that '&' (ctx:OxV) --- 此條和上面的帶參反復(fù) ERROR: space required before the open parenthesis '(' --- 相似{} ,()前后也須要空格 ERROR: spaces required around that '==' (ctx:VxV) --- 比較"=="/"!="和賦值"="前后也須要空格 ERROR: spaces required around that '=' (ctx:VxV) ERROR: spaces required around that '!=' (ctx:VxV) ERROR: trailing whitespace --- 代碼行的末尾有多余的空白(空格/tab>)
分析完ERROR。我們?cè)趤砜纯碬ARNING:
$ grep "WARNING" log | sort | uniq >b -- 吃驚的發(fā)現(xiàn),16個(gè)警告去反復(fù)之后僅僅有4類 WARNING: line over 80 characters -- 代碼行多余80個(gè)字符!為什么是80個(gè)字符。有興趣 能夠去查查(小透露一下,歷史原因。) WARNING: please, no space before tabs -- tab前有空格。全部空格一律使用tab! WARNING: please, no spaces at the start of a line -- 行開始沒有空白 WARNING: printk() should include KERN_ facility level -- printk沒有"KERN_"這種輸出級(jí)別!
為>什么這僅僅是warning?大家都知道,假設(shè)printk沒有帶輸出級(jí)別,它將採(cǎi)用默認(rèn)!
如今還敢說你的代碼習(xí)慣非常好么?你能夠試驗(yàn)一下你最自豪的代碼。祝您玩的愉快
二、 代碼檢測(cè)篇
2.1?Coccinelle?
Coccinelle是一個(gè)程序的匹配和轉(zhuǎn)換引擎,它提供了語言SMPL(語義補(bǔ)丁語言)用于指定C代碼所需的匹配和轉(zhuǎn)換。Coccinelle 最初是用來幫助Linux的演變,支持更改庫(kù)應(yīng)用程序編程接口,比方重命名一個(gè)函數(shù)。添加一個(gè)依賴于上下文的函數(shù)參數(shù)或者又一次組織一個(gè)數(shù)據(jù)結(jié)構(gòu)。除此之外,Coccinelle頁(yè)被人用來查找或者修復(fù)系統(tǒng)代碼的bug。
2.1.1 安裝
(1) sudo apt-get build-dep coccinelle?
假設(shè)您的apt-get 提示找不到coccinelle。建議您把你的" /etc/apt/sources.list "配成我這種吧:
$ cat /etc/apt/sources.list deb http://mirrors.163.com/debian wheezy main non-free contrib deb-src http://mirrors.163.com/debian wheezy main non-free contrib
(2) ./configure --enable-release
(3) sudo?make all
(4) sudo make install
2.1.2 使用
事實(shí)上Coccinelle使用起來非常easy。比方上面的內(nèi)核模塊代碼。我們?cè)鯓邮褂胏occinelle檢查這段代碼?僅僅須要在編譯時(shí)加入coccicheck 選項(xiàng)就可以!
比方,我們的Makefile能夠這么寫:
$ cat Makefile obj-m:=netdump.o default:make -Wall -C /lib/modules/`uname -r`/build M=`pwd` modulescocci:make -C /lib/modules/`uname -r`/build coccicheck MODE=report M=`pwd` clean:make -C /lib/modules/`uname -r`/build M=`pwd` clean
? 這樣。我們能夠使用 make 來簡(jiǎn)單編譯模塊,還能夠使用 make cocci 來使用coccinelle對(duì)代碼進(jìn)行檢查:
$ make cocci make -C /lib/modules/`uname -r`/build coccicheck MODE=report M=`pwd` make[1]: Entering directory `/home/long/Mar_class/linux-3.12.9'Please check for false positives in the output before submitting a patch. When using "patch" mode, carefully review the patch before submitting it.make[1]: Leaving directory `/home/long/kernel/linux-3.12.9'
2.2?sparse
Sparse 是用于 C 語言的語法分析器,用以對(duì) C 代碼進(jìn)行靜態(tài)檢查,它不但能夠檢查 ANSI C 并且還能檢查具有 gcc 擴(kuò)展的 C 。
在 Linux 中,不但能夠檢查用戶端代碼。還能夠檢查內(nèi)核代碼。起初它由 Linus 編寫,后來交給其它人維護(hù)。
Sparse通過 gcc 的擴(kuò)展屬性 __attribute__ 以及自定義的 __context__ 來對(duì)代碼進(jìn)行靜態(tài)檢查。
以下我們來看看這個(gè)奇妙的工具:
2.2.1 安裝
對(duì)于sparse的安裝,能夠使用多種方法:
最簡(jiǎn)單的一種就是使用apt-get安裝:sudo apt-get install sparse
其次是從站點(diǎn)下載,下載sparse-0.4.4.tar.gz壓縮包后解壓,然后直接 make 再 make install 就可以!
最后就是使用?git clone git://git.kernel.org/pub/scm/devel/sparse/sparse.git 克隆sparse倉(cāng)庫(kù),然后進(jìn)入倉(cāng)庫(kù)先使用?git tag 查看最新的版本號(hào)。然后使用?$ git checkout -b stable v0.4.4 切到最新的版本號(hào),最后連續(xù)使用root 權(quán)限make 再 make install 安裝即完畢了。
2.2.2 使用
事實(shí)上sparse的使用比上面介紹的coccinelle還簡(jiǎn)單,僅僅須要在make 后加入 “ C=2 ”。所以上面的Makefile 還能夠擴(kuò)展成:
$ cat Makefile obj-m:=netdump.o default:make -Wall -C /lib/modules/`uname -r`/build M=`pwd` modulescocci:make -C /lib/modules/`uname -r`/build coccicheck MODE=report M=`pwd` sparse:make C=2 -C /lib/modules/`uname -r`/build M=`pwd`clean:make -C /lib/modules/`uname -r`/build M=`pwd` clean
此時(shí)我們僅僅要使用 make sparse 就可以使用sparse工具對(duì)代碼進(jìn)行檢查:
$ make sparse make C=2 -C /lib/modules/`uname -r`/build M=`pwd` make[1]: Entering directory `/home/long/kernel/linux-3.12.9'LD /tmp/test/built-in.oCHECK /tmp/test/netdump.c /tmp/test/netdump.c:23:9: warning: mixing declarations and code /tmp/test/netdump.c:29:48: warning: incorrect type in argument 1 (different base types) /tmp/test/netdump.c:29:48: expected struct net *net /tmp/test/netdump.c:29:48: got struct net extern [addressable] [toplevel] init_netCC [M] /tmp/test/netdump.o /tmp/test/netdump.c: In function ‘hello_init’: /tmp/test/netdump.c:23:2: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement] /tmp/test/netdump.c:29:4: error: incompatible type for argument 1 of ‘dev_get_by_index’ In file included from /tmp/test/netdump.c:18:0: include/linux/netdevice.h:1795:27: note: expected ‘struct net *’ but argument is of type ‘struct net’ make[2]: *** [/tmp/test/netdump.o] Error 1 make[1]: *** [_module_/tmp/test] Error 2 make[1]: Leaving directory `/home/long/kernel/linux-3.12.9' make: *** [sparse] Error 2
由于sparse是對(duì)屬性進(jìn)行檢查,所以在上面使用 make sparse 之前,我把代碼第29行“?dev = dev_get_by_index(&init_net,idx); ”中的& 去掉了,所以sparse會(huì)檢測(cè)出參數(shù)格式錯(cuò)誤!
所以你能夠想象,你的代碼中假設(shè)指針使用錯(cuò)誤,sparse都會(huì)一一指出哦!
!
!
是不是非常幸福?
注:如今貌似也有針對(duì)其它語言的sparse工具,前幾天剛看到有python的sparse,只是還沒嘗試過。
三、總結(jié)篇
使用第一節(jié)中的checkpatch是讓我們養(yǎng)成好的代碼風(fēng)格。既美觀又符合內(nèi)核中的代碼風(fēng)格,何樂而不為?事實(shí)上,不管是對(duì)于已工作的程序員還是對(duì)于要找工作的學(xué)生來說,養(yǎng)成好的代碼習(xí)慣和風(fēng)格總是好的。最大的優(yōu)點(diǎn)是讀代碼方便。其次是好的代碼風(fēng)格能夠讓別人對(duì)你有了最主要的認(rèn)識(shí)!
第二節(jié)中的兩個(gè)工具都是由來已久,并且在內(nèi)何編碼界使用也非常廣泛,假設(shè)你每次都使用這兩個(gè)工具檢查。相信對(duì)你的代碼能力也會(huì)有非常大的提升。
最后送大家一句: 學(xué)習(xí)easy,堅(jiān)持不易。且學(xué)且珍惜!
==================
很多其它閱讀:?
[1] http://kernelnewbies.org/KernelHacking
[2]?http://coccinelle.lip6.fr/
[3]?https://home.regit.org/technical-articles/coccinelle-for-the-newbie/
[4]?http://kernelnewbies.org/Sparse
[5]?http://www.cnblogs.com/wang_yb/p/3575039.html
轉(zhuǎn)載于:https://www.cnblogs.com/bhlsheji/p/5386611.html
總結(jié)
以上是生活随笔為你收集整理的请善用工具审核您的内核代码:)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2016去哪儿编程题:乘坐公交
- 下一篇: 模拟post请求--测试api是否可用-