Skip to content

编码规范

本文档定义 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);

不要做的事

  1. 不要使用动态栈数组int arr[n];
  2. 不要递归调用:内核栈有限
  3. 不要在内核中使用浮点:未初始化 FPU
  4. 不要忽略返回值:检查所有错误
  5. 不要使用全局变量替代参数:保持函数纯度

工具支持

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

启用所有警告,将警告视为错误。