diff --git a/README.md b/README.md index 3b392da39a4bc789759854bde0cccbf1aa2ab853..13fa83142023b61895b47afa6ddcad36ec6416ba 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -文档版本v0.1 基于NXOS develop分支 +文档版本 v0.1 基于 NXOS develop 分支 -如果您在文档中发现了问题或者想参与文档的编写,可以在文档仓库提出issuse或者提交pr: +如果您在文档中发现了问题或者想参与文档的编写,可以在文档仓库提出 issuse 或者提交 pr : [nxos-doc](https://gitee.com/BookOS/nxos-documentation) # NXOS 简介 @@ -11,12 +11,14 @@ ## NXOS 概述 -`NXOS`是一个分时多进程/多线程(也支持实时优先级),支持MMU的现代操作系统,它和Linux内核,WindowsNT内核是同一个数量级的。 +`NXOS`是一个分时多进程/多线程(也支持实时优先级),支持 `MMU` 的现代操作系统,它和 `Linux` 内核, `WindowsNT` 内核是同一个数量级的。 ## 许可协议 -`NXOS`采用Apache-2.0开源协议,可以自由的复制和修改代码,只需要保留版权说明即可,无潜在的商业风险。 +`NXOS`采用 `Apache-2.0` 开源协议,可以自由的复制和修改代码,只需要保留版权说明即可,无潜在的商业风险。 ## NXOS 的架构 -`NXOS`采用混合内核架构,即宏内核和微内核相结合。优势是减小内核代码,增加功能可扩展性,功能模块是独立的进程,添加和删除模块就是进程的创建和关闭。 \ No newline at end of file +`NXOS`采用混合内核架构,即宏内核和微内核相结合。优势是减小内核代码,增加功能可扩展性,功能模块是独立的进程,添加和删除模块就是进程的创建和关闭。 + +[访问文档目录](SUMMARY.md) diff --git a/SUMMARY.md b/SUMMARY.md index b4d7305cd8a8e5c8fe4696b18ecc044f1fa008af..17af6d006e356a3e0928c028713a80185e7de645 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -5,8 +5,26 @@ - [NXOS 简介](README.md) - 快速上手 - - [Qemu虚拟机](tutorial/quick-start.md) + - [虚拟平台运行](tutorial/quick-start.md) + - 内核 + - [整体框架](programing-manual/base/framework.md) + - [启动流程](programing-manual/base/start.md) + - [内存管理](programing-manual/mm/mm.md) + - [buddy伙伴算法](programing-manual/mm/buddy.md) + - [heapCache虚拟内存管理](programing-manual/mm/heap-cache.md) + - [vmspace虚拟地址空间](programing-manual/mm/vmspace.md) + - [调度管理](programing-manual/sched/sched.md) + - [线程管理](programing-manual/sched/thread.md) + - [SMP多核调度](programing-manual/sched/smp.md) + - [进程管理](programing-manual/process/process.md) + - [系统调用](programing-manual/process/syscall.md) + - [中断系统](programing-manual/irq/irq.md) + - [延迟队列](programing-manual/irq/delay_queue.md) + - [定时器](programing-manual/timer/timer.md) + - [驱动框架](programing-manual/driver/framework.md) + - [单元测试](programing-manual/test/utest.md) + - 用户接口 - [API设计](programing-manual/user/api.md) diff --git a/programing-manual/base/framework.md b/programing-manual/base/framework.md new file mode 100644 index 0000000000000000000000000000000000000000..f3c64a9296267b03cec0a860712332d1bbdb7480 --- /dev/null +++ b/programing-manual/base/framework.md @@ -0,0 +1,68 @@ +# NXOS 框架 + +## 一、混合内核框架 + +`NXOS` 采用混合内核框架,也就是结合宏内核和微内核。这样的系统有 `Windows NT` 内核、苹果 `XNU` 内核,还有国产开源系统 `RT-Thread Smart`。 + +``` ++-----------------------------------+ +| app | ++-----------------------------------+ +| (fatfs, extfs, netstack) | ++-----------------------------------+ +| nxbase | udrivers | userverce | ++-----------------------------------+ +/ syscall / ++-----------------------------------+ +| vfs | hub system | ++-----------------------------------+ +| kdrivers | kservice | ++-----------------------------------+ +| thread | vmm | irq | timer | ++-----------------------------------+ +| arch (x86, arm, riscv, ...) | ++-----------------------------------+ +| hardware | ++-----------------------------------+ +``` + +## 二、目录框架 + +### 1. 项目目录 + +| 目录 | 描述 | +| ------------- | --------------------------------------- | +| .vscode | vscode的一些配置文件,有gdb调试配置 | +| configs | 平台配置文件,配置了支持的平台 | +| docs | 内核一些功能的文档说明 | +| scripts | 用到的xbuild脚本和kconfig脚本等 | +| src | nxos 内核的源码 | + +### 2. 内核源码目录 + +| 目录 | 描述 | +| ------------- | --------------------------------------- | +| arch | 架构相关代码,x86, riscv64等 | +| drivers | 驱动程序,磁盘,输入输出设备等 | +| fs | 文件系统框架以及各种文件系统支持 | +| include | 头文件 | +| init | 内核初始化以及main核心 | +| io | I/O驱动框架和中断管理 | +| ipc | 进程间通信 | +| kernel | 内核其余模块 | +| mm | 内存管理模块 | +| platform | 平台支持,存放具体的平台,比如pc平台或者一个开发板 | +| process | 进程管理,系统调用相关内容 | +| sched | 调度模块,线程和SMP的内容,以及同步机制 | +| test | 测试模块,测试框架和测试代码 | +| time | 时间模块,时钟和定时器 | +| utils | 常用的功能组件,小工具,log显示以及字符串操作等 | + +### 3. 平台框架 + +`NXOS` 采取架构和平台分离的措施,其原因在于,一个架构可能有多个平台,分离后可以服用架构的代码。 + +| 架构 | 平台 | +| ------------- | --------------------------------------- | +| x86 | i386, pc | +| riscv64 | virt riscv64, k210, d1 | diff --git a/programing-manual/base/start.md b/programing-manual/base/start.md new file mode 100644 index 0000000000000000000000000000000000000000..d3d659d004868b24547b18fdbae414eb386ae3bf --- /dev/null +++ b/programing-manual/base/start.md @@ -0,0 +1,21 @@ +# NXOS 启动流程 + +在设备开机后,处理器会跳到一个地址去执行代码,一般都是固定的,然后再从这些固定代码跳转到程序员自己编写的程序。程序员自己写的程序就是裸机,rtos或者操作系统。 +在nxos中,不同的平台会有不同的执行地址,所以我们需要指定内核程序的入口地址,在链接的时候指定一个标签 `_Start`,当执行到这个地方的时候,内核就开始运行了。 + +## 内核执行路径 +``` +-> _Start + -> NX_Main (内核的主函数) + -> NX_SMP_Preload + -> NX_HalPlatformInit (平台初始化, 不能使用内核的功能: 平台开发者实现) + -> ShowLogVersion + -> ... 内核功能初始化 ... + -> NX_CallsInit (初始化调用) + -> NX_HalPlatformStage2 (平台初始化二阶段,可以使用内核的功能: 平台开发者实现) + + -> NX_SMP_Main (SMP初始化) + -> NX_SchedToFirstThread (调度第一个线程) + -> NX_HalPlatformMain (平台的主函数,什么都可以使用: 平台开发者实现) + +``` diff --git a/programing-manual/mm/buddy.md b/programing-manual/mm/buddy.md new file mode 100644 index 0000000000000000000000000000000000000000..dc0b771762b1f16ef823a8812f85f281a7ae489c --- /dev/null +++ b/programing-manual/mm/buddy.md @@ -0,0 +1,78 @@ +# 物理内存分配/释放管理之伙伴算法(buddy) + +## 一. 描述 + +内存是操作系统运行的根基,处理器从内存中取出指令来执行,在进入操作系统之前,一般会有引导器 `bootloader` 把内核加载到内核中,然后跳转进去执行。 +这样的话,内核也就占用了一部分的内存,那么我们应该去管理哪部分内存呢? +我们需要管理的物理内存是除了保留地址和操作系统占用以外的空闲的内存,这部分内存就是需要在操作系统运行的过程中进行静态/动态分配的内存。 +`nxos` 在 `qemu` 的 `riscv64` 虚拟平台中,内存布局如下: +```c +/** + * Physical memory layout: + * + * +------------------------+ <- MAX PHY SIZE (TOP to 4GB) + * | L3 PAGES | + * +------------------------+ <- KERNEL PAGES (TOP to 2GB + 128MB) + * | L3(K) & L0/1/2 PAGES | + * +------------------------+ <- 0x82000000 (2GB + 32MB) + * | KERNEL | + * +------------------------+ <- 0x80200000 (2GB + 2MB) + * | OPENSBI | + * +------------------------+ <- 0x80000000 (2GB) + * | MMIO/UNMAPPED | + * +------------------------+ <- 0x00000000 + */ +``` +我们需要管理的内存就是 `0x82000000-TOP` 之间的这部分内存。简单的说就是对一段或者多段空闲的内存地址进行管理,可以进行分配与释放,让操作系统去使用。 + +## 二. 原理 + +在 `nxos` 中使用 `buddy` 伙伴内存管理算法。为了便于页面的维护,将多个页面组成内存块,每个内存块都有2的方幂个页,方幂的指数被称为阶。在操作内存时,经常将这些内存块分成大小相等的两个块,分成的两个内存块被称为伙伴块,采用一位二进制数来表示它们的伙伴关系。当这个位为1,表示其中一块在使用;当这个位为0,表示两个页面块都空闲或者都在使用。系统根据该位为0或位为1来决定是否使用或者分配该页面块。系统每次分配和回收伙伴块时都要对它们的伙伴位跟1进行异或运算。所谓异或是指刚开始时,两个伙伴块都空闲,它们的伙伴位为0,如果其中一块被使用,异或后得1;如果另一块也被使用,异或后得0;如果前面一块回收了异或后得1;如果另一块也回收了异或后得0。 + +算法的具体实现不进行讲解,除非你需要来优化这部分代码,才需要了解到,如果只是去使用,那么就只需要熟悉接口即可。 + +* 优缺点 + +1. 尽管伙伴内存算法在内存碎片问题上已经做的相当出色,但是该算法中,一个很小的块往往会阻碍一个大块的合并,一个系统中,对内存块的分配,大小是随机的,一片内存中仅一个小的内存块没有释放,旁边两个大的就不能合并。 + +2. 算法中有一定的浪费现象,伙伴算法是按2的幂次方大小进行分配内存块,当然这样做是有原因的,即为了避免把大的内存块拆的太碎,更重要的是使分配和释放过程迅速。但是他也带来了不利的一面,如果所需内存大小不是2的幂次方,就会有部分页面浪费。有时还很严重。比如原来是1024个块,申请了16个块,再申请600个块就申请不到了,因为已经被分割了。 + +3. 另外拆分和合并涉及到 较多的链表和位图操作,开销还是比较大的。 + +## 三、框架图 + +![框架图](figures/buddy.png) + +## 四. 接口 + +内存管理最基本的接口就是分配和释放。为了能够支持分页机制,为页表映射提供内存的支持,将内存分为页面大小进行管理会比较合适。 +所以,分配和释放的基础单位就是页面。通常来说,页面大小是4kb,也可以更大,但是由于4kb在32位操作系统中可以很好地对4GB内存进行映射, +所以,4kb也就延续到64位系统了。所以,我们使用4kb作为一个页面的大小。 + +在初始化的时候,会创建一个 `buddy system` 来管理一段连续的物理内存。需要制定物理内存地址和物理内存大小。 +```c +NX_BuddySystem* NX_BuddyCreate(void *mem, NX_Size size); +``` + +页面分配函数,可以从一个 `buddy system` 里面分配 `count` 个页面。在MMU映射和虚拟内存管理器中会使用到。 +有必要的时候,也会在需要物理内存的时候,直接调用该分配。 +```c +void *NX_BuddyAllocPage(NX_BuddySystem* system, NX_Size count); +``` + +页面释放函数,从一个 `buddy system` 里面释放某个地址指针,该指针必须是从 `buddy` 系统中分配的页面。 +在释放页面的时候,需要引用计数为0的时候,才会去正在释放这个页面。 +```c +NX_Error NX_BuddyFreePage(NX_BuddySystem* system, void *ptr); +``` + +有的情况下,当一个页面被多次使用的时候,需要去增加其引用计数,来避免被释放掉。 +```c +NX_Error NX_BuddyIncreasePage(NX_BuddySystem* system, void *ptr); +``` + +在使用页面的过程中,可能会需要把一个地砖转换成其对应的页面结构,来访问该结构里面的成员,这在虚拟内存管理器 +实现的时候有使用到。 +```c +NX_Page* NX_PageFromPtr(NX_BuddySystem* system, void *ptr); +``` diff --git a/programing-manual/mm/figures/buddy.png b/programing-manual/mm/figures/buddy.png new file mode 100644 index 0000000000000000000000000000000000000000..341ad4973009493584388b3f6e5d57ded899fa7b Binary files /dev/null and b/programing-manual/mm/figures/buddy.png differ diff --git a/programing-manual/mm/figures/heap-cache.png b/programing-manual/mm/figures/heap-cache.png new file mode 100644 index 0000000000000000000000000000000000000000..d6a2ff64c4aea824401caa150266376fc7295e1e Binary files /dev/null and b/programing-manual/mm/figures/heap-cache.png differ diff --git a/programing-manual/mm/figures/vmspace.png b/programing-manual/mm/figures/vmspace.png new file mode 100644 index 0000000000000000000000000000000000000000..99e015ec9c867dea882b54fb609a244b96a5410d Binary files /dev/null and b/programing-manual/mm/figures/vmspace.png differ diff --git a/programing-manual/mm/heap.md b/programing-manual/mm/heap.md new file mode 100644 index 0000000000000000000000000000000000000000..d6ebe25fc0ed6cc52d991bec18dd9bea6bf2a239 --- /dev/null +++ b/programing-manual/mm/heap.md @@ -0,0 +1,45 @@ +# 内核虚拟内存管理之堆内存 + +## 一、描述 + +对于应用程序来说,可以使用 `malloc` 和 `free` 来动态地分配内存,那么,对于内核来说,也需要这样的功能。 +所以,需要在内核中实现同样的功能,在 `nxos` 里面,它们是 `NX_MemAlloc` 和 `NX_MemFree`。 + +我们是使用基于缓存的链表来管理堆,这种方式的原理是,在释放一个页面后,会放到一个链表上面,下次分配的时候,直接从链表获取内存对象即可。 + +## 二、原理 + +在 `heap cache` 算法中,每个小的对象都有一个对应的链表来管理内存对象,分配的时候先去链表上获取对象, +没有对象才重新从物理内存管理器中分配,有的话就直接从链表上面摘取。 +释放的时候,也是先释放到链表上面,当链表超出了最大链表长度阈值的时候,才会释放到物理内存分配器中。 + +在 `heap cache` 中,内存对象被划分成3类,小(16byte <= `size` <= 256kb)、中(256kb < `size` <= 1mb)、大(`size` > 1mb)内存对象。 +对于小内存对象,会划分得比较细,如:16, 32, 48, 64, 80, 96, 112, 128等。 +对于中内存对象,就是使用一个链表来管理。 +对于大内存对象,就直接从物理内存管理器分配和释放,不使用链表管理。 + +## 三、框架图 + +![框架图](figures/heap-cache.png) + +## 四、接口 + +内核分配堆内存的接口,需要传入待分配内存的大小,即可获取一个内存地址,如果为空,那么就是分配失败了。 +通常,内核会使用 `NX_MemAlloc` 去分配内存。 +```c +void *NX_HeapAlloc(NX_Size size); +#define NX_MemAlloc(size) NX_HeapAlloc(size) +``` + +内存释放堆内存的时候,需要传入内存对象地址,并返回释放的状态。 +通常,内核会使用 `NX_MemFree` 去释放内存。 +```c +NX_Error NX_HeapFree(void *object); +#define NX_MemFree(ptr) NX_HeapFree(ptr) +``` + +除此之外,还提供了一个获取某个内存对象大小的功能,传入地址即可获取内存对象所在的 `cache` 的大小。 +使用的时候需要注意,必须是通过 `NX_HeapAlloc` 分配的内存对象。 +```c +NX_Size NX_HeapGetObjectSize(void *object); +``` diff --git a/programing-manual/mm/mm.md b/programing-manual/mm/mm.md new file mode 100644 index 0000000000000000000000000000000000000000..90cbf8f0ecd66d52bd9eec7bca021a0b8c1f7c8e --- /dev/null +++ b/programing-manual/mm/mm.md @@ -0,0 +1,11 @@ +# 内存管理 + +内存管理是操作系统中最为核心和重要的部分,这是由于很多其它模块的运行都需要内存的分配和释放。 + +内存管理分为物理内存管理、虚拟内存管理、进程虚拟内存管理这三个大模块。 + +一. [物理内存管理,使用伙伴算法实现。](buddy.md) + +二. [内核虚拟堆内存管理,使用基于缓存的链表实现](heap.md) + +三. [进程虚拟内存管理,基于线性链表实现](vmspace.md) diff --git a/programing-manual/mm/vmspace.md b/programing-manual/mm/vmspace.md new file mode 100644 index 0000000000000000000000000000000000000000..6f14edb330a96dbe8d7dcdc52ff5e64d5daff72e --- /dev/null +++ b/programing-manual/mm/vmspace.md @@ -0,0 +1,58 @@ +# 进程虚拟内存管理之虚拟空间 + +## 一、描述 + +进程在启动前,需要加载对应的代码、数据到内存中,然后才开始执行。 +在进程的运行过程中也会涉及到堆的分配,栈的扩展等。 +对进程的内存管理,是对它的虚拟地址进行管理。由于会有多个进程的存在,所以会对每个进程的虚拟地址 +进行独立的管理,于是进程地址空间就出现了。 + +进程地址空间(`vmspace`)就是用来管理进程的虚拟地址的。由于不同地址会有不同的用途,于是会有虚拟地址节点(`vmnode`)来管理 +不同用法的地址。 + +## 二、原理 + +进程地址空间的管理使用链表来维护每一个节点,节点是根据虚拟地址的值来进行线性排序,低地址在链表的开头,高地址在链表的结尾。 +地址空间通过映射和解除映射来进行管理,类似于分配和释放的操作。 + +映射某段空间的时候,会先检测地址是否被使用,如果已经被使用了,就不能直接使用这块地址。没有被使用,就可以在地址空间中添加一个 +`vmnode`,节点记录了地址的信息以及这段地址的属性。 +值得注意的是,映射的时候,还会去对内存进行物理内存映射,只有映射后,进程才能访问该地址。 + +解除映射的时候,会先判断该地址是否映射,已经映射才能解除。会先解除物理内存映射,然后再把 `vmnode` 删除。 + +## 三、框架图 + +![框架图](figures/vmspace.png) + +## 四、接口 + +虚拟地址在映射的时候,需要指定对应的地址空间,然后就是传入一个想要映射的地址,如果为0,那么就会自动分配一个地址。 +以及这个地址对应的内存大小,内存节点的属性,已经一些扩展的标志。 + +在解析 `elf` 格式的可执行程序的时候,需要映射一段内存来储存代码和数据,就会用到映射功能。 +以及在做进程的堆分配和栈扩展的时候,都会使用到。 + +```c +NX_Error NX_VmspaceMap(NX_Vmspace *space, + NX_Addr addr, + NX_Size size, + NX_U32 attr, + NX_U32 flags, + void **outAddr); + +/* 指定了虚拟地址需要映射到的物理地址,映射的时候,就把虚拟地址映射到该物理地址(未来做共享内存的时候会用到) */ +NX_Error NX_VmspaceMapWithPhy(NX_Vmspace *space, + NX_Addr vaddr, + NX_Addr paddr, + NX_Size size, + NX_U32 attr, + NX_U32 flags, + void **outAddr); +``` + +解除地址映射的时候,需要制定虚拟地址和内存大小,如果在已经映射的内存空间中匹配到`vmnode`,就可以将其解除映射,并释放其 +映射的物理内存。 +```c +NX_Error NX_VmspaceUnmap(NX_Vmspace *space, NX_Addr addr, NX_Size size); +``` diff --git a/tutorial/quick-start.md b/tutorial/quick-start.md index 97e78cba9acf70df3e295a9c9d828808b5a25ff3..b04de2f5691674412ec726d29c2242070f2a74b7 100644 --- a/tutorial/quick-start.md +++ b/tutorial/quick-start.md @@ -91,7 +91,8 @@ source setup.sh # linux环境 make prepare && make defconfig && make run ``` - +* 输入 Ctrl+a x 退出 QEMU +* 输入 Ctrl+d 退出 GDB ### 2. 步骤拆解