首页 关于
树枝想去撕裂天空 / 却只戳了几个微小的窟窿 / 它透出天外的光亮 / 人们把它叫做月亮和星星

一个嵌入式操作系统的实现过程

在学生时代,操作系统对于我而言是一个很神秘的东西,虽然从书本中了解了一些操作系统的基本原理, 但总缺少一些直观的认识。硕士毕业那年,机缘巧合地看到了于渊写的《Orange'S一个操作系统的实现》这本奇书, 得以管窥一二。感觉写一个操作系统应该是一件很酷的事情,而且也不是那么难,只要对计算机结构有清晰的认识, 就应该没有问题。

但是,X86这个架构对于我而言有点复杂了,我需要花费很多精力去了解各种各样的历史原因, 有点麻烦。相比之下,我更喜欢开放的ARM架构,这样我可以自由的把控各个系统外设,必要时可以自定义,是一个很不错的起点。 于是,决定在一款ARM芯片上写一个嵌入式的操作系统。

本系列文章是建立在《STM32入门指南》基础之上的, 一开始选用的开发板是淘宝上买的银杏科技的iCore3,后来切换到了正点原子的探索者。 假设读者对于STM32的单片机有一定的了解。 源码托管在了Github上。

原先iCore3开发板的代码托管在这里

第一部分:Cortex-M4简介

对处理器内核的深入理解,是我们实现上下文切换,进程调度,内核态与用户态权限分离的基础。了解处理器的中断处理机制,内存和外设的访问方式,有助于实现高效的设备驱动,和内存系统。 为了查阅方便,也为了本系列文章内容的完整性,在正式开始操作系统之旅之前,我们要先对要用的处理器内核做一个简单的介绍。 初次阅读的读者可以先跳过这一部分,在以后需要用到的时候,通过超链接回来查阅。

我们所用的单片机STM32F407的内核是ARM的Cortex-M4处理器。在ARM的信息网站上可以找到各个内核的文档。 此外,还有《M3M4权威指南》《STM32的Cortex-M4编程手册》可供参考。

Cortex-M4内核结构 Cortex-M4处理器是ARMv7-M架构的一种实现,它是一种32位精简指令集(Reduced Instruction Set Computing, RISC)的处理器,有一个三阶的指令流水线,依次对指令进行取指、 解码、执行操作。它是一种哈佛架构,可以同时获取指令和数据,使用32位的地址总线,可以访问4G的地址空间。而且它采用的是一种装载存储结构, 不能直接操作内存空间里的数据,需要先把内存空间中的数据装载到内核寄存器中,处理完毕之后再存储到内存中。
内核寄存器 如果把处理器的内核看作是一个黑箱,那么内核寄存器就是这个黑箱的接口。我们通过这个接口来获取内核的状态,控制内核的工作方式。
一般我们使用通用寄存器(R0-R12)处理数据。功能寄存器(R13-R15)则是栈指针(SP)、链接器(LR)和程序计数器(PC)。内核还有反应其工作状态的状态寄存器PSR, 屏蔽中断用的寄存器(PRIMASK、FAULTMASK、BASEPRI),以及系统控制寄存器(CONTROL)。

第二部分:进程

早期的计算机只有一个CPU,这就意味着同一时间只能执行一个任务。但我们在使用的时候,仍然会产生一种计算机在同时处理很多任务的错觉。 这依赖于“进程”这一概念的提出和实现,对计算机的资源进行分时复用,CPU在多个任务之间不断的切换,当切换的频率足够快就会给用户一种同时处理多个任务的假象。

上下文切换控制三色灯 所谓的上下文切换,是指CPU在进程之间进行切换的这一过程。它涉及到两个动作:保存上文和恢复下文。 保存上文是把当前进程的状态保存下来,主要是CPU中的各个寄存器,使得在以后切换回该进程时能够完全恢复其当前的工作状态。 恢复下文则是恢复之前保存下来的目标进程的状态,继续执行目标进程。 代码下载
具有上下文切换的任务框架 麻雀虽小,五脏俱全。虽然这只是一个简单的上下文切换机制,还算不上一个完整的操作系统,但我们已经可以看到操作系统的影子了。 这一功能将在未来被完善成为一个完整的进程机制。代码下载
定时触发的上下文切换 在前两篇文章1, 2中,我们实现了一个简单的上下文切换机制, 但要求应用程序主动触发切换。很多时候,人们写应用程序是不会考虑什么时候、以及如何进行上下文切换。 但这些应用程序却能很好的工作,这要归功于操作系统中的时间片机制。 本文用Cortex-M4内核的SysTick计时器实现一个简单的定时切换的功能,使得任务的实现不需要主动的触发上下文切换。 这一功能将在未来被完善为时间片机制。代码下载
进程队列 在先前的框架中,我们只是单纯的在两个进程之间来回切换, 并没做过多的进程调度的工作。这个框架下的进程调度需要为每个进程添加一个调用规则,过于繁琐。
为了方便管理进程,本文在Linux内核链表的基础上提供了一个队列,称之为进程队列。 代码下载
系统时钟 现在实现一个简单的系统时钟,是因为框架中用的延时函数实在是太低效了。
实际上,系统时钟对于操作系统而言是一个重要的组件。它存在的意义不仅仅是要提供一个延时功能,还应当能够记录系统的运行时间, 甚至是每个进程的运行时间,为我们通过CPU占用率进行优化提供了可能。在我们要实现的时间片机制中,系统时钟的计时功能是必不可少的一个组件。 代码下载
临界区 在写系统进程时,需要时刻注意着多个进程对公共资源的并发访问问题。临界区是保护公共资源的一种方式, 我们通过关闭系统中断来实现临界区,进而保证了临界区中的代码能够完整的执行,也就是一些参考书中所说的原子性
在本文中,我们分析了μCOS的临界区实现方式,对其作了简单的修改。用汇编写了xtos_lock()和xtos_unlock()两个成对使用的函数, 用于进入和退出系统临界区。 代码下载
进程的工作状态 上下文切换需要一系列的入栈和出栈操作,这是很耗费计算资源的一个过程。但为了保证系统的"并行"运行效果, 我们还需要不断的进行切换。为了提高系统的运行效率,尽量避免不必要的进程切换,需要为进程实现一个状态机。
一般操作系统都会定义就绪态、运行态和挂起态三种状态,不同的系统的具体实现可能略有不同。我们这里参考μC/OS和Linux的实现, 初步设计了进程的工作状态机。

进程间通信

守护进程

第三部分:内存管理

暂无介绍。

第四部分:外设驱动

暂无介绍。

第五部分:文件系统

所有的计算机应用都需要存储一些信息。一个进程在运行的过程中,它可以在自己的地址空间中存储有限的信息。 但是,存储空间大小受限于虚拟地址空间(virtual address space)。对于很多应用这个地址空间是很小的。 把信息存储在进程空间中的另一个问题就是,当进程结束或者系统掉电以后,这些信息就会丢失。 另外,很多时候会有多个进程需要访问相同的信息,因此有必要对这些信息进程抽象,使其与进程无关。

为了防止掉电丢失,人们通常会把数据保存在硬盘、FLASH等掉电不会丢失的设备上, 这里我们用的是SD存储卡保存信息。 这类设备的容量通常都很大,就是速度慢。为了提高性能和访问速度,这些存储设备通常是按照数据块的形式进行访问的。 也就是说一次读写操作会处理很多字节的数据,一个数据块的字节数通常是固定的。

文件系统就是对存储数据的抽象,各个进程都以文件的形式访问各种不同的信息。 进程通过文件系统打开文件、读写文件,根本不需要考虑文件存储在什么设备上。这样就把数据和进程之间独立开来了。 主要解决了三个问题:

  1. 数据存储在哪里,是怎么组织的?
  2. 新数据应该以什么形式、写在哪个地方?
  3. 一个进程能够访问什么样的数据?

文件系统原理 在本文中,我们将对文件系统的功能和实现做简单的原理介绍。文件系统的设计和使用是有着很高深的学问的,我们会在以后的文章中逐渐深入。
FAT文件系统 最早FAT文件系统是上个世纪七八十年代微软为了驱动软盘(Floppy Disk)在MS-DOS操作系统上开发的文件系统,现在的Windows操作系统主要用NTFS格式的文件系统。 不过在MP3/MP4还很火的时候,貌似FAT又得到了广泛的应用,现如今的一些嵌入式系统上仍然能够看到FAT的身影。 相比于其它文件系统,它还是比较简单直白的一种,所以我们以FAT为例进行分析,对上一篇文章中介绍的原理进行一些补充。
FatFs —— 一个FAT文件系统的开源实现 暂未写介绍

参考资源

  1. 《M3M4权威指南》
  2. STM32的Cortex-M4编程手册(PM0214)
  3. STM32F407参考手册Reference Manual(RM0090)
  4. STM32F407数据手册Datasheet
  5. iCore3电路原理图
  6. Modern Operating Systems, 3rd
  7. Operating Systems Design and Implementation, 3rd
  8. Linux Kernel Development. 3rd.

用间第十三

孙子曰:凡兴师十万,出征千里,百姓之费,公家之奉,日费千金,内外骚动,怠于道路,不得操事者,七十万家。相守数年,以争一日之胜,而爱爵禄百金, 不知敌之情者,不仁之至也,非民之将也,非主之佐也,非胜之主也。故明君贤将所以动而胜人,成功出于众者,先知也。先知者,不可取于鬼神,不可象于事,不可验于度, 必取于人,知敌之情者也。

故用间有五:有乡间,有内间,有反间,有死间,有生间。五间俱起,莫知其道,是谓神纪,人均之宝也。乡间者,因其乡人而用之;内间者,因其官人而用之; 反间者,因其敌间而用之;死间者,为诳事于外,令吾间知之而传于敌间也;生间者,反报也。故三军之事,莫亲于间,赏莫厚于间,事莫密于间,非圣贤不能用间, 非仁义不能使间,非微妙不得得间之实。微哉微哉!无所不用间也。间事未发而先闻者,间与所告者皆死。凡军之所欲击,城之所欲攻,人之所欲杀,必先知其守将、左右、 谒者、门者、舍人之姓名,令吾间必索知之。敌间之来间我者,因而利之,导而舍之,故反间可得而用也;因是而知之,故乡间、内间可得而使也;因是而知之,故死间为诳事, 可使告敌;因是而知之,故生间可使如期。无间之事,主必知之,知之必在于反间,故反间不可不厚也。

昔殷之兴也,伊挚在夏;周之兴也,吕牙在殷。故明君贤将,能以上智为间者,必成大功。此兵之要,三军所恃而动也。




Copyright @ 高乙超. All Rights Reserved. 京ICP备16033081号-1