第3章第5节 发布Wanlix操作系统

来源:互联网 发布:中国程序员联合开发网 编辑:程序博客网 时间:2024/06/04 14:54


目前更新到5.3节,请在
http://dl.dbank.com/c02ackpwp6下载5.3节的全部文档

 

本节源代码请在http://dl.dbank.com/c09ili3mqc下载

 

第5节 发布Wanlix操作系统

经过前面3.1~3.3节循序渐进的开发,我们已经使Wanlix操作系统具备了最基本的任务切换功能,并在3.4节使用Wanlix开发了一个交通红绿灯控制系统,到此为止,我们已经完成了Wanlix操作系统的所有开发工作。Wanlix操作系统的定位就是一个非常小巧的操作系统,有关嵌入式操作系统更多的功能,我们将在第4章开发Mindows操作系统的过程中不断的引入。

 

前面几节的代码里不仅包含了Wanlix的代码,而且还使用了一些用户代码来演示操作系统的功能,现在我们将操作系统的代码单独整理出来,去掉用户代码,发布仅有操作系统的代码,当用户需要使用这个操作系统时,只要在这些操作系统代码基础上补充自己的代码即可使用。

Wanlix目录下的代码全部是操作系统代码,这个目录下的文件需要保留。srccode目录下是用户代码,需要删除,但wlx_userboot.cwlx_userboot.h文件作为操作系统与用户代码的接口文件需要保留,在wlx_userboot.c文件里需要清空WLX_RootTask函数里面的内容,wlx_userboot.h文件里去掉对用户文件global.h的引用,去掉system目录下Keil开发环境使用的启动文件startup.s和链接文件ADuC702X.ld,最后剩下的就仅仅是操作系统的代码了!

这个小操作系统虽然功能简单,但绝对可以实现任务的调度功能,这点对于一个小项目的程序设计来说就已经方便很多了,而且更难能可贵的是它耗费的系统资源是如此之少,编译后仅仅占用了5600个字节的程序空间(不同编译器编译的结果会有所不同)和16个字节的内存空间。

当然,这个操作系统目前只能用在ARM7芯片上,因为任务切换的核心代码是用汇编编写的,而不同芯片的汇编语言又不兼容。因此,如果你需要在其它芯片上使用这个操作系统就必须修改wlx_core_a.asm文件里的函数,芯片的出入栈方式也是需要考虑的。

这个操作系统还有一个限制:任务不能运行到结束。不管是像TEST_TestTask1任务那样是个死循环,还是像WLX_RootTask任务那样执行一次之后就永远不会再切换回来继续执行了,一定要保证被创建任务的函数永远不能执行到最后一条指令。因为一个函数执行完后,会通过跳转指令返回到它的父函数,而Wanlix操作系统在创建任务时并没有为被创建任务的函数提供返回地址,因此被创建任务的函数就不能结束,这就是任务不能结束的原因。为了防止出现这种问题,每个任务需要使用while构造成一个死循环。本着Wanlix只实现最简单的任务切换功能的原则,这个问题在这里就不解决了,我们将在Mindows操作系统中解决。

另外,任务切换函数WLX_TaskSwitch不能在中断中使用。该函数会备份恢复任务的上下文信息,如果在中断中调用该函数则会破坏中断栈中的数据,导致系统崩溃。

 

仅剩下的这些操作系统代码只能编译,却不能链接出可执行的最终目标文件。

所谓编译就是将我们所写的C、汇编等源代码翻译成芯片能理解的机器语言的过程,这个过程中会使用一些技巧,减少冗余的代码,提高效率,这就是优化,例如

i++;

i++;

可以优化为:

+= 2;

当然,这个过程并非只是像这个例子这么简单,而是包含了大量的相当复杂的处理,以至于编译器的开发在相当长的一段时间内进展不大。在编译时有很多优化选项可以使用,Wanlix所使用的O2选项就是其中的一个。

在程序出现的初期,是并没有编译这一概念的。那时候的电子设备没有键盘、显示器这样的输入输出设备,电子设备最小的功能单元就是一个个门电路,它只能识别0或者1的数字信号,因此,能想到的最方便的编程方法就是直接书写由01组成的程序,也就是直接写二进制机器码,类似2.2节中图6那样的格式,但这样编码不但效率低,而且极容易出错,修改、定位问题时更是痛苦万分。在编程过程中,人们逐渐发现,同一种操作都是对应同一个机器码,那么是否可以使用一个符号来代替这些机器码呢?这样就产生了汇编语言,例如在ARM7芯片上,使用汇编指令“ADD”来代替“加”操作的机器码“0b0100”,但这样替换后,也就需要一个翻译器将ADD符号及其后面的操作参数一起翻译成机器码,这就是编译器的概念。汇编语言的出现极大的提高了编程的效率,程序员们只需要记住汇编指令即可,不需要去查找复杂的机器码指令了。但汇编语言也只是简单的做了一些指令翻译的工作,程序运行的细节还需要程序员去关心,与硬件相关性也非常强,不同芯片的机器码也是不同的,汇编指令也是不同的,这给程序移植造成了很大的困难。这样编程语言就发展到了高级语言阶段,例如我们所使用的C语言。高级语言屏蔽掉了硬件层的概念,将程序语言抽象为接近人类逻辑的数字语言和自然语言,那么这个屏蔽硬件层、抽象语言的过程就完全由高级语言编译器来完成了。

 

源程序经过编译器的处理,被编译成了芯片可以识别的机器码,但此时还不能直接运行,因为编译过程只产生了机器码,并没有为这些机器码分配地址空间,前面我们介绍过,函数调用的过程就是PC指针跳转的过程,就是跳转到指令运行的地址空间取指的过程。每段程序必须有自己运行的空间,这是在链接过程中确定的,链接器会根据链接文件的配置,将已编译好的机器码分配到不同的地址空间,并计算各个函数、变量之间的地址关系,将他们关联起来,这样才会生成最终可执行的目标文件。

在链接过程,我们可以选择输出map文件,在Keil环境下可以选择“Project>Options for target”,打开下面的对话框,选择“Listing”页,把红框内的“Memory Map”选上,重新编译链接就会生成map文件。

第3章第5节 <wbr>发布Wanlix操作系统

图 25  Keil中生成map文件的选项

map文件中包含了各个段、函数、全局等符号的地址分配情况,下面我截取了3.4map文件中的一部分内容做个简单介绍:

006  Allocating common symbols

007  Common symbol       size              file

008  

009  gaucRootTaskStack   0x190             ../outfile/wlx_core_c.o

010  gpstrRootTaskTcb    0x4               ../outfile/wlx_core_c.o

011  guiCurSta           0x4               ../outfile/test.o

 

……

 

025  Memory Configuration

026  

027  Name             Origin             Length             Attributes

028  IntFLASH         0x00080000         0x0000f800         xr

029  IntRAM           0x00010000         0x00001b74         rw

030  *default*        0x00000000         0xffffffff

 

……

 

066   *(.text)

067   .text          0x00080108       0x54 ../outfile/wlx_core_a.o

068                  0x00080108                WLX_ContextSwitch

069                  0x0008013c                WLX_SwitchToTask

070   .text          0x0008015c      0x1f4 ../outfile/wlx_core_c.o

071                  0x0008015c                WLX_TaskTcbInit

 

……

 

227                  0x00010308                guiCurSta

006行可知gaucRootTaskStack全局变量的大小是0x190字节,在wlx_core_c.c文件中定义的。从011行可知guiCurSta全局变量的大小是4字节,在test.c文件中定义的,从227行可以知道它位于0x00010308的地址空间。从028行可知软件中有一个IntFLASH段,它从0x00080000地址开始,长度为0x0000f800字节,属性是只读和可执行。从029行可知软件中有一个IntRAM段,从0x00010000地址开始,长度为0x00001b74字节,属性是可读可写。从067068行可知,WLX_ContextSwitch函数位于wlx_core_a.asm文件中,它的起始地址是0x00080108

map文件对软件开发还有会有一些帮助的,定位问题时我们可能会需要通过查找map文件来获得一些信息。不同工具生成的map文件格式是不同的,但内容大概都差不多,我这里只能抛砖引玉,遇到具体的情况还得读者自己分析。

 

   本节发布的Wanlix代码,由于没有启动文件,也没有链接文件,因此不能生成可执行的目标文件,但我们可以将它们编译成库文件。在Keil环境下可以选择“Project>Options for target”,打开下面的对话框,选择“Output”页,选择红框内的“Create Library”,重新编译就会生成库文件。

第3章第5节 <wbr>发布Wanlix操作系统

图 26  Keil中生成库文件的选项

   库文件是编译的结果,它里面包含了源代码编译后的机器码以及符号表,因此它可以作为链接过程的输入。我们可以将操作系统编译成库文件提供给用户使用,用户只要在自己的工程里包含库文件,就可以直接调用库文件里的函数、全局变量就可以了,而不需要拥有库文件的源代码。库文件的方式屏蔽了源代码,只提供机器码,对于不开源的软件,往往就是使用这种方法。对于某些较大的项目也可以使用这种方式开发,底层软件人员将他们的代码编译成库文件,发布给上层软件人员使用,这样上层软件人员不需要全套代码就能编译出最终目标文件了。这样做不仅管理方便,而且也可以降低泄露产品全套源代码的风险。

第3章第5节 <wbr>发布Wanlix操作系统

图 27  不使用库文件和使用库文件Keil工程对比

27中,左侧是没有使用库文件的工程文件树结构,使用Wanlix的源代码编译。右侧是使用了库文件的工程文件树结构,使用Wanlix的库文件编译。注意,当使用库文件时,必须将库中的接口函数声明到一个h头文件中,使用库文件的程序也必须包含此头文件。在Wanlix操作系统中,这个头文件就是wanlix.h,里面包含了Wanlix操作系统的全部对外接口函数,见附录1

写到这里,我遇到一个问题,在GNU环境下编译出的是.a库文件,使用.a库文件链接时链接程序出错了,不知道问题处在哪了,使用Keil自带的RealView编译链接器则没有此问题。如果哪位知道原因的话请到论坛上反馈一下,多谢了!

 

Wanlix的开发到此就告一段落,我们最后为Wanlix设定一个版本号作为这一阶段的结束标志。版本号的格式为Major.Minor.Revision.BuildMajor是主版本号,软件功能或结构重大改变时才修改此版本号,比如增加了多个重要功能或者整体架构发生变化。Minor是子版本号,基于原有功能、结构增加、修改一些功能时修改此版本号Revision是修改版本号,当修改bug、完善一些小功能时就更改此版本号。Build是编译版本号,每次正式编译时该版本号加1。每当上一级版本号变动后,下一级版本号归0重新开始。

MajorMinor版本很重要,修改时需要产品相关人员参加讨论是否需要修改,如何修改,这2个版本往往影响了产品大的特性,对用户会有直接的影响。Revision版本一般限于项目组内由项目经理控制,但需要发布给其它项目组使用。Build版本一般限于项目组内部的修改测试,不对外公布。因此,产品发布新版本时,需要体现出MajorMinorRevision版本,Build版本建议不体现。

版本控制也非常重要,不能随意乱发导致版本过多,过多的版本会给产品维护带来非常多的麻烦,并且会增加维护成本。发布版本前需要做好版本计划,规划好一段时间内的版本数量,需要解决哪些问题,需要在哪个版本解决,版本发布后需要记录已发版本的特性、产品不同模块之间版本的配合使用关系等等问题。

版本号

说明

Major

Minor

Revision

Build

001.001.001.000

主版本号

子版本号

修正版本号

编译版本号

表 5  Wanlix版本号格式

此次Wanlix发布的版本号为001.001.001.000,只提供任务切换功能,Wanlix后续还有发展的话,就在此版本号基础上修改。

我最原始的计划只是将代码写到这里,写操作系统的初衷只是因为当时找不到一个适合小型嵌入式设备的操作系统,才萌生自己写一个只具有任务切换功能的操作系统的想法。但当我写到这里,实现了任务切换功能之后,我发现我还可以实现更多的功能,还可以讲述更多的原理,还可以让更多的人了解更多的操作系统知识,还可以继续写下去。因此,我将继续写下去,去编写一个功能更强大更完善的操作系统——Mindows操作系统。

接下来的章节,我们将开始设计Mindows操作系统内核,一个具有实时抢占性的嵌入式操作系统内核!在编写Mindows的过程中我们将了解操作系统更多的知识,实现操作系统更多的功能!

阅读全文
0 0
原创粉丝点击