8.3.2 操作系统软件架构
8.3.2.1 操作系统总体架构
星载嵌入式操作系统是运行在星载计算机中的系统软件,它是管理星载计算机的硬件和软件资源的计算机程序,是直接运行在“裸机”上的最基础的系统软件。星载计算机上的任何其他软件接口必须在星载嵌入式操作系统的支持下才能运行。星载嵌入式操作系统是软件与计算机硬件平台的接口,也是星载计算机与星上其他单机设备之间通信的支撑平台。
基于航天多年的星载嵌入式软件设计经验,结合风云翼辉嵌入式操作系统的内核IP授权,上海航天电子技术研究所孵化出了适用于宇航领域的星载嵌入式操作系统——风云翼辉(AIC-OS)嵌入式操作系统。AIC-OS是一款内核100%自主可控、大型实时、国产化嵌入式操作系统。其系统架构如图8-4所示,整个操作系统架构从下到上分为硬件平台、操作系统BSP层、操作系统内核层、操作系统服务层,以及应用层。
图8- 4 AIC-OS操作系统架构图
8.3.2.2 操作系统BSP层
星载计算机系统由硬件环境、星载嵌入式操作系统和应用程序组成,硬件环境是星载嵌入式操作系统和应用程序运行的计算机硬件平台,星上不同的应用需求会对硬件环境提出不同的要求。因此,对于星载计算机系统来说,其运行的硬件平台具有多样性。为了能够给星载操作系统提供统一的运行环境,在星载嵌入式操作系统架构设计时,特别在计算机硬件平台和操作系统之间提供一层硬件相关层,来屏蔽不同星载计算机之间的硬件差异。这种硬件相关层就是星载嵌入式操作系统中的板级支持包(board support package,BSP)。
1)BSP层的功能
从图8-4中可以看出,基于软件分层设计思想,将操作系统中和硬件相关的部分提取出来,形成BSP层,连接硬件平台和嵌入式操作系统,实现了上层软件和硬件的无关性。BSP和嵌入式操作系统内核之间采用了统一硬件抽象接口,不会因硬件平台的不同而有所改变。但是,对于不同的硬件平台,设计出的BSP肯定有所不同,即使硬件的处理器相同,但是外围硬件不同的话,依然会影响BSP的实现。
BSP层的功能主要包括初始化和驱动程序两个方面。初始化功能主要实现从上电到操作系统内核和系统任务启动的执行过程,是目标系统启动时的硬件初始化及操作系统引导历程,只在系统启动过程中执行一次;驱动程序就是设置某个硬件完成其固有功能的程序,其本身直接和硬件交互。驱动程序对上层操作系统内核需要提供一套规范抽象化的接口,对下层硬件就需要驱动硬件设备进行工作,它起到一个中间转换的作用,移植BSP的主要工作就是对目标板特定硬件设备的驱动程序进行编写。
2)BSP层次结构
BSP层次结构的设计初衷是为在各种不同星载计算机硬件平台上的嵌入式操作系统内核开发提供统一的硬件平台的相关功能,这就要求BSP层本身能够易于扩展和移植到不同的硬件平台之上,才能为相应的星载计算机硬件平台上的操作系统开发提供支持。与硬件平台相关的软件分为体系结构相关软件和外围设备端口寄存器操作相关部分。体系结构相关软件部分能够用于与CPU内核系统结构兼容的不同星载嵌入式微处理器上,而对外围设备端口寄存器的操作,则每种嵌入式微处理器都不相同。因此,BSP层功能实现设计的3个层次结构是通用硬件驱动层、体系结构层及外围设备驱动层。BSP层次结构如图8- 5所示。
图8-5 BSP层次结构图
(1)通用硬件驱动层。为C语言所编写,不涉及体系结构及外围端口寄存器具体操作,能够通用于各种硬件平台。通用硬件驱动层包括对统一的与编译器无关的数据类型、抽象设备的数据结构定义,以及提供给星载嵌入式操作系统内核抽象设备的各种统一操作服务接口的通用实现部分。
通用硬件驱动层分为两部分:一部分存在于操作系统内核中,为抽象设备通用实现部分;另一部分为具体体系结构和外围设备实现的回调接口,这部分存在于BSP层中,主要涉及对CPU内核寄存器的操作以及对外围I/O端口寄存器的操作。在不同平台之间移植或扩展时,主要修改的就是这些回调接口部分。
(2)体系结构层。针对各种星载嵌入式微处理器CPU内核的系统结构,体系接口层需要分别设计实现。体系结构层对体系结构中具体的寄存器上下文保存格式、中断异常向量的起始地址、各种微处理器异常及中断处理入口偏移等进行定义。体系结构层还需要负责实现通用硬件驱动层对体系结构的回调部分功能,主要包括对微处理器内核中各个寄存器的访问、对中断向量表的操作,以及对底层中断和异常的处理。另外,星载微处理器中除了CPU内核外,还有各种外围硬件的控制器需要软件驱动,这部分驱动是通过外围设备回调接口在外围设备驱动层实现的。
(3)外围设备驱动层。它是针对各种星载嵌入式微处理器以及外围硬件设备而分别设计实现的,主要包括对外围I/O接口和设备属性的定义(包括中断控制器连接的外围硬件设备个数、定时器个数等),并且负责对各个外围I/O设备端口寄存器的访问操作。外围设备驱动层需要严格按照上层定义的接口实现。
外围设备驱动层必须提供对存储控制、总线控制、中断控制器、定时器控制及UART等基本I/O端口寄存器的访问操作功能。外围设备驱动层是与各种星载微处理器和外围硬件设备一一对应的,在采用不同星载微处理器的硬件平台之间,外围设备驱动层是无法通用的。因此,针对新的星载计算机硬件平台的BSP移植或扩展,外围设备驱动层都需要重新设计实现。
外围设备驱动层中的设备驱动按照设备类型大致分为以下几类:
①字符设备驱动。字符设备是能够像字节流一样被访问的设备。字符设备驱动程序通常至少要实现open、close、read和write等系统调用。字符设备可以通过文件系统节点进行访问,这些设备文件和普通文件之间的唯一差别在于对普通文件的访问可以前后移动访问位置,而大多数字符设备是一个只能顺序访问的数据通道,如星上各种422串口、CAN设备等驱动。
②块设备驱动。块设备能够容纳文件系统,其也是通过设备节点来访问。在操作系统中,进行I/O操作时块设备每次只能传输一个或多个完整的块,而每块包含512字节(或2的幂字节倍数的数据),如星上的NOR Flash、NAND Flash及固态存储器等驱动。
③网络设备驱动。网络设备不同于字符设备和块设备,它是面向报文而不是面向流的,它既不支持随机访问,也没有请求缓冲区。内核和网络设备驱动程序间的通信,完全不同于内核和字符以及块驱动程序之间的通信,内核调用一套和数据包传输相关的函数来实现对网络设备的控制。
④总线子系统。主要包括星上常用的1553B总线和CAN总线,另外还有SPI总线、PCIE总线等。总线子系统主要实现了总线管理适配器和总线传输接口,这样总线上的设备能够调用统一的系统接口,实现其对应的功能。
8.3.2.3 操作系统内核层
操作系统内核层是整个星载嵌入式操作系统的核心,系统其他部分必须依赖于这部分软件提供的服务,例如提供设备管理、分配系统资源等。通常一个内核层由负责响应中断的中断服务程序,负责多个线程分享处理器时间的调度程序,负责管理进程地址空间的内存管理程序,以及线程、进程之间通信等系统服务程序共同组成。对于提供保护机制的现代操作系统来说,内核独立于普通的应用程序,它一般处于系统状态,拥有受保护的内存空间和访问硬件设备的所有权限。
1)任务管理模块
星载嵌入式操作系统内核中的任务管理模块主要包含了实时任务的调度器管理、线程管理及进程管理。
(1)调度器管理。调度程序负责决定将哪个线程任务投入运行、何时运行及运行多长时间,调度程序可以看作在可运行状态的线程之间分配有限的处理器时间资源的内核子系统。星载嵌入式操作系统的实时性主要由内核调度管理的策略决定,只有通过调度程序的合理调度,系统资源才能够最大限度地发挥作用,对外体现出多线程、多进程的并发执行的效果。
任务的调度分为非抢占和抢占两种基本方式。在非抢占的情况下,一旦任务处于运行状态,就会不断执行直到终止,任务要么是因为等待系统I/O资源或请求某些操作系统服务而阻塞,要么是因为任务主动放弃CPU使用权,否则将一直占用CPU资源;在抢占的情况下,当前正在运行的任务可能被操作系统中断,并转换为就绪态。一个新任务到达时,或者中断发生后把一个阻塞态的任务置为就绪态,或者出现周期性的时间中断,就需要进行抢占决策。目前,绝大多数的操作系统都采用了抢占的方式,其抢占原则包括优先级原则、时间片原则和短作业优先原则。
任务调度的策略是将CPU占用时间作为系统中的一种资源,进行调度时应考虑的主要原则有:
①公平,即每个任务合理地获得CPU的运行时间。
②有效,即尽量使CPU百分之百地处于忙碌状态。
③周旋时间,即用户等待输出的时间尽可能短。
④响应时间,即交互用户的响应时间尽可能短。
⑤吞吐量,即单位时间内处理的作业数尽可能多。
实时系统是实时应用,程序结果的正确性不仅与计算结果有关,还和计算结果的响应时间有关,数据必须在一定的截止期限内完成,否则产生的结果毫无意义。宇航应用的场景基本都是实时应用,所以调度策略还应考虑实时性,保证实时任务在截止期限内完成。
在宇航应用的场景下,广泛采用的调度策略是基于优先级抢占的调度机制。当一个优先级更高的任务到达时,允许将当前正在运行的任务暂时挂起,而令高优先级的任务立即投入运行,这样便可以满足实时系统对任务截止时间的要求。另外,对于同一优先级的任务采用了先来先服务和时间轮转的策略。
(2)线程管理。线程又称为任务,指某个单一顺序的指令流,是操作系统调度的最小单位。一个标准的线程由线程句柄(或ID)、当前指令指针(PC)、CPU寄存器集合和线程栈组成。每一个线程都是操作系统调度的单位。线程的管理包括线程的创建、调度、退出和取消等。
一个CPU在一个时刻只能运行一个线程,如果系统中存在多个线程,则CPU需要在几个线程之间切换运行,从宏观上来看相当于多个线程并发执行。CPU什么时刻运行哪一个线程是由操作系统调度器策略决定的。实时操作系统中每一个线程都拥有自己的优先级,当优先级高的线程需要执行时,操作系统会立即切换当前CPU去执行更高优先级的线程,这样的调度算法满足航天器电子系统中对实时信号的响应需求。
进程或内核中的多个线程之间可以并发执行。但由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程包括运行、就绪、阻塞和挂起四种基本状态。线程状态切换如图8-6所示。四种状态含义如下:
①运行:线程已被操作系统调度(操作系统将一个CPU分配给线程用于执行线程代码)。
②就绪:线程已经拥有使其运行的一切资源,等待操作系统调度。
③阻塞:线程缺少使其运行的条件和资源,必须等条件满足后方可进入就绪态。
④挂起:当线程被挂起时,该线程不会被CPU所运行。
图8-6 线程状态切换
每个线程都有自己的属性,包括线程的优先级、栈信息和线程参数等。
(3)进程管理。进程是操作系统中资源的容器,管理程序的代码、数据、线程和信号量等资源;所有程序都必须依附于进程运行。当一个进程销毁时,所有属于该进程的资源也会被销毁,如文件句柄、socket套接字和线程等。
进程管理主要包括进程状态机、进程调度、进程控制。进程状态反映进程执行过程的不同阶段,其随进程的执行和外界条件的变化而转换。进程状态转换如图8-7所示。存在以下四种状态的进程:
①初始态:进程尚在初始化过程中,正在执行程序加载、内存初始化等操作,尚不具备运行条件。
②运行态:进程正在运行,进程中的线程或参与调度,或处于阻塞状态。
③退出态:进程已经结束运行,进程在进入退出态时会发送信号给其父进程,由父进程适时回收子进程残余资源。
图8-7 进程状态转换
④停止态:部分进程在运行过程中会进入停止态,在停止态下,进程所有线程停止运行,不参与调度。如在调试进程时,调试器会经常让进程进入停止态而观察进程数据。
进程是系统资源分配的基本单位(可以看作资源的容器),线程是调度的基本单位,因此对进程的调度是指对进程的主线程进行调度。可以设置满足条件的所有线程的调度优先级、进程亲和度(主要应用于多核)等。
2)任务同步与通信模块
星载嵌入式操作系统内核中的任务同步与通信模块主要分为线程和进程之间的同步和通信。任务同步主要用于协调多任务系统中任务的执行顺序和共享资源的互斥访问。通信模块主要用于在不同任务间传递数据。任务同步和通信的机制主要包括信号量、互斥量、消息队列、信号、条件变量及读写锁等。
(1)线程间的同步和互斥。一个可供线程访问的变量、设备或内存块等类型的实体被称为资源,可供多个线程访问的资源被称为共享资源。如果线程在访问共享资源时不独占该共享资源,那么可能会造成资源异常(如变量值混乱、设备出错或内存块内容不是期望值等),进而导致程序运行异常甚至崩溃。线程间通信就是对多个线程访问共享资源时,采用互斥访问,也即同一时间只允许一个线程访问共享资源。实现互斥有多种方法:关中断、禁止任务调度等。而对于实时性要求较高的航天器系统中,关中断和禁止任务调度所带来的风险更大,所以,应用于航天器电子系统的嵌入式实时操作系统需要有较完整的信号量机制确保资源的共享安全。线程间同步和互斥的手段主要包括以下几项:
①二进制信号量。取值限定于FALSE和TRUE,允许线程访问的一个资源,使用二进制信号量作为互斥手段,初始值为TRUE;通过线程或中断通知另一个线程事件发生,初始值为FALSE。
②计数型信号量。通常用于多个线程共享使用某资源,主要应用在有允许线程访问的n个资源中,使用计数型信号量作为资源剩余计数,初始值为n;通过线程或中断通知另一个线程事件发生,使用计数型信号量作为事件计数,初始值为0。
③互斥信号量。用于共享资源需要互斥访问的场合,可以理解为初始值为TRUE的带优先级天花板和优先级继承机制的二进制信号量,适用于两个线程以上的共享资源互斥访问。
(2)进程间通信。它是指两个或两个以上的进程之间传递数据或信号的一些技术或方法。进程是计算机系统分配资源的最小单位,每个进程都有自己的一部分独立的系统资源,彼此是隔离的。为了能使不同的进程互相访问资源并进行协调工作,需要进程间通信机制。进程间通信方式包括:
①管道。为进程间通信的一种方式。和现实世界的传输管道类似,管道有两个端口,即读端和写端,并且只允许数据从写端流向读端,所以管道是一种流式设备。其包括匿名管道和命名管道。
②消息队列。用于在不同进程间传输数据,消息以队列的形式存储。接收消息的进程按照先进先出或优先级原则排除接收消息。在按优先级次序排队的情况下,相同优先级的接收进程按照先进先出原则排队。接收消息的进程排队原则在消息队列创建时定义。
③共享内存。用于进程间大数据量的传输。为了避免有多个写者进程对同一块共享内存进行写操作,通常需要使用一个命名信号量作为共享内存的写锁。同时为了让读者进程能够及时知道写者进程已经修改共享内存的内容,通常需要使用一个命名信号量作为该共享内存的读通知信号。
3)时钟与定时器模块
嵌入式实时操作系统内部记录了自系统启动后所产生的时钟节拍(称作TICK)计数,该计数即代表系统时间。时钟节拍以一个固定的频率产生,用于操作系统中实现调度、通信和定时管理。
4)中断管理模块
中断是计算机系统中一个十分重要的概念,现代计算机毫无例外地都采用中断机制。在计算机执行程序的过程中,由于出现某个特殊情况(或称为“事件”),使得CPU中止现行程序,转去执行该事件的处理程序(俗称中断处理或中断服务程序),待中断服务程序执行完毕,再返回断点继续执行原来的程序,这个过程称为中断。
(1)中断向量。一般系统将所有的中断信号统一进行了编号(例如256个中断信号:0~255),这个编号称为中断向量。在中断响应前,中断向量与中断信号的对应关系已经定义好。中断向量和中断服务程序的对应关系由中断向量表描述,操作系统在中断向量表中设置好不同中断向量对应的中断服务函数,待CPU查询使用。
CPU在执行完每一条指令后,都会去确认在执行刚才的指令过程中,中断控制器是否发送中断请求。如果有中断请求,CPU就会在相应的时钟脉冲到来时从总线上读取该中断请求的中断向量。
(2)中断处理。当中断产生时,CPU执行完当前指令后,PC指针将跳转到异常向量表的相应地址去执行。该地址处是一句跳转指令,PC指针继续跳转到系统定义的总中断服务函数里面去执行,然后系统进行任务上下文的保存、中断向量号的获得、具体中断服务函数的执行等,执行结束后,恢复被中断任务的上下文,继续执行任务。中断处理过程如图8- 8所示。
图8-8 中断处理过程
5)内存管理模块
内存管理包括定长内存管理和变长内存管理。在支持虚拟内存管理的电子系统中,有内核空间和用户空间之分。内核线程、驱动程序和内核模块均存在于内核空间,应用程序(即进程)和动态链接库均存在于用户空间。
(1)定长内存管理。定长内存是指每次分配获得的内存大小是相同的,即使用的是有确定长度的内存块。同时,内存块总的个数也是确定的,即整个内存总的大小也是确定的。使用这样的内存有两大优点:一是由于事先已经分配好了足够的内存,可极大地提高关键应用的稳定性;二是对于定长内存的管理通常有更为简单的算法,分配/释放的效率更高。
(2)变长内存管理。变长内存相对于定长内存,最大的不同就是每次分配的内存可能大小是不同的。同时,在使用上它和malloc/free类似,唯一的区别是所使用的内存由用户提供。
8.3.2.4 操作系统服务层
星载嵌入式操作系统在服务层提供网络通信服务、文件系统服务及日志系统服务等功能。
1)网络通信服务
AIC-OS支持BSD标准的socket通信接口,这些接口包括socketpair、socket、accept、bind、shutdown、connect、getsockname、getpeername、setsockopt、getsockopt、listen、recv、recvfrom、recvmsg、send、sendto、sendmsg等,同时由于socket也是I/O系统的一部分,所以AIC-OS支持read、write、ioctl、select、fcntl等标准I/O的操作API。
AIC-OS的socket支持四类域,它们分别是:
(1)AF_INET IPV4协议。
(2)AF_INET6 IPV6协议。
(3)AF_UNIX UNIX域协议(socketpair仅支持AF_UNIX域)。
(4)AF_PACKET链路层数据包收发接口。
AIC-OS系统网络架构如图8- 9所示。
图8- 9 AIC-OS系统网络架构
AF_INET和AF_INET6可以用作互联网通信,例如浏览网页、发送电子邮件、传输文件和网络可视电话等。
在AF_INET和AF_INET6下支持的socket类型有SOCK_STREAM、SOCK_DGRAM和SOCK_RAW,它们分别称作流式套接字、用户数据报套接字和原始套接字。其中,流式套接字使用TCP协议进行通信,它提供面向连接的、可靠的全双工管道通信;用户数据报套接字使用UDP协议进行通信,它提供非连接的、不可靠的通信;原始套接字可以让用户自行操作一些更加底层的通信,例如ICMP数据包等。
AF_UNIX可以用作本机进程间双向管道通信,例如,在POSIX系统中大多数支持多进程的图形系统,进程间都是使用AF_UNIX进行双向管道通信。最为著名的是X Windows(X11或X)系统,这个系统使用非常广泛。在它之上运行着很多大型的图形界面,例如GTK+。同样在嵌入式系统里面Qt的QWS也是借助AF_UNIX实现多进程间通信的。
2)文件系统服务
AIC-OS提供了多种标准的文件系统,方便用户使用,这些文件系统是AIC-OS内建的,如果需要更多的文件系统如NTFS,则可以通过内核模块的形式加入。AIC-OS的文件系统实际上是一组虚拟的设备驱动,它提供两组API接口,对上符合I/O系统标准,对下要求设备驱动符合块设备标准。文件系统在I/O系统中的结构如图8- 10所示。
图8-10 文件系统简要结构
AIC-OS文件系统使用I/O系统提供的标准接口进行挂载,然后通过标准I/O操作函数进行访问,换句话说,操作一个普通文件与操作一个设备文件没有任何区别。
目前AIC-OS内建的文件系统包括TPSFS文件系统、ROOT文件系统、PROC文件系统、RAMFS文件系统、FAT文件系统、YAFFS文件系统、NFS文件系统和ROMFS文件系统。
其中,ROMFS文件系统是只读文件系统,系统的关键性文件都可以放在此文件系统中确保安全。如果通过mount挂载FAT、NFS文件系统,那么YAFFS文件系统也都可以挂载成只读文件系统。
PROC文件系统是保存操作系统信息和进程信息的文件系统,这个文件系统对应的文件实体都在操作系统内核中,是内核反馈出来的运行参数和信息,例如每一个进程的进程号都有一个对应的目录,里面存放着进程当前运行的信息,如进程对应的可执行文件、进程打开的文件描述符表、进程消耗的内存信息和进程内部的动态链接库信息等。AICOS内部所有的设备(包括文件系统)都必须挂载到根文件系统上,根文件系统的设备名称非常特殊,为“/”。所有的设备或文件绝对路径都是以根符号起始,也就是说,操作系统查询一个设备总是从根文件系统开始。
3)日志系统服务
为了能够实时记录系统发生的各种事件,AIC-OS加入了日志管理功能,用户通过分析日志文件可以及时发现和处理系统运行过程中的问题。
在实际应用中日志系统根据具体情况分为不同的等级,AIC-OS日志等级与Linux日志等级兼容。AIC-OS提供以下宏来表示不同的日志等级:
(1)KERN_EMERG:会导致主机系统不可用的情况。
(2)KERN_ALERT:必须马上采取措施解决的问题。
(3)KERN_CRIT:比较严重的情况。
(4)KERN_ERR:运行出现错误。
(5)KERN_WARNING:可能会影响系统功能的事件。
(6)KERN_NOTICE:不会影响系统但值得注意。
(7)KERN_INFO:一般信息。
(8)KERN_DEBUG:程序或系统调试信息等。
日志等级从上到下依次变低,通常对于系统来说,如果发现等级KERN_EMERG的日志,则代表发生了严重的问题导致系统不可以再运行。等级KERN_DEBUG通常被用于一些调试信息的打印,在AIC-OS驱动的开发中,经常使用等级KERN_ERR来打印一些错误信息,使用等级KERN_INFO来打印一些普通信息。