跳转到内容

进程与线程

OpenOS 的进程模型与传统 Unix 有显著不同。进程是隔离的地址空间容器,创建时为空——父进程必须显式地映射内存和启动线程。

进程是一个隔离的地址空间,包含一个或多个线程。创建时为空——父进程必须映射内存对象并显式启动线程。

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) → 终止所有线程,关闭所有 Handle
6. process_wait(proc) → 父进程获取退出状态
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 被终止时,其中的所有进程都被终止。

fn process_start(
proc: Handle,
thread: Handle,
entry: u64,
stack: u64,
arg: u64,
) -> result: i64

在进程中启动一个线程,从给定的入口点开始。 stack 是初始栈指针。 arg 作为第一个参数传递给入口函数。

fn process_exit(status: u64) -> !

终止调用进程。所有 Handle 被关闭,所有内存被取消映射,所有线程被终止。status 可通过 process_wait 由父进程获取。

fn process_wait(proc: Handle, timeout_ns: u64) -> (status: i64)

等待进程退出。返回退出状态。 如果 timeout_ns 为 0,如果仍在运行则立即返回 EAGAIN。 如果 timeout_nsu64::MAX,无限期阻塞。

孤儿处理:每个进程属于恰好一个 Job(见下文)。如果进程退出,其子进程不会被自动终止——它们保留在同一个 Job 中。Job 本身管理生命周期。如果 Job 被终止,其中的所有进程都被终止。

Job 是带有资源限制的进程容器。Job 形成——每个 Job 都有一个父 Job(除了在启动时创建的根 Job)。每个进程属于恰好一个 Job。

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
}
fn job_attach(job: Handle, proc: Handle) -> result: i64

将进程附加到 Job。进程一次只能在一个 Job 中。进程不能已经附加到不同的 Job。

fn job_kill(job: Handle) -> result: i64

终止 Job 中的所有进程和所有子 Job。这是批量终止机制——用于清理整个服务子树。

孤儿回收:当进程退出时,其子进程留在同一个 Job 中。如果没有人对子进程调用 process_wait,它们会一直保留到 Job 被终止。根 Job 的所有者(init 进程)负责回收孤儿进程。这是显式的——没有隐式的重新挂靠。

线程是进程内的执行上下文。线程共享进程的地址空间和 Handle 表。每个线程有自己的栈和寄存器状态。

创建时挂起——需要 process_start 才能开始执行。thread_exit 终止当前线程。如果是最后一个线程,进程以状态 0 退出。

fn thread_create(proc: Handle) -> thread_handle: Handle

在给定进程中创建一个新线程。线程是挂起的——使用 process_start 开始执行。

fn thread_exit() -> !

终止调用线程。如果是进程中的最后一个线程,进程以状态 0 退出。

fn thread_yield() -> result: i64

主动将 CPU 让给下一个就绪线程。

// 1. 为子进程创建 Job
let 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);
操作POSIXOpenOS
创建进程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()
让出 CPUsched_yield()thread_yield()

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] 开发中基本实现