summaryrefslogtreecommitdiff
path: root/docs/specs/00_conventions.md
blob: 47da6f1d73c561c6622cd5841d49c3973ce5f6c5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
# LumeOS 0 号规范 (Project Conventions)

本文档是 LumeOS 全项目的出厂规范。所有后续 spec 文档和代码必须遵守此规范。

---

## 1. 架构总览

### 1.1 分层模型

```
┌─────────────────────────────────────────────────────────┐
│                    User Programs                        │
│               (Busybox, init, shell)                    │
├────────────────────── syscall ABI ──────────────────────┤
│  kernel/syscall   │  kernel/proc   │  kernel/sched      │
│  (系统调用分发)    │  (任务管理)     │  (调度器)          │
├───────────────────┼────────────────┼────────────────────┤
│       fs/         │      mm/       │   kernel/trap      │
│  (VFS + 具体 FS)  │  (PMM/Slab/VMM)│  (MI 异常分发)     │
├───────────────────┴────────────────┴────────────────────┤
│                    drivers/                             │
│          (VirtIO, UART, BlockDevice 实现)                │
├─────────────────────────────────────────────────────────┤
│                   arch/$(ARCH)/                         │
│    (CSR, 上下文切换, pmap, TLB, 中断向量, uaccess)       │
├─────────────────────────────────────────────────────────┤
│                   boot/ + linker.ld                     │
│              (entry.S, 多核同步, 高半区跳转)              │
└─────────────────────────────────────────────────────────┘
```

**核心规则:** 依赖方向严格向下。上层模块可以调用下层接口,反之禁止。
`kernel/`、`fs/`、`mm/` 严禁出现内联汇编或架构特定寄存器操作。

### 1.2 多态策略

| 层级 | 多态方式 | 理由 |
|------|---------|------|
| CPU 核心层 (arch primitives) | Namespace 函数 + 编译期链接 | 零运行时开销,极热路径 |
| 设备抽象层 (BlockDevice 等) | C++ 纯虚类 | 运行时多态,驱动可替换 |
| 跨平台页表 (pmap) | Namespace 函数 + 编译期链接 | 极热路径,每次 page fault |

### 1.3 目录结构

```
lumeOS/
├── include/                # 公共头文件(跨模块可见)
│   ├── lume/               # 内核公共接口命名空间
│   │   ├── types.h         # 基础类型 (uint64, size_t, ssize_t, ...)
│   │   ├── errno.h         # 全局错误码 (-EINVAL, -ENOMEM, ...)
│   │   └── config.h        # 编译期配置 (PAGE_SIZE, NCPU, ...)
│   └── uapi/               # 用户态/内核态共享 (syscall 编号, ioctl)
│
├── arch/riscv/             # 架构相关(MD 层)
│   ├── include/arch/       # arch 私有头文件
│   ├── boot/               # entry.S, trampoline.S
│   ├── mm/                 # pmap 实现 (页表操作)
│   ├── trap/               # 中断向量, 上下文保存/恢复
│   └── uaccess.cc          # copy_from_user / exception table
│
├── mm/                     # 内存管理(MI 层)
│   ├── pmm.cc / pmm.h      # 物理页分配 (Buddy)
│   ├── slab.cc / slab.h    # 字节级分配器
│   └── vmm.cc / vmm.h      # VMA 管理, demand paging
│
├── kernel/                 # 内核核心(MI 层)
│   ├── main.cc             # kernel_main 初始化序列
│   ├── trap/               # MI 异常分发
│   ├── proc/               # TCB, 生命周期, clone/exec/exit
│   ├── sched/              # 调度器
│   ├── sync/               # Spinlock, Mutex, WaitQueue
│   └── syscall/            # syscall 跳转表与处理函数
│
├── fs/                     # 文件系统
│   ├── vfs.cc / vfs.h      # VFS 抽象层, MountTable
│   ├── file.cc / file.h    # File 基类与 FileTable
│   └── fat32/              # FatFs 胶水层
│
├── drivers/                # 设备驱动
│   ├── uart/               # 串口
│   ├── virtio/             # VirtIO Block/Net
│   └── tty/                # TTY + Line Discipline
│
├── lib/                    # 内核通用库
│   ├── kprintf.cc          # 内核格式化输出
│   ├── string.cc           # memcpy, memset, strlen, ...
│   └── rbtree.cc           # 红黑树
│
├── docs/specs/             # 模块 Spec 文档
├── linker.ld               # 链接脚本
└── Makefile                # 构建系统
```

**头文件规则:**
- 跨模块可见的接口 → `include/lume/` 或 `include/uapi/`
- 模块私有头文件 → 与 `.cc` 同目录(如 `mm/pmm.h`)
- `#pragma once` 替代 include guard

---

## 2. 统一术语表 (Ubiquitous Language)

以下术语在代码和文档中必须严格一致,不得混用同义词。

| 概念 | 术语 | 禁止混用 |
|------|------|---------|
| 物理处理器核心 | `CPU` | ~~Hart, core, processor~~ (RISC-V 文档中 Hart 仅在 arch/ 层内部使用) |
| 逻辑调度单元 | `Task` | ~~Process, Thread~~ (对用户态呈现时可说"进程/线程",内核代码统一用 Task) |
| 任务控制块 | `TaskControlBlock` / `TCB` | ~~Proc, PCB~~ |
| 物理内存页帧 | `Frame` | ~~physical page~~ |
| 虚拟页 | `Page` | 仅在虚拟地址语境使用 |
| 虚拟地址区域 | `VMA` (Virtual Memory Area) | ~~vm_area, segment~~ |
| 页表操作层 | `pmap` | ~~pagetable, mmu~~ |
| 内核地址空间 | `kernel space` | ~~supervisor space~~ (代码中 S-mode 指特权级,不指地址空间) |
| 中断请求号 | `irq` | ~~interrupt number~~ |
| 文件描述符编号 | `fd` | ~~fileno, handle~~ |
| 系统调用编号 | `sysno` | ~~syscall_nr, scall~~ |

---

## 3. 全局错误处理范式

**选定方案:Linux 负错误码(方案 A)。全局死守,不得例外。**

### 3.1 函数返回值约定

```cpp
// 返回 int 的函数:
//   成功 → 0 或正数(如 read 返回字节数)
//   失败 → 负的错误码(-EINVAL, -ENOMEM, -EFAULT, ...)
int vfs_read(File *f, char *buf, size_t n);

// 返回指针的函数(仅当失败原因唯一时允许):
//   成功 → 有效指针
//   失败 → nullptr
//   适用场景:失败原因显而易见且唯一(如"内存不足")
Frame *pmm_alloc_frame();

// 返回指针但失败原因可能有多种时 → 禁止返回裸指针。
// 必须改为返回 int 错误码 + 出参:
int task_alloc(TaskControlBlock **out);  // 成功: *out = 有效指针, 返回 0
                                         // 失败: *out 不变, 返回 -ENOMEM / -EINVAL
```

### 3.2 错误码定义

所有错误码定义在 `include/lume/errno.h`,编号严格对齐 Linux `asm-generic/errno-base.h`:

```cpp
#define EPERM   1   // Operation not permitted
#define ENOENT  2   // No such file or directory
#define ENOMEM 12   // Out of memory
#define EFAULT 14   // Bad address
#define EINVAL 22   // Invalid argument
// ...
```

### 3.3 禁止事项

- **禁止** 使用布尔值表示可能有多种失败原因的操作
- **禁止** 使用魔数(如 `-1`)代替命名错误码
- **禁止** 忽略被调用函数的返回值(必须检查或显式 `(void)` 标记)

---

## 4. C++ 命名与格式规范

### 4.1 命名约定

| 元素 | 风格 | 示例 |
|------|------|------|
| 类 / 结构体 | `PascalCase` | `TaskControlBlock`, `Spinlock` |
| 函数 / 方法 | `snake_case` | `pmm_alloc_frame()`, `task.get_pid()` |
| 局部变量 | `snake_case` | `page_count`, `vma_entry` |
| 私有成员变量 | `snake_case` + `_` 后缀 | `lock_`, `state_`, `parent_` |
| 全局变量 | `g_` 前缀 | `g_pmm`, `g_sched` |
| 编译期常量 | `k` 前缀 + `PascalCase` | `kPageSize`, `kMaxHarts` |
| 宏 / 枚举值 | `UPPER_SNAKE` | `PAGE_SIZE`, `RUNNABLE` |
| 命名空间 | `snake_case` | `namespace arch`, `namespace pmap` |
| 抽象接口类 | 直接命名,无前缀 | `BlockDevice`(不用 `IBlockDevice`) |

### 4.2 文件命名

- 源文件:`snake_case.cc`(C++)、`snake_case.S`(汇编)
- 头文件:`snake_case.h`
- 一个 `.h` + 一个 `.cc` 组成一个模块单元,文件名一致

### 4.3 格式(交给工具)

- 缩进、花括号、列宽等由 `.clang-format` 统一管控
- 代码提交前必须通过 `clang-format` 格式化

### 4.4 C++ 使用边界

| 特性 | 允许 | 禁止 |
|------|------|------|
| 类 / 继承 / 虚函数 | ✅ | |
| 模板(简单泛型) | ✅ | |
| RAII (LockGuard) | ✅ | |
| Placement new | ✅ | |
| 异常 (throw/catch) | | ❌ `-fno-exceptions` |
| RTTI (dynamic_cast) | | ❌ `-fno-rtti` |
| STL 容器 | | ❌ 依赖堆分配器 |
| 全局构造函数(隐式) | | ❌ 使用 Placement New |

---

## 5. 并发与锁约定

### 5.1 锁声明要求

每个持有锁的数据结构,必须在头文件注释中声明:
```cpp
// 锁契约:
//   持有: pmm_lock_ (内部自旋锁)
//   可能获取: 无
//   调用方禁止持有: sched_lock (避免反向依赖)
class PhysicalMemoryManager {
    Spinlock pmm_lock_;
    // ...
};
```

### 5.2 全局锁序

各 spec 文档必须声明"本模块持有哪些锁、需要获取哪些外部锁"。
Phase 2 全部完成后,从各 spec 中提取汇总为 `docs/specs/lock_ordering.md` 全局 DAG。

### 5.3 原子操作

- 引用计数(`refcount`)使用 `__atomic_fetch_sub` / `__atomic_fetch_add` + `__ATOMIC_ACQ_REL`
- **[强制]** 所有在多核间无锁共享的数据,必须使用 `lume::atomic<T>` 类型封装,并显式指定内存序。
- Spinlock 的 acquire/release 内含隐式 fence,临界区内无需额外屏障
- **禁止** 用裸 `volatile` 替代原子操作
- **禁止** 在代码中直接使用 `__atomic_*` 内建函数,必须通过 `lume::atomic<T>` 包装器。

---

## 6. Spec 文档标准模板

后续每份 spec 文档必须包含以下章节:

```markdown
# 模块名称 (Module Name)

## 1. 目标 (Objective)
一句话描述本模块的职责边界。

## 2. 前置依赖 (Dependencies)
列出本模块依赖的其他模块及其接口。

## 3. 核心数据结构 (Data Structures)
结构体定义、内存布局、字段含义。

## 4. 公共 API (Public APIs)
函数签名、参数语义、返回值约定、错误码。

## 5. 并发与锁契约 (Concurrency & Locks)
- 本模块持有的锁
- 本模块可能获取的外部锁
- 调用方的锁限制

## 6. 错误路径 (Error Paths)
每个 API 失败时的副作用(是否回滚已分配资源)。

## 7. 状态机 / 时序图 (State Machine / Sequence)
(如适用)关键流程的状态转换或调用时序。
```

---

## 7. 构建系统架构

### 7.1 工具链

| 项目 | 选择 |
|------|------|
| 构建系统 | GNU Make (非递归模式) |
| 编译器 | `riscv64-*-gcc` / `riscv64-*-g++` |
| C++ 标准 | C++23 (`-std=c++23`) |
| 关键编译选项 | `-ffreestanding -nostdlib -fno-exceptions -fno-rtti -mcmodel=medany` |
| 代码格式化 | `clang-format`(`.clang-format` 配置) |
| 静态分析 | `clang-tidy`(通过 `bear -- make` 生成 `compile_commands.json`) |
| 调试 | QEMU + GDB (`make debug`) |
| 许可证 | GPL-2.0-or-later |
| 源文件头 | 必须包含 SPDX 标识符和版权声明 |

### 7.2 构建模式:非递归 Make + 模块片段

采用**单一顶层 Makefile + 各模块 `module.mk` 片段**的非递归模式,兼顾模块化与依赖正确性。

**目录结构示例:**
```
lumeOS/
├── Makefile              # 顶层:定义规则、变量、include 所有 module.mk
├── arch/riscv/module.mk  # 声明 arch 层源文件
├── mm/module.mk          # 声明内存管理源文件
├── kernel/module.mk      # 声明内核核心源文件
├── fs/module.mk          # 声明文件系统源文件
├── drivers/module.mk     # 声明驱动源文件
├── lib/module.mk         # 声明通用库源文件
└── build/                # 编译产出目录 (out-of-tree)
```

**模块片段 (`module.mk`) 格式:**
```makefile
# mm/module.mk
# 每个模块只声明自己的源文件列表,追加到全局变量

SRCS_CC += mm/pmm.cc mm/slab.cc mm/vmm.cc
SRCS_CC += mm/cow.cc
```

**顶层 Makefile 骨架:**
```makefile
# === 变量定义 ===
ARCH    := riscv
BUILD   := build
TARGET  := $(BUILD)/kernel.elf

# === 包含所有模块片段 ===
SRCS_CC :=
SRCS_S  :=
include arch/$(ARCH)/module.mk
include mm/module.mk
include kernel/module.mk
include fs/module.mk
include drivers/module.mk
include lib/module.mk

# === 统一规则 ===
OBJS := $(patsubst %.cc,$(BUILD)/%.o,$(SRCS_CC)) \
        $(patsubst %.S,$(BUILD)/%.o,$(SRCS_S))

$(TARGET): $(OBJS) linker.ld
	$(LD) -T linker.ld -o $@ $(OBJS)

$(BUILD)/%.o: %.cc
	@mkdir -p $(dir $@)
	$(CXX) $(CXXFLAGS) -c $< -o $@

$(BUILD)/%.o: %.S
	@mkdir -p $(dir $@)
	$(CC) $(CFLAGS) -c $< -o $@

# 自动依赖追踪
-include $(OBJS:.o=.d)
```

### 7.3 构建规范

- **Out-of-tree 编译:** 所有 `.o` 和 `.d` 文件输出到 `build/` 目录,不污染源码树
- **自动依赖:** 编译时加 `-MMD -MP` 生成 `.d` 依赖文件,增量构建正确追踪头文件变更
- **新增模块流程:** 创建目录 → 创建 `module.mk` 声明源文件 → 在顶层 Makefile 一行 `include` → 完成
- **禁止递归 Make:** 不使用 `$(MAKE) -C subdir`,避免依赖追踪断裂

### 7.4 标准 Make Target

| Target | 含义 |
|--------|------|
| `make` / `make all` | 编译内核 ELF |
| `make run` | QEMU 运行(无 GDB) |
| `make debug` | QEMU 运行 + 等待 GDB 附加 |
| `make clean` | 清理 `build/` 目录 |
| `make fmt` | `clang-format -i` 格式化所有源文件 |
| `make lint` | `clang-tidy` 静态分析 |
| `make test` | QEMU 无头冒烟测试 (Phase 4) |