Skip to content

内核编码规范

Linux内核有着严格的编码规范,这些规范不仅保证了代码的一致性和可读性,也有助于减少错误和提高代码质量。本章将详细介绍Linux内核的编码规范。

缩进和空格

8空格缩进

Linux内核使用8个空格作为缩进单位,这是内核编码规范中最显著的特点之一:

c
// 正确的缩进方式
if (condition) {
        do_something();
        if (another_condition) {
                do_another_thing();
        }
}

// 错误的缩进方式(4空格)
if (condition) {
    do_something();
    if (another_condition) {
        do_another_thing();
    }
}

空格使用规则

  1. 关键字后加空格

    c
    // 正确
    if (condition)
    for (i = 0; i < count; i++)
    
    // 错误
    if(condition)
    for(i = 0; i < count; i++)
  2. 二元操作符前后加空格

    c
    // 正确
    int result = a + b;
    
    // 错误
    int result = a+b;
  3. 一元操作符后不加空格

    c
    // 正确
    int result = -value;
    ptr++;
    
    // 错误
    int result = - value;
    ptr ++;

大括号风格

Linux内核采用K&R风格的大括号:

c
// 函数定义
int my_function(int arg1, int arg2)
{
        if (condition) {
                do_something();
        } else {
                do_something_else();
        }
        
        return 0;
}

// 错误的风格(Allman风格)
int my_function(int arg1, int arg2)
{
        if (condition)
        {
                do_something();
        }
        else
        {
                do_something_else();
        }
        
        return 0;
}

特殊情况

  1. 单行语句

    c
    // 正确
    if (condition)
            do_something();
    
    // 可接受(但不推荐)
    if (condition) {
            do_something();
    }
  2. 复杂的条件语句

    c
    if (condition1 ||
        condition2 ||
        condition3) {
            do_something();
    }

命名约定

函数命名

  1. 动词开头

    c
    // 正确
    int register_device(void);
    void unregister_device(void);
    char *alloc_buffer(size_t size);
    
    // 错误
    int device_register(void);
    void device_unregister(void);
  2. 私有函数使用下划线前缀

    c
    static int _internal_function(void);

变量命名

  1. 使用小写字母和下划线

    c
    // 正确
    int buffer_size;
    char *device_name;
    
    // 错误
    int bufferSize;
    char *DeviceName;
  2. 全局变量使用前缀

    c
    static int global_counter;

宏定义

  1. 全大写

    c
    #define MAX_BUFFER_SIZE 1024
    #define DEVICE_NAME "my_device"
  2. 避免函数式宏

    c
    // 尽量避免
    #define MIN(a, b) ((a) < (b) ? (a) : (b))
    
    // 更好的方式
    static inline int min_int(int a, int b)
    {
            return a < b ? a : b;
    }

注释规范

单行注释

使用/* */而不是//

c
/* 这是一个单行注释 */

/*
 * 这是一个多行注释
 * 可以跨越多行
 */

函数注释

c
/**
 * register_device - 注册一个新的设备
 * @dev: 要注册的设备结构体
 *
 * 该函数将设备注册到内核中,并分配必要的资源。
 * 返回0表示成功,负值表示错误码。
 */
int register_device(struct device *dev)
{
        /* 实现代码 */
        return 0;
}

数据类型使用

基本类型

  1. 使用内核定义的类型

    c
    // 正确
    u8  value8;
    u16 value16;
    u32 value32;
    u64 value64;
    
    s8  signed8;
    s16 signed16;
    s32 signed32;
    s64 signed64;
  2. 布尔值使用bool

    c
    #include <linux/types.h>
    
    bool is_enabled = true;

指针声明

c
// 正确
char *ptr;

// 错误
char * ptr;
char* ptr;

错误处理

错误码返回

  1. 使用负的错误码

    c
    if (allocation_failed)
            return -ENOMEM;
    
    if (invalid_parameter)
            return -EINVAL;
  2. 错误路径清理

    c
    struct device *dev;
    int ret;
    
    dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    if (!dev)
            return -ENOMEM;
    
    ret = initialize_hardware(dev);
    if (ret)
            goto err_free_dev;
    
    ret = register_device(dev);
    if (ret)
            goto err_cleanup_hw;
    
    return 0;

err_cleanup_hw: cleanup_hardware(dev); err_free_dev: kfree(dev); return ret;


## 内存管理

### 资源分配

1. **使用devm_*系列函数**:
```c
/* 自动管理的内存分配 */
void *ptr = devm_kzalloc(dev, size, GFP_KERNEL);

/* 自动管理的ioremap */
void __iomem *io_base = devm_ioremap_resource(dev, res);
  1. 检查分配结果
    c
    ptr = kmalloc(size, GFP_KERNEL);
    if (!ptr)
            return -ENOMEM;

锁机制

锁的使用

  1. 锁的命名

    c
    static DEFINE_MUTEX(device_mutex);
    static spinlock_t device_lock;
  2. 锁的范围

    c
    mutex_lock(&device_mutex);
    /* 临界区代码应尽可能短 */
    mutex_unlock(&device_mutex);

代码组织

头文件包含

  1. 包含顺序

    c
    /* 内核标准头文件 */
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    
    /* 设备特定头文件 */
    #include <linux/platform_device.h>
    
    /* 本地头文件 */
    #include "my_driver.h"
  2. 避免重复包含

    c
    #ifndef _MY_DRIVER_H
    #define _MY_DRIVER_H
    
    /* 头文件内容 */
    
    #endif /* _MY_DRIVER_H */

编译器特性

内联函数

c
static inline int get_device_status(void)
{
        return readl(device_base + STATUS_REG);
}

编译器警告

  1. 使用适当的编译器属性
    c
    __attribute__((noreturn))
    void panic_handler(void)
    {
            /* 处理panic */
    }
    
    __attribute__((format(printf, 1, 2)))
    void custom_printk(const char *fmt, ...)
    {
            /* 自定义打印函数 */
    }

调试和日志

printk使用

  1. 使用适当的日志级别

    c
    pr_info("Device initialized successfully\n");
    pr_err("Failed to allocate memory\n");
    pr_debug("Debug information: value = %d\n", value);
  2. 避免在生产代码中使用printk

    c
    #ifdef DEBUG
    #define dbg_print(fmt, ...) pr_debug(fmt, ##__VA_ARGS__)
    #else
    #define dbg_print(fmt, ...)
    #endif

最佳实践

  1. 保持函数简短:单个函数不应超过100行
  2. 避免深层嵌套:使用早期返回来减少嵌套层级
  3. 使用const修饰符:对不修改的数据使用const
  4. 初始化变量:所有变量在使用前都应初始化
  5. 避免魔法数字:使用命名常量代替直接的数字

遵循这些编码规范不仅有助于代码审查和维护,也能帮助你编写出更加健壮和高效的内核驱动程序。