diff --git a/programing-manual/process/figures/process.png b/programing-manual/process/figures/process.png new file mode 100644 index 0000000000000000000000000000000000000000..1ab337f5ceb84393c02172973dd04620c434e209 Binary files /dev/null and b/programing-manual/process/figures/process.png differ diff --git a/programing-manual/process/figures/syscall.png b/programing-manual/process/figures/syscall.png new file mode 100644 index 0000000000000000000000000000000000000000..7a8a49f0f8037a5cf1677333db5da5b3648e3906 Binary files /dev/null and b/programing-manual/process/figures/syscall.png differ diff --git a/programing-manual/process/process.md b/programing-manual/process/process.md new file mode 100644 index 0000000000000000000000000000000000000000..2b72652ffe1a8016bfbf4386fd0381e7adbfb07e --- /dev/null +++ b/programing-manual/process/process.md @@ -0,0 +1,39 @@ +# 进程管理 + +## 一. 描述 + +进程是具有独立的线性地址空间的线程集合,是线程的容器。在现代操作系统中,硬件的 `MMU` (内存管理单元)为虚拟内存的实现奠定基础。 +线程 + `MMU` 就可以升级为进程。除此之外,还会有一些其它进程独有的资源。 + +进程执行独立的程序,程序运行出故障也不会导致系统出现故障。因此可以把一些服务程序放到用户态作为进程执行,当服务出错的时候不会导致 +内核崩溃。 + +## 二. 原理 + +进程的实现依赖于 `MMU` ,来实现进程的虚拟地址空间。每个进程都有一个 `MMU` 映射表,切换进程的时候需要修改硬件 `MMU` 寄存器,访问的地址都是新的 `MMU` 映射的虚拟地址。 + +由于切换了 `MMU` 后,访问的地址会发生变化。因此,为了能够保证访问内核的时候是正确的,在映射 `MMU` 的时候,每个进程都会把部分 +页表项映射到内核,并设置权限为内核才能访问,这样就能实现切换 `MMU` 的时候,内核也能够正常运行,新进程的地址访问也不会出错。 + +那么 `MMU` 究竟是何物?`MMU` 是虚拟地址转换成为物理地址的硬件机制。在开启 `MMU` 前,访问的地址都是物理地址, +即直接访问的是内存的地址。比如内存只有 `32MB` ,那么访问的地址就只能是在 `32MB` 以内进行数据存取。 +当开启 `MMU` 后,访问的地址就是虚拟地址了。此时访问的地址,会通过 `MMU` 转换成那 `32MB` 内的物理地址, +再去对物理地址进行存取。比如虚拟地址是 `0xF0000000` ,显然超过了 `32MB` 的内存范围,但是,只要在访问前映射 `0xF0000000` 到 + `32MB` 内的物理地址,就能正确地进行数据存取,不然,则会产生页面故障。 + +## 三、框架图 + +![进程原理](figures/process.png) + +## 四. 接口 + +创建进程的时候,需要从文件系统上加载程序的代码和数据,然后再去执行进程的代码。 +启动进程的时候,需要传入进程的名字 `name` ,要加载程序的路径 `path` ,以及进程标志 `flags`。 +```c +NX_Error NX_ProcessLaunch(char *name, char *path, NX_U32 flags); +``` + +进程执行结束的时候,需要退出,并传入退出时候的状态码。 +```c +void NX_ProcessExit(int exitCode); +``` diff --git a/programing-manual/process/syscall.md b/programing-manual/process/syscall.md new file mode 100644 index 0000000000000000000000000000000000000000..30124ca67b2a8ddb1d13e0492255caa533d61ce6 --- /dev/null +++ b/programing-manual/process/syscall.md @@ -0,0 +1,28 @@ +# 系统调用 + +## 一. 描述 + +在现代操作系统中,分为了内核态和用户态。内核态是内核程序本身,可以对设备资源进行管理核控制。而用户态只能够去调用内核提供的服务,不能够直接对资源进行访问。 +这么做的目的是为了避免用户恶意程序对系统的损坏,对设备进行恶意操作。于是用户态核与内核态之间的接口就出现了,它就是系统调用。 + +系统调用是指用户态程序通过特殊的指令陷入到内核态,执行内核态提供的服务程序,然后再返回到用户态。 + +支持特权保护的处理器架构中,都提供了模式切换的指令。至少需要有两个特权级,一个用于内核态,特权更高,可以直接访问硬件。另外一个用于用户态, +只能通过系统调用执行内核提供好的服务。在 `x86` 架构中,有4个特权级(ring0, ring1, ring2, ring3),在 `riscv` 架构中,有3个特权级(Machine, Supervisor, User)。 + +## 二. 原理 + +系统调用的实现,是基于特权级切换指令实现的,一般都是中断或者异常。用户态通过调用这些指令,就会产生一个中断或者异常,处理器会去中断表中查找系统调用指令对应的处理函数,然后就根据系统调用的 `API` 进行内核服务的分发,去调用对应的内核函数。 + +值得注意的是,在执行系统调用期间,需要打开中断,避免在系统调用时出现死循环,导致内核卡死。 + +## 三、框架图 + +![系统调用](figures/syscall.png) + +## 四. 接口 + +系统调用表是在内核固定下来的,不同的架构只要根据固定的 `API` 值,就能获取到对应的内核服务功能,然后以函数指针的方式去调用执行即可。 +```c +NX_SyscallHandler NX_SyscallGetHandler(NX_SyscallApi api); +```