Appearance
内核编码规范
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();
}
}空格使用规则
关键字后加空格:
c// 正确 if (condition) for (i = 0; i < count; i++) // 错误 if(condition) for(i = 0; i < count; i++)二元操作符前后加空格:
c// 正确 int result = a + b; // 错误 int result = a+b;一元操作符后不加空格:
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;
}特殊情况
单行语句:
c// 正确 if (condition) do_something(); // 可接受(但不推荐) if (condition) { do_something(); }复杂的条件语句:
cif (condition1 || condition2 || condition3) { do_something(); }
命名约定
函数命名
动词开头:
c// 正确 int register_device(void); void unregister_device(void); char *alloc_buffer(size_t size); // 错误 int device_register(void); void device_unregister(void);私有函数使用下划线前缀:
cstatic int _internal_function(void);
变量命名
使用小写字母和下划线:
c// 正确 int buffer_size; char *device_name; // 错误 int bufferSize; char *DeviceName;全局变量使用前缀:
cstatic int global_counter;
宏定义
全大写:
c#define MAX_BUFFER_SIZE 1024 #define DEVICE_NAME "my_device"避免函数式宏:
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;
}数据类型使用
基本类型
使用内核定义的类型:
c// 正确 u8 value8; u16 value16; u32 value32; u64 value64; s8 signed8; s16 signed16; s32 signed32; s64 signed64;布尔值使用bool:
c#include <linux/types.h> bool is_enabled = true;
指针声明
c
// 正确
char *ptr;
// 错误
char * ptr;
char* ptr;错误处理
错误码返回
使用负的错误码:
cif (allocation_failed) return -ENOMEM; if (invalid_parameter) return -EINVAL;错误路径清理:
cstruct 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);- 检查分配结果:c
ptr = kmalloc(size, GFP_KERNEL); if (!ptr) return -ENOMEM;
锁机制
锁的使用
锁的命名:
cstatic DEFINE_MUTEX(device_mutex); static spinlock_t device_lock;锁的范围:
cmutex_lock(&device_mutex); /* 临界区代码应尽可能短 */ mutex_unlock(&device_mutex);
代码组织
头文件包含
包含顺序:
c/* 内核标准头文件 */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> /* 设备特定头文件 */ #include <linux/platform_device.h> /* 本地头文件 */ #include "my_driver.h"避免重复包含:
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);
}编译器警告
- 使用适当的编译器属性:c
__attribute__((noreturn)) void panic_handler(void) { /* 处理panic */ } __attribute__((format(printf, 1, 2))) void custom_printk(const char *fmt, ...) { /* 自定义打印函数 */ }
调试和日志
printk使用
使用适当的日志级别:
cpr_info("Device initialized successfully\n"); pr_err("Failed to allocate memory\n"); pr_debug("Debug information: value = %d\n", value);避免在生产代码中使用printk:
c#ifdef DEBUG #define dbg_print(fmt, ...) pr_debug(fmt, ##__VA_ARGS__) #else #define dbg_print(fmt, ...) #endif
最佳实践
- 保持函数简短:单个函数不应超过100行
- 避免深层嵌套:使用早期返回来减少嵌套层级
- 使用const修饰符:对不修改的数据使用const
- 初始化变量:所有变量在使用前都应初始化
- 避免魔法数字:使用命名常量代替直接的数字
遵循这些编码规范不仅有助于代码审查和维护,也能帮助你编写出更加健壮和高效的内核驱动程序。