logo资料库

自己动手写嵌入式操作系统(文字版).pdf

第1页 / 共699页
第2页 / 共699页
第3页 / 共699页
第4页 / 共699页
第5页 / 共699页
第6页 / 共699页
第7页 / 共699页
第8页 / 共699页
资料共699页,剩余部分请下载后查看
第一章 概 述
1.1 嵌入式系统概述
1.2 嵌入式操作系统概述
1.2.1 嵌入式操作系统特点
1.可裁剪性
2.与应用代码一起连接
3.可移植性
4.可扩展性
1.2.2 嵌入式操作系统与通用操作系统的区别
1.地址空间上的区别
2.内存管理上的区别
3.应用方式上的区别
1.2.3 嵌入式实时操作系统
1.3 操作系统的基本概念
1.3.1 微内核与大内核
1.3.2 进程、线程与任务
1.3.3 可抢占与不可抢占
1.3.4 同步机制
1.事件(Event)
2.信号量(Semaphore)
3.互斥体(Mutex)
4.内核线程对象(KernelThreadObject)
5.睡眠
6.定时器
1.4 Hello China概述
1.4.1 Hello China的功能特点
1.4.2 Hello China的开发环境
开发环境的搭建
1.创建一个DLL工程:
2.设置项目编译与连接选项:
1.4.3 面向对象思想的模拟
1.使用结构体定义实现对象
2.使用宏定义实现继承
3.使用强制类型转换实现动态类型
1.4.4 对象机制
1.4.5 Hello China V1.0版本的源文件构成
1.4.6 Hello China V1.5版本的源文件构成
1.4.7 Hello China的使用
1.5 实例:一个简单的IP路由器的实现
1.5.1 概述
1.5.2 路由器的硬件结构
1.5.3 路由器的软件功能
1.5.4 各任务的实现
第二章Hello China的加载和初始化
2.1 常见嵌入式系统的启动
2.1.1 典型嵌入式系统内存映射布局
2.1.2 嵌入式系统的启动概述
2.1.3 常见嵌入式操作系统的加载方式
2.1.4 嵌入式系统软件的写入
2.2 Hello China在PC机上的启动
2.2.1 PC机启动过程概述
2.2.2 Hello China的引导过程
2.2.3 实地址模式下的初始化
2.2.4 保护模式下的初始化
2.2.5 操作系统核心功能的初始化
第三章Hello China的SHELL
3.1 Hello China的SHELL
3.1.1 Shell的启动和初始化
3.1.2 Shell的消息处理过程
3.1.3 内部命令的处理过程
3.1.4 外部命令的处理过程
第四章Hello China的线程
4.1 线程概述
4.1.1 进程、线程和任务
4.2 Hello China V1.0版本的线程实现
4.2.1 核心线程管理对象
4.2.2 线程的状态及其切换
4.2.3 核心线程对象
4.2.4 线程的上下文
4.2.5 线程的优先级与调度
4.2.6 线程的创建
4.2.7 线程的结束
4.2.8 线程的消息队列
4.2.9 线程的切换——中断上下文
4.2.10 线程的切换——系统调用上下文
4.2.11 上下文保存和切换的底层函数
4.2.12 线程的睡眠与唤醒
4.3 V1.5版本中核心线程的实现
4.3.1 概述
4.3.2 核心线程调度时机
4.4 V1.5核心线程管理器(KernelThreadManager)的实现
4.4.1 V1.5核心线程队列的实现
4.5 V1.5核心线程对象(KernelThreadObject)的实现
4.5.1 V1.5版本中硬件上下文的保存
4.5.2 线程的调度-中断上下文
KERNEL_THREAD_STATUS_SUSPENDED:
KERNEL_THREAD_STATUS_SLEEPING:
KERNEL_THREAD_STATUS_TERMINAL:
KERNEL_THREAD_STATUS_BLOCKED:
4.5.3 线程的调度-程序上下文
KERNEL_THREAD_STATUS_RUNNING:
KERNEL_THREAD_STATUS_READY:
KERNEL_THREAD_STATUS_SUSPENDED:
KERNEL_THREAD_STATUS_SLEEPING:
KERNEL_THREAD_STATUS_TERMINAL:
核心线程的创建和初始化
中断处理程序结束后的线程调度
第五章 内存管理机制
5.1 内存管理机制概述
5.2 IA32 CPU内存管理机制
5.2.1 IA32 CPU内存管理机制概述
5.2.2 几个重要的概念
5.2.3 分段机制的应用
5.2.3.1 基本平展段模式
5.2.3.2 保护平展段模式
5.2.3.3 多段模式
5.2.4 分页机制的应用
分页机制概述
操作系统核心的保护
虚拟内存的实现
按需内存分配
代码共享机制
部分装入机制
Power PC CPU的内存管理机制
Hello China内存管理模型
Hello China的内存管理模型
Hello China的内存布局
核心内存池的管理
页框管理对象(PageFrameManager)
页面索引对象
Initialize
Uninitialize
GetPhysicalAddress
ReservePage:
SetPageFlag
ReleasePage
页索引管理器的应用
虚拟内存管理对象(VirtualMemoryMgr)
虚拟区域
虚拟内存管理器(Virtual Memory Manager)
Initialize
Uninitialize
VirtualAlloc
VirtualFree
GetPdeAddress
第六章线程本地堆的实现
6.1 Heap概述
6.2 堆的功能需求定义
6.3 堆的实现概要
6.4 堆的详细实现
6.4.1 堆的创建
6.4.2 堆的销毁
6.4.3 堆内存申请
6.4.4 堆内存释放
6.4.5 malloc和free的实现
第七章 互斥和同步机制的实现
7.1 互斥和同步概述
7.2 关键区段概述
7.3 关键区段产生的原因
7.3.1 多个线程之间的竞争
7.3.2 中断服务程序与线程之间的竞争
7.3.3 多个CPU之间的竞争
单CPU下关键区段的实现
多CPU下关键区段的实现
多CPU环境下的实现方式
Hello China的未来实现
Power PC下关键区段的实现
Power PC提供的互斥访问机制
多CPU环境下的互斥机制
关键区段使用注意事项
Semaphore概述
Semaphore对象的定义
Semaphore对象的实现
Initialize和Uninitialize实现
WaitForThisObject的实现
WaitForThisObjectEx的实现
ReleaseSemaphore的实现
中断和异常概述
硬件相关部分处理
IA32中断处理过程
IDT初始化
硬件无关部分处理
系统对象和中断对象
中断调度过程
缺省中断处理函数
对外服务接口
几个注意事项
Power PC的异常处理机制
PPC 异常处理机制概述
Power PC异常的分类
异常的处理和返回
定时器概述
SetTimer调用
CancelTimer调用
ResetTimer调用
设置定时器操作
定时器超时处理
定时器取消处理
定时器复位
定时器注意事项
第八章 中断和定时处理机制的实现
8.1 中断和异常概述
8.2 硬件相关部分处理
8.2.1 IA32中断处理过程
8.2.2 IDT初始化
8.3 硬件无关部分处理
8.3.1 系统对象和中断对象
8.3.2 中断调度过程
8.3.3 缺省中断处理函数
8.4 对外服务接口
8.5 几个注意事项
8.6 Power PC的异常处理机制
8.6.1 PPC 异常处理机制概述
Power PC异常的分类
异常的处理和返回
定时器概述
SetTimer调用
CancelTimer调用
ResetTimer调用
设置定时器操作
定时器超时处理
定时器取消处理
定时器复位
定时器注意事项
第九章 系统总线管理
9.1 系统总线概述
9.1.1 系统总线
9.1.2 总线管理模型
9.1.3 设备标识符
9.2 系统资源管理
9.2.1 资源描述对象
9.2.3 IO端口资源管理
9.3 驱动程序接口
9.3.1 GetResource
9.3.2 GetDevice
9.3.3 CheckPortRegion
9.3.4 ReservePortRegion
9.3.5 ReleasePortRegion
9.3.6 AppendDevice
9.3.7 DeleteDevice
9.4 PCI总线驱动程序概述
9.4.1 PCI总线概述
9.4.2 PCI设备的配置空间
9.4.3 配置空间关键字段的说明
9.4.3.1 Device ID和Vendor ID
9.4.3.2 Class code
9.4.3.3 Header type
9.4.3.4 Base Address Register
Interrupt Line和Interrupt Pin
Primary、Secondary和Subordinate总线号
IO Base和IO Limit
Memory base和Memory Limit
Prefetch memory base和Prefetch memory limit
PCI配置空间的读取与设置
PCI总线驱动程序的实现
探测PCI总线是否存在
对普通PCI设备进行枚举
配置PCI桥接设备
第十章 驱动程序管理框架
10.1 设备驱动程序管理框架
10.1.1 概述
10.1.2 设备管理器和IO管理器
10.1.2.1 通用的设备管理机制
10.1.2.2 Hello China的设备管理机制
10.1.2.2.1设备管理器(DeviceManager)
10.1.2.2.2 I/O管理器(IOManager)
10.1.3 Hello China的设备管理框架
10.1.4 I/O管理器(I/OManager)
10.1.4.1驱动程序对象和设备对象
10.1.4.2 IOManager对设备对象和设备驱动程序的管理
10.1.4.3 IOManager的实现框架
10.1.4.4 初始化函数(Initialize)
10.1.4.5 IOManager对应用的接口
10.1.4.6 CreateFile的实现
10.1.4.7 ReadFile的实现
10.1.4.8 WriteFile的实现
10.1.4.9 CloseFile的实现
10.1.4.10 IOControl的实现
10.1.4.11 SetFilePointer的实现
10.1.4.12 FlushFile的实现
10.1.4.13IOManager对设备驱动程序的接口
10.1.4.14 驱动程序入口(DriverEntry)
10.1.4.15 设备驱动程序的卸载
10.2 文件系统的实现
10.2.1 文件系统与文件的命名
10.2.3 文件系统驱动程序
10.2.4 打开一个文件的操作流程
10.3 设备驱动程序框架
10.3.1 设备请求控制块(DRCB)
10.3.2 设备驱动程序的文件组织结构
10.3.3 设备驱动程序的功能实现
10.3.3.1 读操作(DeviceRead)的实现
10.3.3.2 写操作(DeviceWrite)的实现
10.3.3.3 设备控制(DeviceCtrl)的实现
10.3.4 设备驱动程序对象
10.3.5 DriverEntry的实现
10.3.6 UnloadEntry的实现
10.4 设备对象
10.4.1 设备对象的定义
10.4.2 设备对象的命名
10.4.3 设备对象的类型
10.4.4 设备对象的设备扩展
10.4.5 设备的打开操作
设备命名策略
采用全球唯一标识符来命名设备
采用网络接口卡硬件地址命名设备
10.5 设备的中断管理
第十一章 应用编程接口
11.1 核心线程操作接口
CreateKernelThread
DestroyKernelThread
SendMessage
GetMessage
SetKernelThreadPriority
GetKernelThreadPriority
GetKernelThreadID
内存操作接口
KMemAlloc
KMemFree
VirtualAlloc
VirtualFree
malloc
free
CreateHeap
DestroyHeap
HeapAlloc
HeapFree
定时器操作接口
SetTimer
CancelTimer
核心线程同步操作接口
Sleep
CreateMutex
ReleaseMutex
DestroyMutex
CreateEvent
SetEvent
ResetEvent
DestroyEvent
WaitForThisObject
WaitForThisObjectEx
系统中断操作接口
ConnectInterrupt
DisconnectInterrupt
输入/输出(IO)接口
CreateFile
ReadFile
WriteFile
IoControl
SetFilePointer
FlushFile
CloseFile
设备驱动程序接口
CreateDevice
DestroyDevice
相关辅助功能接口
StrLen
StrCpy
MemZero
MemCpy
PC服务接口
PrintLine
PrintChar
ChangeLine
GotoHome
第十二章 Hello China的应用开发方法
12.1 Hello China的开发方法概述
12.2 在Hello China基础上开发一个简单应用程序
附录A 串口交互程序及其实现
串行通信接口概述
串行通信编程方式
串口初始化
数据发送
基于轮询方式的数据发送
基于中断方式的数据发送
数据接收
基于轮询方式的数据接收
基于中断方式的数据接收
串口交互程序的实现
串口交互程序的使用
通过PC机的串口,控制特定功能的设备。
通过串口连接两台计算机,实现点对点通信
直连串口线的制作方法
轮询模式的串口交互程序实现
中断模式的串口交互程序实现
串行通信编程总结
轮询方式和中断方式编程的对比
串口交互程序的其它实现方式
附录B 核心线程CPU占用率统计功能
CPU占用率概述
核心线程CPU占用率统计的实现
统计周期和统计算法
核心线程统计对象
核心线程创建回调函数
核心线程调入CPU回调函数
核心线程调出CPU回调函数
核心线程结束回调函数
CPU统计对象
CPU统计对象的定义
CPU统计对象的初始化
核心线程CPU占用率统计
CPU占用率统计线程
进程和多CPU情况下的考虑
进程的用户态和核心态执行时间统计
多CPU环境下的考虑
附录C 系统核心HOOK机制的实现
Hook概述
线程Hook的实现
线程Hook的实现概述
线程调度前后的回调机制
ScheduleFromProc函数中的回调
ScheduleFromInt函数中的回调
线程创建和结束的回调机制
CallThreadHook例程的实现
线程Hook的应用
附录D 如何搭建一个基于Windows的操作系统开发平台
总体概述
Windows RAD开发工具概述
常用的Windows RAD
Microsoft Visual C++
Borland Delphi
JBuilder等Java开发工具
Borland C++
Windows RAD工具总结
Windows RAD工具不能直接用于OS开发的原因
缺省情况下,生成的目标文件的入口地址固定
缺省情况下,生成的目标文件的加载地址固定
RAD生成的目标文件,增加了一个特定文件头
目标模块加载到内存后,需要经过处理才能运行
RAD开发工具开发OS注意事项
避免调用任何Windows操作系统提供的系统调用
避免任何C运行期函数调用
避免使用C或C++提供的异常处理机制
使用Microsoft Visual C++搭建一个OS开发环境
操作系统开发中常用的Microsoft Visual C++特性
内嵌汇编代码
__declspec(naked)函数修饰
如何搭建一个OS映象文件开发环境
创建一个Windows DLL工程
设置项目编译与连接选项
对目标文件进行处理
映象文件的加载与运行
简单OS开发示例
创建一个名字为OSIMG_1的DLL工程
添加一个源程序文件,并编辑实现代码
设置编译连接选项,并进行编译连接
处理目标文件
创建引导磁盘
如何快速掌握汇编语言
掌握汇编语言的益处
可以充分利用在操作系统等系统软件开发中
可以充分理解高级语言特性
可以帮助程序员进行程序调试,寻找程序中的bug
熟练掌握汇编语言的困难所在
汇编语言没有建立起方便的输入/输出方法
汇编语言编译/连接复杂,开发工具难以使用
一种快速掌握汇编语言的方法
附录E 一种代码执行时间测量方法的实现
概述
执行时间计算方法
实现方式
对外接口
IA32硬件平台下的实现
误差分析
Hello China系统时钟中断测试结果
附录F 64bit整型数据类型的实现
概述
加法
__U64的加法
__I64的加法
减法
__U64的减法
__I64的减法
比较
__U64的比较
__I64的比较
移位
__U64的移位
__I64的移位
后续支持
附录G IOCTRL控制程序使用介绍及实例
概述
使用方式
Inputb
Inputw
Inputd
Outputb
Outputw
Outputd
Memwb
Memww
Memwd
Memrb
Memrw
Memrd
Help和exit
一个读取PCI设备配置空间的例子
PCI配置空间读取方法
PCI配置空间布局
PCI总线设备位置获取方法
一个读取PCI配置信息的例子
附录H 优先队列(Priority Queue)和环形缓冲区(RING BUFFER)的实现
优先队列概述
优先队列对象的定义和对外接口
中断安全和多CPU支持
优先队列的实现
InsertIntoQueue的实现
DeleteFromQueue的实现
GetHeaderElement的实现
初始化和销毁函数的实现
队列接口函数的复杂度分析
环形缓冲区概述
环形缓冲区的实现
初始化函数的实现
GetElement函数的实现
AddElement函数的实现
2 自己动手写嵌入式操作系统 1 第一章 概 述 1.1 嵌入式系统概述 当今时代,人们的生活越来越依赖基于计算机技术和数据通信技术的电子产品,因 此,有人说,当今时代是电子产品时代;也有人说,当今时代是互联网时代;还有人说, 当今时代是 e 时代。这些都充分说明了电子产品和互联网技术给人们的生活带来的改变。 但这些说法都有些偏颇,一个更接近本质的说法是“当今时代,是嵌入式系统时代”。 嵌入式系统可以简单地理解为“为完成一项功能而开发的、由具有特定功能的硬件 和软件组成的一个应用产品或系统”。嵌入式系统在我们的生活中到处可见,例如,手机、 PDA、家里的数字电视机、全自动洗衣机等,都是嵌入式系统。当然,在我们日常生活 接触不到的领域中,嵌入式系统也被广泛应用。例如,应用于通信网络中的电话交换机、 光传输分叉/复用设备、互联网路由器等,都是嵌入式系统的实例。这些实例都有一个共 同的特点,那就是“具备特定的用途”。比如,手机只能用于完成移动通信(移动通话、 移动短信息等),而不具备数字电视的功能,同样地,数字电视只具备数字电视信号接收、 解码和播放功能,以及相关的一些简单附加功能,而不具备洗衣机的功能,等等。因此, 嵌入式系统一个最基本的特点,就是“功能专一”。 一般情况下,嵌入式系统是由嵌入式硬件和嵌入式软件两部分组成的。嵌入式硬件, 是由完成嵌入式系统功能所需要的机械装置、数字芯片、光/电转换装置等组成,嵌入式 硬件决定了嵌入式系统的功能集合,即嵌入式系统的最终功能。嵌入式软件则是附加在 嵌入式硬件之上的,驱动嵌入式硬件完成特定功能的逻辑指令。嵌入式软件可以非常简 单,比如,在一些简单的自动控制洗衣机中,软件部分可能只有数百行汇编代码,系统 功能基本上由硬件完成,软件仅仅起到辅助功能。嵌入式软件也可以非常复杂,比如, 手机、大型通信设备等嵌入式系统,软件部分往往由数十万行,甚至数百万行代码组成, 这些系统的大部分功能都是由软件逻辑实现的。通过分析这些嵌入式系统,可以发现一 个规律,那就是嵌入式软件所占比重越高的嵌入式系统,其灵活性越好,功能也越强大, 这很容易理解,因为软件比重大的系统中,大部分功能是由软件完成的,通过迭加更多 的软件,就可以实现更多的功能。相反,若一种嵌入式系统由硬件占主导地位,则在这 种系统上增加新的功能或配置将非常不方便,因为需要更换硬件。 对于嵌入式系统的软件,可以进一步分为嵌入式操作系统和嵌入式应用软件。其中, 嵌入式操作系统是系统软件,是直接接触硬件的一层软件,嵌入式操作系统为应用软件 提供了一个统一的接口,屏蔽了不同硬件之间的差别,使得应用软件的开发和调试变得
3 十分方便。嵌入式应用软件则是真正完成系统功能的软件。当然,这两种软件并不是所 有嵌入式系统都必需的,在一些简单的嵌入式系统中,比如在微波炉、自动控制洗衣机 等嵌入式系统中,软件功能十分简单,这样就没有必要采用嵌入式操作系统,但在一些 复杂的嵌入式系统中,比如在互联网路由器中,嵌入式操作系统则是必不可少的部件, 因为这些嵌入式系统的应用软件十分复杂,若不采用嵌入式操作系统来进行支撑,其开 发工作将十分困难,甚至无法完成。 总之,嵌入式系统就是由嵌入式硬件和嵌入式软件组成的,具备特定功能的计算机 系统,其中,嵌入式软件又可进一步分为嵌入式操作系统和嵌入式应用软件,如图 1-1 所示。 错误! 嵌入式应用软件 嵌入式操作系统 嵌入式软件 嵌入式硬件 图 1-1 嵌入式系统软硬件之间的关系 嵌入式操作系统是整个嵌入式软件的灵魂,起到承上启下(连接嵌入式硬件和嵌入 式应用软件)的作用,而且往往也是嵌入式软件中最复杂的部分。虽然复杂,嵌入式操 作系统的功能接口却相对标准化和统一,功能差异很大的嵌入式系统,往往可以采用相 同的嵌入式操作系统来进行设计,比如,一台复杂的数字控制机床的控制系统与一架军 用飞机的控制系统,可能采用了相同的嵌入式操作系统,仅仅是具体的应用软件不同。 因此,嵌入式操作系统可以被理解为通用软件,不同的嵌入式操作系统,除了性能上的 差异和实现细节上的差异之外,功能部分往往是相同的。在本书中,我们介绍一个嵌入 式操作系统的功能及其功能的实现细节。
4 自己动手写嵌入式操作系统 1 1.2 嵌入式操作系统概述 从上面的描述中我们知道,嵌入式操作系统是嵌入式系统中的软件部分,且是软件 部分的核心内容。嵌入式操作系统在本质上也是一个操作系统,其一些概念与通用计算 机操作系统是一致的,但由于应用环境的不同,嵌入式操作系统与通用操作系统有一些 区别,且嵌入式操作系统本身具备一些通用操作系统所不具备的特性。在本节中,我们 对嵌入式操作系统本身具备的一些特点,以及与通用操作系统的区别进行简单描述。 1.2.1 嵌入式操作系统特点 一个典型的嵌入式操作系统应该具备下列特点: 1.可裁剪性 可裁剪性是嵌入式操作系统最大的特点,因为嵌入式操作系统的目标硬件配置差别 很大,有的硬件配置非常高档,有的却因为成本原因,硬件配置十分紧凑,嵌入式操作 系统必须能够适应不同的硬件配置环境,具备较好的可裁剪性。在一些配置高、功能要 求多的情况下,嵌入式操作系统可以通过加载更多的模块来满足这种需求;而在一些配 置相对较低,功能单一的情况下,嵌入式操作系统必须能够通过裁剪的方式,把一些不 相关的模块裁剪掉,只保留相关的功能模块。为了实现可裁剪,在编写嵌入式操作系统 的时候,就需要充分考虑,进行仔细规划,把整个操作系统的功能进行细致的划分,每 个功能模块尽量以独立模块的形式来实现。 对于裁剪的具体实现,可通过两种方式。一种方式是把整个操作系统功能分割成不 同的功能模块,进行独立编译,形成独立的二进制可加载映像,这样就可以根据应用系 统的需要,通过加载或卸载不同的模块来实现裁剪。另外一种方式,是通过宏定义开关 的方式来实现裁剪,针对每个功能模块,定义一个编译开关(#define)来进行标志。若 应用系统需要该模块,则在编译的时候,定义该标志,否则取消该标志,这样就可以选 择需要的操作系统核心代码,与应用代码一起编联,实现可裁剪的目的。其中,第一种 方式是二进制级的可裁剪方式,对应用程序更加透明,且无需公开操作系统的源代码, 第二种方式则需要应用程序详细了解操作系统的源代码组织。
5 2.与应用代码一起连接 嵌入式操作系统的另外一个重要特点,就是与应用程序一起,连接成一个统一的二 进制模块,加载到目标系统中。而通用操作系统则不然,通用操作系统有自己的二进制 映像,可以自行启动计算机,应用程序单独编译连接,形成一个可执行模块,并根据需 要在通用操作系统环境中运行。 3.可移植性 通用操作系统的目标硬件往往比较单一,比如,对于 UNIX、Windows 等通用操作 系统,只考虑几款比较通用的 CPU 就可以了,比如 Intel 的 IA32 和 Power PC。但在嵌入 式开发中却不同,存在多种多样的 CPU 和底层硬件环境,光 CPU,流行的可能就会达到 十几款。嵌入式操作系统必须能够适应这种情况,在设计的时候充分考虑不同底层硬件 的需求,通过一种可移植的方案来实现不同硬件平台上的方便移植。比如,在嵌入式操 作系统设计中,可以把硬件相关部分代码单独剥离出来,在一个单独的模块或源文件中 实现,或者增加一个硬件抽象层,来实现不同硬件的底层屏蔽。总之,可移植性是衡量 一个嵌入式操作系统质量的重要标志。 4.可扩展性 嵌入式操作系统的另外一个特点,就是具备较强的可扩展性,可以很容易地在嵌入 式操作系统上扩展新的功能。比如,随着 Internet 的快速发展,可以根据需要,在对嵌入 式操作系统不做大量改动的情况下,增加 TCP/IP 协议功能或 HTTP 协议解析功能。这样 必然要求嵌入式操作系统在设计的时候,充分考虑功能之间的独立性,并为将来的功能 扩展预留接口。 1.2.2 嵌入式操作系统与通用操作系统的区别 1.地址空间上的区别 一般情况下,通用操作系统,充分利用了 CPU 提供的内存管理机制(MMU 单元), 实现了一个用户进程(应用程序)独立拥有一个地址空间的功能,比如,在 32 位 CPU 的硬件环境中,每个进程都有自己独立的 4GB 的地址空间。这样每个进程之间相互独立, 互不影响,即一个进程的崩溃,不会影响另外的进程,一个进程地址空间内的数据,不 能被另外的进程引用。嵌入式操作系统多数情况下不会采用这种内存模型,而是操作系 统和应用程序共用一个地址空间,比如,在 32 位硬件环境中,操作系统和应用程序共享 4GB 的地址空间,不同应用程序之间可以直接引用数据。这类似于通用操作系统上的线 程模型,即一个通用操作系统上的进程,可以拥有多个线程,这些线程之间共享进程的 地址空间。
6 自己动手写嵌入式操作系统 1 这样的内存模型实现起来非常简单,且效率很高,因为不存在进程之间的切换(只 存在线程切换),而且不同的应用之间可以很方便地共享数据,对于嵌入式应用来说,是 十分合适的。但这种模型的最大缺点就是无法实现应用之间的保护,一个应用程序的崩 溃,可能直接影响到其他应用程序,甚至操作系统本身。但在嵌入式开发中,这个问题 却不是问题,因为在嵌入式开发中,整个产品(包括应用代码和操作系统核心)都是由 产品制造商开发完成的,很少,需要用户编写程序,因此整个系统是可信的。而通用操 作系统之所以实现应用之间的地址空间独立,一个立足点就是应用程序的不可信任性。 因为在一个系统上,可能运行了许多不同厂家开发的软件,这些软件良莠不齐,无法信 任,所以采用这种保护模型是十分恰当的。 2.内存管理上的区别 通用的计算机操作系统为了扩充应用程序可使用的内存数量,一般实现了虚拟内存 功能,即通过 CPU 提供的 MMU 机制,把磁盘上的部分空间当做内存使用(详细信息请 参考本书“Hello China 的内存管理机制”一章)。这样做的好处是可以让应用程序获得比 实际物理内存大得多的内存空间,而且还可以把磁盘文件映射到应用程序的内存空间, 这样应用程序对磁盘文件的访问,就与访问普通物理内存一样了。 但在嵌入式操作系统中,一般情况下不会实现虚拟内存功能,这是因为: (1)一般情况下,嵌入式系统没有本地存储介质,或者即使有,数量也很有限,不 具备实现虚拟内存功能的基础(即强大的本地存储功能); (2)虚拟内存的实现,是在牺牲效率的基础上完成的,一旦应用程序访问的内存内 容不在实际的物理内存中,就会引发一系列的操作系统动作,比如引发一个异常、转移 到核心模式、引发文件系统读取操作等一系列动作,这样会大大降低应用程序的执行效 率,使得应用程序的执行时间无法预测,这在嵌入式系统开发中是无法容忍的。 因此,权衡利弊,嵌入式操作系统首选,是不采用虚拟内存管理机制,这也是嵌入 式操作系统与通用的操作系统之间的一个较大的区别。 3.应用方式上的区别 通用的操作系统在使用之前必须先进行安装,安装包括检测并配置计算机硬件、安 装并配置硬件驱动程序、配置用户使用环境等过程,这个过程完成之后,才可以正常使 用操作系统。但嵌入式操作系统则不存在安装的概念,虽然驱动硬件、管理设备驱动程 序也是嵌入式操作系统的主要工作,但与普通计算机不同,嵌入式系统的硬件都是事先 配置好的,其驱动程序、配置参数等往往与嵌入式操作系统连接在一起,因此,嵌入式 操作系统不必自动检测硬件,因而也无需存在安装的过程。 除了上述特点与区别外,嵌入式操作系统还有一些其他自身特点,在此不再详述, 有兴趣的读者可参阅相关资料。
7 1.2.3 嵌入式实时操作系统 另外一个需要提及的概念,就是嵌入式实时操作系统。嵌入式实时操作系统也是嵌 入式操作系统的一种,顾名思义,嵌入式实时操作系统一般应用于对时间要求十分苛 刻的场合,比如高精度的数字控制机床、通信卫星控制系统等。嵌入式实时操作系统 对外部事件的响应时间是有严格控制的,一般有一个底限,在这个底限之内,需要对 外部发生的事件进行响应,这样嵌入式实时操作系统在设计的时候,必须充分考虑这 些要求。 但需要说明的是,一个实时系统并不是由嵌入式实时操作系统自身决定的,而是由 嵌入式硬件、嵌入式操作系统、嵌入式应用软件等共同决定的,单一因素,比如嵌入式 操作系统无法决定整个系统的实时性,这很容易理解。 还有一种对嵌入式操作系统的实时性进行描述的说法叫做“半实时操作系统”。这种 操作系统不像严格的实时操作系统(姑且叫做硬实时操作系统)对事件的相应有一个严 格的底限,但又与普通操作系统对外部事件相应的不确定性有所区别,介于两者之间。 这样的操作系统可以满足大部分嵌入式应用的需求,而且当前情况下,一般商用的操作 系统都是这种“半实时操作系统”。本书介绍的“Hello China”操作系统也属于半实时操 作系统。 操作系统、嵌入式操作系统、实时嵌入式操作系统和半实时操作系统的关系,如图 1-2 所示。 操作系统 嵌入式 操作系统 嵌入式实时操作系统 嵌入式半实时操作系统 嵌入式非实时操作系统 图 1-2 操作系统之间的关系 1.3 操作系统的基本概念 为了便于后续内容的理解,我们简单介绍与操作系统相关的几个概念。
8 自己动手写嵌入式操作系统 1 1.3.1 微内核与大内核 微内核与大内核是操作系统设计中的两种不同的思想,这与 CPU 的设计中 RISC 和 CISC 构架类似。其中,微内核的思想是,把尽量少的操作系统机制放到内核模块中进行 实现,而把尽量多的操作系统功能以单独进程或线程的方式实现,这样便于操作系统体 系结构的扩展。比如,一个常见的设计思路就是,把进程(或线程)调度、进程间通信 机制(IPC)与同步、定时功能、内存管理功能、中断调度等功能放到内核中实现,由于 这些功能需要的代码量不是很大,所以可使得内核的尺寸很小。另外,把操作系统必须 实现的文件系统、设备驱动程序、网络协议栈、IO 管理器等功能作为单独的进程或任务 来实现,用户应用程序在需要这些功能的时候,通过核心提供的 IPC 机制(比如消息机 制)向这些服务进程发出请求,即一种典型的客户-服务器机制。 这种微内核的实现思路有很明显的优势,比如体系结构更加清晰,扩展性强等,而 且由于内核保持很小,移植性(不同 CPU 之间的移植)也很强。但此思路也有很大的弊 端,其中最大的一个弊端就是效率相对低下,因为系统调用等服务都是通过 IPC 机制来 间接实现的,若服务器进程繁忙,对于客户的请求可能无法及时响应。因此这种微内核 的设计方式,不太适合嵌入式操作系统的设计,因为效率是嵌入式操作系统追求的最主 要目标。 图 1-3 示意了微内核的设计思路。 设备驱动 文件系统 网络协议栈 线程调度 内存管理 内核 I/O 管理器 用户应用 1 虚拟内存管理 用户应用 2 IPC 中断调度 用户应用 3 定时 同步 图 1-3 微内核操作系统的设计思想 大内核则是一种相反的设计思路,这种思想是,把尽可能多的操作系统功能拿到内 核模块中实现,在操作系统加载的时候,把这些内核模块加载到系统空间中。由于这些 系统功能是静态的代码,不像微内核那样作为进程实现,而且为这些代码直接在调用进 程的空间中运行,不存在发送消息、等待消息处理、消息处理结果返回等延迟,因此调 用这些功能代码的时候,效率特别高。因此,在追求效率的嵌入式操作系统开发中,这 种大内核模型是合适的。
但大内核也有一些弊端,最明显的就是内核过于庞大,有时候会使得它的扩展性不 好(这可以通过可动态加载模块来部分解决)。但在嵌入式操作系统开发中,这种弊端表 现得不是很明显。图 1-4 示意了大内核的开发思想。 9 用户空间 内核空间 用户应用 1 用户应用 2 用户应用 3 线程调度 内存管理 文件系统 设备驱动程序 IPC 中断调度 虚拟内存管理 定时 同步 I/O 图 1-4 大内核操作系统的设计思想 1.3.2 进程、线程与任务 一般情况下,描述操作系统的任务管理机制时存在三个说法。 进程 所谓进程,是一个动态的概念。一个可执行模块(可执行文件)被操作系 统加载到内存,分配资源,并加入到就绪队列后,就形成了一个进程。一般情况 下,进程有独立的内存空间(比如在典型的 PC 机操作系统中,如果目标 CPU 是 32 位则一个进程就有独立的 4GB 的虚拟内存空间),如果不通过 IPC 机制,进程 之间是无法交互任何信息的,因为进程之间的地址空间是独立的,不存在重叠的 部分; 线程 一般情况下,线程是 CPU 可感知的最小的执行单元,一个进程往往包含多 个线程,这些线程共享进程的内存空间,线程之间可以直接通过内存访问的方式 进行通信,线程之间共享同进程的全局变量,但每个线程都有自己的堆栈和硬件 寄存器; 任务 概念同线程类似,但与线程不同的是,任务往往是针对没有进程概念的操 作系统来说的,比如嵌入式操作系统。这些操作系统没有进程的概念,或者说整 个操作系统就是一个进程,这种情况下,任务便成了操作系统中最直接的执行单 位。另外一个说法就是,任务往往是一个无限循环,操作系统启时,任务随之启 动,然后一直运行到操作系统结束为止。 目前情况下在 Hello China 的实现中是没有进程概念的,但却存在多个执行线索,因 此,用任务来描述这些执行线索是最合适的。但考虑到这些执行线索并不一定是无限循
分享到:
收藏