本篇介绍“进程模型”、“内核模块与动态链接库”以及“C++支持”。官网:http://www.sylixos.com。
SylixOS在版本1.0.0时引入了进程概念,成为全世界少数几个支持进程的实时操作系统之一。在以往的嵌入式系统中大多数系统不存在MMU,CPU性能也很低,所以根本没有引入进程的必要性,只需要一个多线程操作系统就可以工作的非常好,可是时过境迁,如今的嵌入式处理器性能普遍较高,而且都含有完整的MMU,这使得嵌入式系统在能接受的复杂度下支持多进程成为可能。
由于应用场合与PC大为不同,所以面向工业领域的嵌入式系统大多数为实时操作系统,所以为了实现实时性特点,SylixOS引入的进程类似于VxWorks6.0之后的RTP进程与uClinux的进程,也就是说SylixOS内所有进程共享一个地址空间,只是在动态链接的时候进行代码和数据的重定向,那么进程调度时就不需要切换庞大的页表,这样进程的切换时间就符合实时系统对时间确定性的要求。SylixOS进程实际上是一个资源的容器,这个容器中包含代码段,数据段,文件描述符,内核对象,posix 对象,C库和众多支持的动态链接库,这些库和应用程序共享一个进程容器,进程容器间彼此隔离,那么在使用起来和linux上的标准进程也就没有任何区别了。
SylixOS支持POSIX除fork以外的所有进程API,因为没有fork,所以SylixOS创建进程可以使用ISO C规定的spawn系列函数(头文件为process.h)。同时SylixOS支持POSIX1003.1b规定的实时进程标准,这些函数在
SylixOS进程分为三种类型,它们分别是:正常进程、孤儿进程、僵尸进程。正常进程是指存在父亲进程且本进程与父进程都在运行的进程。孤儿进程是指父进程先于当前子进程结束,当前子进程还在运行的进程。僵尸进程是指子进程已经结束,正在等待父进程回收自己的资源,可是父进程退出时没有回收子进程资源。
对于正常进程,在结束时需要父进程调用wait或者waitpid来回收自己的资源,那么资源的回收将在父进程中进行。
对于孤儿进程,SylixOS内核线程t_reclaim将接管这个进程,该进程结束时,t_reclaim将回收这个进程的所有资源,t_reclaim内核线程类似于Linux系统的init进程。
对于僵尸进程,由于父进程没有调用wait或者waitpid回收子进程就直接退出,这时操作系统t_reclaim内核线程将会自动接管该进程的所有子进程,同时回收该进程所有已经退出但没有回收资源的子进程的资源。这种算法可以使SylixOS尽可能的减少僵尸进程对系统资源的浪费。
SylixOS提供了较为丰富的进程间通信方法,分为异步通信和同步通信。其中同步通信都是采用文件方式,这些文件都存在于内核内存中。
进程间异步通信可以直接使用操作系统提供的信号接口,通过kill或者sigqueue系统调用发送指定的信号给接收进程。
进程间同步通信方式比较多,他们包括:
1. POSIX命名信号量
2. POSIX命名消息队列
3. 命名或匿名管道
4. socket套接字管道
5. 共享内存
注意:POSIX命名对象是虚拟存在的,它并不对应操作系统I/O空间内的实体文件,所以,在操作系统I/O空间内并不显示,用户可以通过查询/proc/posix/pnamed文件来获取操作系统中所有POSIX命名对象的信息。
POSIX命名信号量:多个进程可以通过sem_open创建或者打开拥有同样名称的命名信号量,用此信号量进行同步通信。
POSIX命名消息队列:多个进程可以通过mq_open创建或者打开拥有同样名称的命名消息队列,用此消息队列进行同步通信。需要说明的是SylixOS支持的POSIX消息队列拥有32个消息优先级,消息根据优先级在消息队列内排序,优先级越高的消息最先被接收。下图为进程间使用消息队列通信示意图。
命名或匿名管道:命名管道通过mkfifo来创建,其它进程可以打开这个管道进行进程间通信。匿名管道通过pipe创建,pipe将返回两个文件描述符,一个是读端,另一个是写端,子进程可以通过继承父进程的描述符,也可通过POSIX实时进程提供的file_action参数传递文件描述符进行进程间通信。下图为进程间使用管道通信示意图。
如果进程间需要双向通信,则可以建立两条管道,如下图所示。
socket套接字管道:多个进程间可以使用AF_UNIX协议域进行通信,需要说明的是socket套接字管道不同于标准命名或匿名的单向数据流管道,socket套接字管道不同于普通的单向数据流管道,它是双向全双工管道。当然用户也可以通过TCP/IP的127.0.0.1回环地址进行通信。下图为进程间使用socket管道通信示意图。
共享内存:SylixOS提供专用的共享内存设备/dev/shm,此设备为虚拟设备,它不同于Linux上的tmpfs文件系统,SylixOS的共享内存设备只为了共享内存服务。换句话说所有对该设备的内存映射操作,虽然返回的虚拟地址不同,但是物理地址是相同的。应用程序可在此设备上创建虚拟文件并设置虚拟文件的大小,不同的进程可使用mmap函数映射相关的文件内存实现进程间内存的共享。下图为进程间使用共享内存通信示意图。
SylixOS支持内核模块与动态链接库概念,SylixOS的内核模块与Linux内核模块概念和功能基本相同,加入内核的内核模块,实际上就是内核的一部分,一般利用内核模块实现一些基本的内核服务,例如USB和PCI的总线抽象层;一些外设的驱动程序;额外要支持的文件系统等等。
SylixOS内核可以编译成只带有少数驱动的镜像,然后根据运行硬件的不同,加入不同的内核模块(包含对应的硬件驱动程序)。SylixOS内核模块与Linux格式相同,内核模块文件名一般为*.ko。内核模块一般放置于/lib/modules目录下,设备驱动则一般放在/lib/modules/drivers目录下。
SylixOS支持应用程序动态链接库功能,一个进程内可以加入无限制数量的动态链接库,他们可以由操作系统自动加入也可以由用户代码手动加入。
自动加入动态库:开发应用程序时,在链接阶段通过链接命令指定进程运行时依赖的动态链接库,并把相应的动态库放在环境变量LD_LIBRARY_PATH指定的目录中,这样进程启动时SylixOS会自动装载依赖的动态链接库到指定的进程空间中。
手动加入动态链接库:进程在运行时可以使用POSIX提供的动态链接库功能手动的装载动态链接库。这些API在头文件dlfcn.h中声明,它们分别是:dlclose、dlerror、dlopen、dlsym、dladdr。其中dladdr不是标准的POSIX函数,dladdr提供从地址获取近似符号的功能。
提示:进程崩溃时(收到SIGSEGV等崩溃信号)相关信号句柄可以通过execinfo函数获取到当前的调用栈信息。然后通过dladdr来分析调用栈中的函数名称,这样有利于分析应用程序的BUG。
SylixOS动态链接库的格式也与Linux相同,动态链接库的文件名一般为*.so。如果存在一个动态库多个版本的情况,可以采取Linux对动态库版本的命名方法进行区分,同时建立一个符号链接文件指明当前默认使用的动态链接库。
SylixOS内核提供了一个小型的C++运行时环境,内核支持运行基本的C++程序,这些程序不能带有异常处理(exception)和运行时类型识别(rtti)。因为相关的操作需要完整的C++运行时支持。
内核中的C++程序分为两种:
1. 随SylixOS内核一起编译的C++程序
2. SylixOS装载含有C++程序的内核模块
对于随SylixOS内核一起编译的C++程序,系统将会引出四个符号:
__LW_CTOR_LIST__
__LW_CTOR_END__
__LW_DTOR_LIST__
__LW_DTOR_END__
这四个符号需要在SylixOS链接脚本中放在指定的位置,用他们来指明内核C++程序的全局构造函数表和全局析构函数表。SylixOS在启动时会运行相关的C++全局构造函数表,SylixOS在结束时也会运行相应的全局析构函数表。全局构造与析构函数表如下图所示。
对应的连接脚本范例如下所示。
.ctors : { KEEP (*cppRtBegin*.o(.ctors)) KEEP (*(.preinit_array)) KEEP (*(.init_array)) KEEP (*(SORT(.ctors.*))) KEEP (*(.ctors)) KEEP (*cppRtEnd*.o(.ctors)) } .dtors : { KEEP (*cppRtBegin*.o(.dtors)) KEEP (*(.fini_array)) KEEP (*(SORT(.dtors.*))) KEEP (*(.dtors)) KEEP (*cppRtEnd*.o(.dtors)) } |
相关的文档见SylixOS doc目录的CPLUSPLUS文件。
SylixOS装载含有C++程序的内核模块:SylixOS装载器会自动的识别全局构造函数和析构函数表,然后在适当的时机运行。
应用程序中如果使用C++程序则完全没有内核中使用的诸多限制,因为C++程序可以链接编译器提供的标准stdc++库,这个库和SylixOS内部的C++基础库同时构成完整的C++运行时库,能够支持异常与运行时类型识别等C++高级特性。
(本篇结束)