进程与线程
OpenOS 的进程模型与传统 Unix 有显著不同。进程是隔离的地址空间容器,创建时为空——父进程必须显式地映射内存和启动线程。
进程是一个隔离的地址空间,包含一个或多个线程。创建时为空——父进程必须映射内存对象并显式启动线程。
进程生命周期
Section titled “进程生命周期”1. process_create(job, name) → 空地址空间2. memory_create + memory_map → 填充地址空间3. thread_create → 创建执行上下文4. process_start(proc, thread, entry, stack, arg) → 开始执行5. process_exit(status) → 终止所有线程,关闭所有 Handle6. process_wait(proc) → 父进程获取退出状态process_create
Section titled “process_create”fn process_create( job: Handle, name: *const u8, name_len: u64,) -> (proc_handle: Handle, vmar_handle: Handle)在给定的 Job 下创建一个新的、空的进程。返回进程及其根虚拟内存地址区域(VMAR)的 Handle。进程没有线程,没有映射的内存——使用 memory_create + memory_map 设置它,然后使用 process_start 开始执行。
job Handle 决定进程在进程树中的位置。当 Job 被终止时,其中的所有进程都被终止。
process_start
Section titled “process_start”fn process_start( proc: Handle, thread: Handle, entry: u64, stack: u64, arg: u64,) -> result: i64在进程中启动一个线程,从给定的入口点开始。
stack 是初始栈指针。
arg 作为第一个参数传递给入口函数。
process_exit
Section titled “process_exit”fn process_exit(status: u64) -> !终止调用进程。所有 Handle 被关闭,所有内存被取消映射,所有线程被终止。status 可通过 process_wait 由父进程获取。
process_wait
Section titled “process_wait”fn process_wait(proc: Handle, timeout_ns: u64) -> (status: i64)等待进程退出。返回退出状态。
如果 timeout_ns 为 0,如果仍在运行则立即返回 EAGAIN。
如果 timeout_ns 为 u64::MAX,无限期阻塞。
孤儿处理:每个进程属于恰好一个 Job(见下文)。如果进程退出,其子进程不会被自动终止——它们保留在同一个 Job 中。Job 本身管理生命周期。如果 Job 被终止,其中的所有进程都被终止。
Job 概念
Section titled “Job 概念”Job 是带有资源限制的进程容器。Job 形成树——每个 Job 都有一个父 Job(除了在启动时创建的根 Job)。每个进程属于恰好一个 Job。
job_create
Section titled “job_create”fn job_create(parent_job: Handle, limits: *const JobLimits) -> job_handle: Handle在父 Job 下创建一个新 Job。Job 是带有资源限制的进程容器。内核在启动时创建一个根 Job——所有进程最终都属于这棵树。
JobLimits(通过指针传递):
struct JobLimits { max_memory: u64, // 最大总内存(字节),0 = 继承父 Job max_processes: u32, // 最大进程数,0 = 继承父 Job max_threads: u32, // 最大线程数,0 = 继承父 Job}job_attach
Section titled “job_attach”fn job_attach(job: Handle, proc: Handle) -> result: i64将进程附加到 Job。进程一次只能在一个 Job 中。进程不能已经附加到不同的 Job。
job_kill
Section titled “job_kill”fn job_kill(job: Handle) -> result: i64终止 Job 中的所有进程和所有子 Job。这是批量终止机制——用于清理整个服务子树。
孤儿回收:当进程退出时,其子进程留在同一个 Job 中。如果没有人对子进程调用 process_wait,它们会一直保留到 Job 被终止。根 Job 的所有者(init 进程)负责回收孤儿进程。这是显式的——没有隐式的重新挂靠。
线程是进程内的执行上下文。线程共享进程的地址空间和 Handle 表。每个线程有自己的栈和寄存器状态。
创建时挂起——需要 process_start 才能开始执行。thread_exit 终止当前线程。如果是最后一个线程,进程以状态 0 退出。
thread_create
Section titled “thread_create”fn thread_create(proc: Handle) -> thread_handle: Handle在给定进程中创建一个新线程。线程是挂起的——使用 process_start 开始执行。
thread_exit
Section titled “thread_exit”fn thread_exit() -> !终止调用线程。如果是进程中的最后一个线程,进程以状态 0 退出。
thread_yield
Section titled “thread_yield”fn thread_yield() -> result: i64主动将 CPU 让给下一个就绪线程。
进程创建示例
Section titled “进程创建示例”// 1. 为子进程创建 Joblet child_job = job_create(parent_job, null);
// 2. 创建空进程let (proc, vmar) = process_create(child_job, "child", 5);
// 3. 加载 ELF 到子进程地址空间let code_mem = memory_create(elf_size, 0);// ... 将 ELF 数据写入 code_mem ...memory_map(code_mem, 0, elf_size, 0, MAP_READ | MAP_EXEC);
// 4. 创建栈let stack_mem = memory_create(stack_size, 0);memory_map(stack_mem, 0, stack_size, 0, MAP_READ | MAP_WRITE);
// 5. 创建线程let thread = thread_create(proc);
// 6. 启动进程process_start(proc, thread, entry_point, stack_top, 0);
// 7. 等待退出let status = process_wait(proc, u64::MAX);与 POSIX 对比
Section titled “与 POSIX 对比”| 操作 | POSIX | OpenOS |
|---|---|---|
| 创建进程 | fork() + exec() | process_create() + memory_map() + process_start() |
| 终止进程 | exit(status) | process_exit(status) |
| 等待子进程 | waitpid(pid, &status, 0) | process_wait(proc, u64::MAX) |
| 创建线程 | pthread_create() | thread_create() + process_start() |
| 终止线程 | pthread_exit() | thread_exit() |
| 让出 CPU | sched_yield() | thread_yield() |
地址空间管理
Section titled “地址空间管理”VMAR(虚拟内存地址区域)
Section titled “VMAR(虚拟内存地址区域)”VMAR 是进程虚拟地址空间中的命名区域。每个进程有一个根 VMAR(由 process_create 返回)。VMAR 将地址空间组织成逻辑区域——代码、栈、堆、共享内存。
进程地址空间:┌─────────────────────────────────────┐│ 根 VMAR ││ ┌─────────────────────────────────┐││ │ 代码区域 (R-X) │││ │ 0x400000 - 0x500000 │││ └─────────────────────────────────┘││ ┌─────────────────────────────────┐││ │ 数据区域 (RW-) │││ │ 0x500000 - 0x600000 │││ └─────────────────────────────────┘││ ┌─────────────────────────────────┐││ │ 堆区域 (RW-) │││ │ 0x600000 - 0x700000 │││ └─────────────────────────────────┘││ ┌─────────────────────────────────┐││ │ 栈区域 (RW-) │││ │ 0x7FFF00000000 - 0x800000000000│││ └─────────────────────────────────┘│└─────────────────────────────────────┘// 创建内存对象let mem = memory_create(size, 0);
// 映射到进程地址空间let vaddr = memory_map(mem, 0, size, 0, MAP_READ | MAP_WRITE);
// 取消映射memory_unmap(vaddr, size);- 每个进程有独立的地址空间
- 进程不能直接访问其他进程的内存
- 通过 Channel IPC 进行通信
// Job 资源限制struct JobLimits { max_memory: u64, // 最大内存 max_processes: u32, // 最大进程数 max_threads: u32, // 最大线程数}- Handle 权限控制对资源的访问
- 进程只能访问它持有 Handle 的资源
- 权限可以委托但只能收窄
| 功能 | 状态 | 说明 |
|---|---|---|
| 进程创建 | [plan] 计划中 | 需要完整的内存管理 |
| 线程创建 | [plan] 计划中 | 需要上下文切换 |
| Job 管理 | [plan] 计划中 | 需要进程树 |
| VMAR | [WIP] 开发中 | 基本实现 |
| 内存映射 | [WIP] 开发中 | 基本实现 |
- Handle 与 Rights - 能力系统
- Channel IPC - IPC 机制
- 安全模型 - 安全设计