编码规范¶
本文档定义 NoobKernel 的编码风格和最佳实践。
文件组织¶
目录结构¶
src/ # 源代码
include/ # 头文件
module/ # 模块头文件
docs/ # 文档
scripts/ # 构建脚本
文件命名¶
- 源文件:小写,下划线分隔(
kalloc.c) - 头文件:小写,下划线分隔(
spinlock.h) - 模块目录:小写,单字(
mm/,fs/)
头文件保护¶
#pragma once // 推荐
// 或传统方式
#ifndef _MODULE_FILE_H
#define _MODULE_FILE_H
...
#endif
代码风格¶
缩进¶
- 使用 4 个空格缩进(不使用 Tab)
- 函数体、代码块使用一级缩进
行宽¶
- 最大 100 字符
- 长行应适当换行
大括号¶
// K&R 风格
if (condition) {
do_something();
} else {
do_other();
}
// 函数
int foo(int arg) {
return arg + 1;
}
空格¶
// 关键字后加空格
if (x)
while (x)
for (int i = 0; i < n; i++)
// 运算符两侧加空格
x = a + b;
y = (a + b) * c;
// 函数调用不加空格
foo(a, b);
// 指针声明
int *ptr;
空行¶
- 函数之间空一行
- 逻辑块之间空一行
- 文件开头和结尾不加空行
命名约定¶
类型¶
// 结构体:小写下划线
struct page {
u32 refs;
};
// typedef:小写下划线,_t 后缀
typedef struct page page_t;
// 枚举:大写驼峰
enum ProcessState {
ProcessRunning,
ProcessSleeping,
};
// 函数指针类型:_t 后缀
typedef void (*handler_t)(int);
变量¶
// 局部变量:小写下划线
int page_count;
struct proc *current_proc;
// 全局变量:小写下划线
struct cpu cpus[CPU_NUM];
volatile bool sched_enabled;
// 静态变量:小写下划线
static int init_done;
// 常量:大写下划线
#define PAGE_SIZE 4096
#define MAX_PROCS 64
函数¶
// 函数名:小写下划线
void page_init(void);
int buddy_alloc(size_t size);
// 内部函数:可加下划线前缀
static int _find_free_block(void);
// 获取/设置函数
struct proc *get_current_proc(void);
void set_page_flags(struct page *p, u32 flags);
宏¶
// 宏:大写下划线
#define PAGE_SHIFT 12
#define MAX_ORDER 11
// 多语句宏:do-while
#define LIST_INIT(head) \
do { \
(head)->prev = (head); \
(head)->next = (head); \
} while (0)
注释¶
文件头¶
/*
* memory.c - Physical memory management
*
* This module manages physical memory pages using a buddy allocator
* for large allocations and a slab allocator for small objects.
*/
函数注释¶
/*
* buddy_alloc - Allocate contiguous memory
* @size: Requested size in bytes
*
* Returns a pointer to allocated memory, or NULL if allocation fails.
* Memory is aligned to page boundary.
*/
void *buddy_alloc(size_t size);
行内注释¶
// 初始化页表
w_satp(MAKE_SATP(kpagetable));
/*
* 刷新 TLB
* 注意:切换页表后必须刷新
*/
sfence_vma();
代码组织¶
头文件顺序¶
#include <module/header.h> // 本模块头文件
#include <other/module.h> // 其他模块
#include <misc/stdint.h> // 基础库
#include <config.h> // 配置
函数顺序¶
// 1. 静态辅助函数声明
static int helper(void);
// 2. 导出函数实现
int public_api(void) {
...
}
// 3. 静态函数实现
static int helper(void) {
...
}
变量声明¶
// 按用途分组
struct proc *p; // 进程指针
int ret; // 返回值
size_t size; // 大小
// 在需要时声明
for (int i = 0; i < n; i++) {
...
}
错误处理¶
返回值¶
// 成功返回 0,失败返回负错误码
int page_alloc(void **ptr) {
*ptr = buddy_alloc(PAGE_SIZE);
if (!*ptr) {
return -ENOMEM;
}
return 0;
}
检查返回值¶
void *ptr = kmalloc(size);
if (!ptr) {
errorf("kmalloc failed");
return -ENOMEM;
}
清理资源¶
int foo(void) {
void *a = kmalloc(SIZE);
if (!a) return -ENOMEM;
void *b = kmalloc(SIZE);
if (!b) {
kfree(a); // 清理已分配资源
return -ENOMEM;
}
// 使用 a 和 b
...
kfree(b);
kfree(a);
return 0;
}
内存管理¶
分配匹配¶
// 正确
void *p = kmalloc(256);
kfree(p);
void *q = buddy_alloc(4096);
buddy_free(q);
// 错误
void *p = kmalloc(256);
buddy_free(p); // 分配器不匹配
避免内存泄漏¶
// 分配后检查
void *p = kmalloc(size);
if (!p) return -ENOMEM;
// 确保所有路径都释放
if (error) {
kfree(p);
return error;
}
kfree(p);
return 0;
并发编程¶
锁的使用¶
// 持锁时间要短
spinlock_acquire(&lock);
counter++;
spinlock_release(&lock);
// 不要在持锁时调度
spinlock_acquire(&lock);
// sched_yield(); // 错误!可能死锁
spinlock_release(&lock);
原子操作¶
// 简单计数使用原子操作
static atomic_t count = ATOMIC_INIT(0);
atomic_inc(&count);
// 复杂操作使用锁
spinlock_acquire(&lock);
complex_operation();
spinlock_release(&lock);
调试支持¶
断言¶
#include <misc/log.h>
void foo(int *ptr) {
assert(ptr != NULL); // 检查条件
...
}
日志¶
infof("Process %d started", pid);
debugf("Allocated page at %p", page);
warnf("Low memory: %d pages", free);
errorf("Failed to open file: %s", path);
不要做的事¶
- 不要使用动态栈数组:
int arr[n]; - 不要递归调用:内核栈有限
- 不要在内核中使用浮点:未初始化 FPU
- 不要忽略返回值:检查所有错误
- 不要使用全局变量替代参数:保持函数纯度
工具支持¶
clang-format¶
项目包含 .clang-format 文件:
# 格式化单个文件
clang-format -i file.c
# 格式化所有文件
find src include -name "*.c" -o -name "*.h" | xargs clang-format -i
编译警告¶
C_FLAGS := -Wall -Werror -Wno-unused-variable
启用所有警告,将警告视为错误。