当前位置: 首页 > news >正文

Linux内核的CodingStyle

Chapter 1: Indentation(缩进)

缩进要使用tab键,并且tab键的宽度为8个字符

switch和case对其,不用缩进

    switch (suffix) {
    case 'G':
    case 'g':
        mem <<= 30;
        break;
    case 'M':
    case 'm':
        mem <<= 20;
        break;
    case 'K':
    case 'k':
        mem <<= 10;
        /* fall through */
    default:
        break;
    }

一行只有一个表达式,不要写成下面这样

if (condition) do_this;
      do_something_everytime;

Chapter 2: Breaking long lines and strings(换行)

代码长度控制在80个字符以内,过长的代码需要换行,并且保持代码的可阅读性

printk(KERN_WARNING "Warning this is a long printk with "
                       "3 parameters a: %u b: %u "
                       "c: %u \n", a, b, c);

Chapter 3: Placing Braces and Spaces(括号和空格的位置)

3.1 大括号

非函数的代码块,左大括号不用另起一行

if (x is true) {
        we do y
    }
switch (action) {
    case KOBJ_ADD:
        return "add";
    case KOBJ_REMOVE:
        return "remove";
    case KOBJ_CHANGE:
        return "change";
    default:
        return NULL;
    }
do {
        body of do-loop
    } while (condi)
    
if (x == y) {
        ..
    } else if (x > y) {
        ...
    } else {
        ....
    }

只有一行的语句块不用大括号

if (condition)
    action();

函数的代码块要单独占一行

int function(int x)
{
    body of function
}

3.2 空格

下列关键字后面追加空格

if, switch, case, for, do, while

下列关键字后面不加空格

sizeof, typeof, alignof, or __attribute__. 
s = sizeof( struct file );

双目运算符和三目运算符要加空格间隔开

=  +  -  <  >  *  /  %  |  &  ^  <=  >=  ==  !=  ?  :

单目运算符,不论前置还是后置都不加空格

&  *  +  -  ~  !  sizeof  typeof  alignof  __attribute__  defined
++  --

结构体成员访问运算符后面不加空格

. ->

Chapter 4: Naming(命名)

全局变量和函数需要有一个描述性的名称

count_active_users()cntusr()

局部变量命名要简洁,例如:tmp

Chapter 5: Typedefs

尽量不要使用typedef,使用typedef主要为了下面的用途

  1. 完全不透明的类型(访问这些类型也需要对应的访问函数)

ex. pid_t, uid_t, pte_t … 等等

  1. 避免整型数据的困扰,比如int, long类型的长度在不同体系结构中不一致等等, 使用 u8/u16/u32 来代替整型定义
  2. 当使用kernel的sparse工具做变量类型检查时, 可以typedef一个类型.
  3. 定义C99标准中的新类型
  4. 为了用户空间的类型安全

内核空间的结构体映射到用户空间时使用typedef, 这样即使内核空间的数据结构有变化, 用户空间也能正常运行

Chapter 6: Functions(函数)

函数要简短,一个函数只做一件事情

函数长度一般不超过2屏(1屏的大小是 80x24), 也就是48行

如果函数中的 switch 有很多简单的 case语句, 那么超出2屏也可以

函数中局部变量不能超过 5~10 个

函数与函数之间空一行,但是和EXPORT*之间不用空

int one_func(void)
{
        return 0;
}

int system_is_up(void)
{
    return system_state == SYSTEM_RUNNING;
}
EXPORT_SYMBOL(system_is_up);

Chapter 7: Centralized exiting of functions(函数退出)

将函数的退出集中在一起, 特别有需要清理内存的时候.(goto 并不是洪水猛兽, 有时也很有用)

int fun(int a)
{
    int result = 0;
    char *buffer = kmalloc(SIZE);

    if (buffer == NULL)
        return -ENOMEM;

    if (condition1) {
        while (loop1) {
            ...
        }
        result = 1;
        goto out;
    }
    ...
out:
    kfree(buffer);
    return result;
}

Chapter 8: Commenting(注释)

注释code做了什么, 而不是如何做的

使用C89的注释风格(/* … */), 不要用C99的注释风格(// …)

Chapter 9: You’ve made a mess of it(控制缩进的方法)

(defun c-lineup-arglist-tabs-only (ignored)
  "Line up argument lists by tabs, not spaces"
  (let* ((anchor (c-langelem-pos c-syntactic-element))
     (column (c-langelem-2nd-pos c-syntactic-element))
     (offset (- (1+ column) anchor))
     (steps (floor offset c-basic-offset)))
    (* (max steps 1)
       c-basic-offset)))

(add-hook 'c-mode-common-hook
          (lambda ()
            ;; Add kernel style
            (c-add-style
             "linux-tabs-only"
             '("linux" (c-offsets-alist
                        (arglist-cont-nonempty
                         c-lineup-gcc-asm-reg
                         c-lineup-arglist-tabs-only))))))

(add-hook 'c-mode-hook
          (lambda ()
            (let ((filename (buffer-file-name)))
              ;; Enable kernel mode for the appropriate files
              (when (and filename
                         (string-match (expand-file-name "~/src/linux-trees")
                                       filename))
                (setq indent-tabs-mode t)
                (c-set-style "linux-tabs-only")))))

Chapter 10: Kconfig configuration files(Kconfig 配置文件)

config AUDIT
    bool "Auditing support"
    depends on NET
    help
      Enable auditing infrastructure that can be used with another
      kernel subsystem, such as SELinux (which requires this for
      logging of avc messages output).  Does not do system-call
      auditing without CONFIG_AUDITSYSCALL.

不稳定的特性要加上EXPERIMENTAL

config SLUB
    depends on EXPERIMENTAL && !ARCH_USES_SLAB_PAGE_STRUCT
    bool "SLUB (Unqueued Allocator)"
    ...

危险的特性要加上DANGEROUS

config ADFS_FS_RW
    bool "ADFS write support (DANGEROUS)"
    depends on ADFS_FS
    ...

Chapter 11: Data structures(数据结构)

结构体要包含一个引用计数字段 (内核中没有自动垃圾收集, 需要手动释放内存)

需要保证结构体数据一致性时要加锁

结构体中有时甚至会需要多层的引用计数

ex. struc mm_struct, struct super_block

Chapter 12: Macros, Enums and RTL(宏、枚举类型和RTL)

宏定义常量常使用大写字母

#define CONSTANT 0x12345

宏定义多行语句时要放入 do - while 中, 此时宏的名称用小写

#define macrofun(a, b, c)           \
    do {                    \
        if (a == 5)         \
            do_this(b, c);      \
    } while (0)

宏定义表达式要放在()中

#define CONSTANT 0x4000
#define CONSTEXP (CONSTANT | 3)

Chapter 13: Printing kernel messages(打印内核信息)

保持打印信息的简明清晰

比如, 不要用 “dont”, 而是使用 “do not” 或者 “don’t”

内核信息不需要使用 “.” 结尾

打印 “(%d)” 之类的没有任何意义, 应该避免

选择合适的打印级别(调试,还是警告,错误等等)

Chapter 14: Allocating memory(分配内存)

分配内存时sizeof(指针) 而不是 sizeof(结构体)

p = kmalloc(sizeof(*p), ...);

Chapter 15: The inline disease(内联的弊端)

如果一个函数有3行以上, 不要使用 inline 来标记它

Chapter 16: Function return values and names(函数的返回值及命名)

如果函数功能是一个动作或者一个命令时, 返回 int型的 error-code

比如, add_work() 函数执行成功时返回 0, 失败时返回对应的error-code(ex. -EBUSY)

如果函数功能是一个判断时, 返回 “0” 表示失败, “1” 表示成功

所有Exported函数, 公用的函数都要上述2条要求

所有私有(static)函数, 不强制要求, 但最好能满足上面2条要求

如果函数返回真实计算结果, 而不是是否成功时, 通过返回计算结果范围外的值来表示失败

比如一个返回指针的函数, 通过返回 NULL 来表示失败

Chapter 17: Don’t re-invent the kernel macros(不要重复发明内核宏)

内核定义的宏在头文件 include/linux/kernel.h 中, 想定义新的宏时, 首先看看其中是否已有类似的宏可用

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f))

Chapter 18: Editor modelines and other cruft(编辑器模式行和其他

不要在代码中加入特定编辑器的内容或者其他工具的配置,

-*- mode: c -*-
/*
Local Variables:
compile-command: "gcc -DMAGIC_DEBUG_FLAG foo.c"
End:
*/
/* vim:set sw=8 noet */

Chapter 19: Inline assembly(内联汇编)

尽量不要使用内联汇编,一般汇编代码应该都在.S文件中的,也许你会使用volatile关键确保去寄存器中取值,但是gcc有可能忽略此请求

相关文章:

  • 常见面试题及面试准备阶段要思考的问题
  • Qt中事件的传递过程
  • Qt事件初探-发现事件
  • Qt中TCP通信的实现
  • ubuntu下MQTT的移植安装
  • MQTT协议 发布/订阅 机制初探 - (模拟物联网传感器设备和控制模块间的通信)
  • MQTT初探
  • VScode快捷键
  • Linux C的MQTT测试代码编写 - 跨主机的MQTT客户端通信
  • Linux系统嵌入式设备的串口通信
  • Anaconda在Windows的安装
  • notebook修改默认打开的路径
  • Python实现MQTT客户端的基本功能
  • 初探C++标准库
  • 命名空间(名字空间)
  • [译] 理解数组在 PHP 内部的实现(给PHP开发者的PHP源码-第四部分)
  • 【391天】每日项目总结系列128(2018.03.03)
  • avalon2.2的VM生成过程
  • JS+CSS实现数字滚动
  • mysql 数据库四种事务隔离级别
  • PhantomJS 安装
  • PHP变量
  • python_bomb----数据类型总结
  • Transformer-XL: Unleashing the Potential of Attention Models
  • 给第三方使用接口的 URL 签名实现
  • 如何胜任知名企业的商业数据分析师?
  • 数据结构java版之冒泡排序及优化
  • 微信小程序--------语音识别(前端自己也能玩)
  • 继 XDL 之后,阿里妈妈开源大规模分布式图表征学习框架 Euler ...
  • ​LeetCode解法汇总518. 零钱兑换 II
  • #我与Java虚拟机的故事#连载18:JAVA成长之路
  • (04)Hive的相关概念——order by 、sort by、distribute by 、cluster by
  • (3)STL算法之搜索
  • (31)对象的克隆
  • (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  • (C)一些题4
  • (done) NLP “bag-of-words“ 方法 (带有二元分类和多元分类两个例子)词袋模型、BoW
  • (Java岗)秋招打卡!一本学历拿下美团、阿里、快手、米哈游offer
  • (LeetCode) T14. Longest Common Prefix
  • (二)linux使用docker容器运行mysql
  • (转)Java socket中关闭IO流后,发生什么事?(以关闭输出流为例) .
  • (转)树状数组
  • ***微信公众号支付+微信H5支付+微信扫码支付+小程序支付+APP微信支付解决方案总结...
  • .gitignore文件---让git自动忽略指定文件
  • .NET C#版本和.NET版本以及VS版本的对应关系
  • .NET delegate 委托 、 Event 事件,接口回调
  • .net framework4与其client profile版本的区别
  • .net FrameWork简介,数组,枚举
  • .NET MVC、 WebAPI、 WebService【ws】、NVVM、WCF、Remoting
  • .NET WebClient 类下载部分文件会错误?可能是解压缩的锅
  • .NET 命令行参数包含应用程序路径吗?
  • .NET是什么
  • /bin/rm: 参数列表过长"的解决办法
  • @Autowired @Resource @Qualifier的区别
  • @JoinTable会自动删除关联表的数据