当前位置: 首页 > 科技观察

说说Linux内核驱动的编码风格

时间:2023-03-23 12:14:54 科技观察

最近提交了一些驱动给Linux内核。在提交的过程中,我发现自己的代码离linux内核的编码风格要求还差得很远。一开始只是简单浏览了内核文档中的CodingStyle一文。真正写代码的时候,很多细节我都不会去管。然而,在不遵守规则的程序员队伍中,我并不是唯一一个。如果你查看drivers/staging下的代码,你会发现很多驱动并没有严格遵守内核的编码风格,在很多驱动的TODO文件中,“checkpatch.plfixes”将是他们的目标之一(checkpatch.pl是用来检查代码是否符合编码风格的脚本)。不可否认,编码风格是仁智之事。比如微软提倡的匈牙利语命名法,在Linus看来就是一种脑残的做法。也许你不认同Linus制定的编码风格,但是在提交内核驱动的时候,最好还是以大局为重。对于这样一个巨大的市场式开发,随意编写代码肯定会带来严重的可维护性灾难。一些辅助工具当代码量达到一定程度时,手动检查和修改编码风格是非常繁琐的。幸运的是,我们还有一些工具可以使用。scripts/checkpatch.pl这是一个检查代码是否符合内核编码标准的脚本。顾名思义,checkpatch是用来检查补丁的,默认调用确实是一样的。如果用于查看原始文件,则需要加上“-f”选项。我们来看一段无聊的代码(文件名为print_msg.c):voidprint_msg(inta){switch(a){case1:printf("a==1\n");break;case2:printf("a==2\n");break;}}这段代码的编码风格有问题吗?使用checkpatch.pl检查:scripts/checkpatch.pl-fprint_msg.c检查的结果是:ERROR:switchandcaseshouldbeatthesameindent#3:FILE:switch.c:3:+switch(a){+case1:[...]+案例2:总计:1个错误,0个警告,12行检查开关。机箱样式问题,请检查。如果这些错误中的任何一个是误报,请将它们报告给维护人员,请参阅维护人员中的检查补丁。在Linux内核的编码风格中,switch与case要求的缩进相同。本例代码很少,只有一处错误,手工修改非常方便。如果有很多类似的缩进错误怎么办?scripts/Lindentscripts目录中的工具Lindent可用于自动更正缩进问题。提醒一下,使用Lindent需要系统安装缩进工具。对于上面的例子,执行Lindent命令:scripts/Lindentprint_msg.c得到的新代码是:voidprint_msg(inta){switch(a){case1:printf("a==1\n");break;case2:printf("a==2\n");break;}}sedsed是一个流编辑器,它强大的功能可以帮助我们处理很多重复性的工作。例如,Linux内核的编码风格要求行尾不能有空格(包括制表符),可以使用sed来去除这些空格。我自己的坏习惯经常在代码行的末尾留下一些空格。比如当一行代码太长需要改的时候,我总是下意识地在换行的地方打一个空格。另外,我常用的编辑器之一Kate,经常会在空行前留下几个缩进的Tabs,方便对齐(如下图)。手动去除这些尾随空格是一项繁重的工作,但使用sed则小菜一碟。命令格式如下:sed's/[\t]*$//g'your_code.cSomeCodingStyleindentationofneedsattention1.除注释、文档和Kconfig外,使用Tab缩进代替空格,以及The制表符的宽度为8个字符;2.在switch...case...语句中,switch和case的缩进相同(参考上文);3.花括号的使用参考了K&R风格。如果是函数,花括号另起一行:intfunction(intx){bodyofffunction}否则,花括号应该放在语句的***旁边:if(xistrue){wedoy}如果有只有一行语句,你不需要使用花括号:if(condition)action();但是,对于条件语句,如果一个分支是一行语句,另一个分支是多行语句,则需要保持一致并使用花括号:if(condition){do_this();do_that();}else{otherwise();}空格4.关键字“if,switch,case,for,do,while”后需要加空格,如:if(something)5.关键字“sizeof,typeof,alignof,or__attribute__”后","后不要加空格,如:sizeof(structfile)6.括号内的表达式两边不要加空格,例如下面是反例:sizeof(structfile)7.最二元运算符和三元运算符两边都需要空格,例如"=+–<>*/%|&^<=>===!=?:";8、一元运算符后不能有空格,如"&*+–~!sizeoftypeofalignof__attribute__defined";9.前缀递增和递减运算符后和后缀递增和递减运算符(“++”和“–”)之前不需要空格;10.结构成员运算符(“.”和“->")两边不需要空格;11.行尾不需要空格;注意12.使用C89的"/*...*/"风格,而不是C99的"//..."风格;13、多行注释可以参考下面的例子:/**Thisisthepreferredstyleformmulti-line*commentsintheLinuxkernelsourcecode.*Pleaseuseitconsistently.**说明:Acolumnofasterrisksontheleftside,*withbeginningandendingalmost-blanklines.*/Kconfig14,"config"定义下面的语句用Tab缩进,help下面的语句用两个空格缩进,如:configAUDITbool"Auditingsupport"dependsonNEThelpEnableauditinginfrastructurethatcanbeusedwithaotherassubsystemssuchas:(whichrequiresthisforloggingofavcmessagesoutput).Doesnotdosystem-callauditingwithoutCONFIG_AUDITSYSCALL.宏15.多行宏定义需要用“do..while”封装,如:#definemacrofun(a,b,c)\do{\if(a==5)\do_this(b,c);\}while(0)函数返回值16、函数返回值***的定义也必须遵循一定的规则。如果函数名是动作或命令式语句,则应以错误码形式返回(通常为0表示成功,-Exxx形式的负数表示错误),如:do_something()如果名称函数的of是一个判断语句,返回值应该类似于一个布尔值(通常1表示成功,0表示错误),如:something_is_present()