uC/OS - II学习记录

1. 整个操作系统启动的大致过程是这样的:

  • 初始化系统定时器,用于操作系统的心跳
  • 初始化操作系统(调用OSInit()函数),这个函数内部大致执行以下的工作
    • 初始化变量
    • 初始化各个链表:任务控制块链表、事件链表
    • 创建Idle和统计线程
  • 创建用户任务
  • 开启任务调度

2. 操作系统开启任务调度后流程:

Created with Raphaël 2.1.2 开始 执行当前任务 系统Tick中断? 搜寻最高优先级任务 当前任务优先级最高? 触发一次调度 执行最高优先级任务 yes no yes no

执行第一个任务->系统Tick中断->系统时钟处理->搜寻最高优先级任务->如果当前任务不是最高优先级任务,则触发一次任务调度->否则进行正常的中断返回

3. 如何阅读操作系统源码

在明白了uC/OS - II的大致流程后,有点迷茫了,大概有这样几个困惑:

  • 不知道该从哪里下手
  • 我是想完完整整把uC/OS - II搞懂,但不知道怎样才算完完整整搞懂
  • 有人说会用就行,不知道怎样才算会用

对上述困惑,也有了以下的一些分析:

  • 手里有一本任哲的《嵌入式实时操作系统 uC/OS - II原理及应用(第3版)》,已经看过一遍了,下面可以从线程开始,接着任务间同步和通信,内存管理这样一块一块的看,看一点实践一点
  • 手里还有邵贝贝的翻译的《嵌入式实时操作系统uC/OS - II(第2版)》,大家都说这本书比较权威,但可能不太适合初学者,我觉得到现在也基本过了初学者的阶段了,应该可以系统地看一遍,慢慢找寻知识间的联系,估计最终能明白什么叫完完整整搞懂uC/OS - II
  • 对于怎样才算会用,我觉得,其实不管RTT也好,uC/OS - II也好,只要能实现自己想要的功能的,就算是会用了。而我阅读源码,只是为了懂的更深一点,心里总是觉得一些东西不搞懂总归会有一天被绊倒,也不知道对不对,按照马斯洛层次需求理论,可能我处在自我实现的需要这一阶段吧。

4. 什么叫做移植

为特定的处理器编写特定代码的过程成为“移植”1

5. 临界段

在阅读c语言教程时经常会碰到临界段这个概念,从字面上一直不好理解。查阅了英文资料,临界段的英文原文叫做Critical Section或者Critical Region,意思是特别重要的、不能被中断打断执行的代码段。所以一般把关中断的操作叫做进入临界段,开中断的操作叫做退出临界段2

6. 上下文切换

上下文切换一词在操作系统中经常提到,对应英文为Context Switches,本质上是指CPU寄存器内容的切换,实际效果对应于系统运行任务的切换3

7. CPU利用率

作为系统设计的一条原则,CPU的利用率应该小于60%-70%4

8. 任务

操作系统中,任务通常是无限循环5,形如:

void YourTask(void *pdata)
{
    for (;;)
    {
        /** 用户操作 */
        do something...
        /** 调用系统函数 */
        OSTimeDly(1);
    }
}

9. OSUnMapTbl表的由来

在uC/OS - II中,使任务进入就绪状态的代码如下:

OSRdyGrp          |= OSMapTbl[prio>>3];
OSRdyTbl[prio>>3] |= OSMapTbl[prio&0x07];

其中,OSMapTbl[]数组内容如下:

下标位掩码(二进制)
000000001
100000010
200000100
300001000
400010000
500100000
601000000
710000000

而使得任务脱离就绪状态的代码如下:

if ((OSRdyTbl[prio>>3] &= ~OSMapTbl[prio&0x07]) == 0)
{
    OSRdyGrp &= ~OSMapTbl[prio>>3];
}

在获取当前优先级最高的任务时,为了保证系统的实时性,即不管最高优先级任务在哪,系统查询到最高优先级任务所花费的时间要固定。因此不能采用常规的循环查询的方法,此处,uC/OS - II采用了直接查表的方法,每次都能以相同的时间获取最高优先级任务。原理6如下:

1. 先搞个例子感受下

某一时刻OSRdyGrp的值为0b00010001,而此时OSRdyTbl[]中的值为:

OSRdyTbl[0] = 0b00000110;
OSRdyTbl[4] = 0b00010000;

则时刻系统中就绪任务优先级如下:

0 * 8 + 1 = 1;
0 * 8 + 2 = 2;
4 * 8 + 4 = 36;

即系统中的优先级为1,2,36

2. 分析

在上述例子中,假设OSRdyGrp二进制表示下最低位为1的位置为YOSRdyTbl[Y]最低位为1的位置为X,则系统中最高优先级为:

prio = Y * 8 + X;

因此,要想求得系统最高优先级任务,只需把0x00 - 0xFFOSRdyGrpOSRdyTbl[i]都是8位二进制数,共有0x00 - 0xFF种二进制表示)二进制表示的最低位为1的位置列成表就行:

INT8U  const  OSUnMapTbl[256] =
{
    0u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x00 to 0x0F */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x10 to 0x1F */
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x20 to 0x2F */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x30 to 0x3F */
    6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x40 to 0x4F */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x50 to 0x5F */
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x60 to 0x6F */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x70 to 0x7F */
    7u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x80 to 0x8F */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x90 to 0x9F */
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xA0 to 0xAF */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xB0 to 0xBF */
    6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xC0 to 0xCF */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xD0 to 0xDF */
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xE0 to 0xEF */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u  /* 0xF0 to 0xFF */
};

最终,求最高优先级任务代码如下:

Y = OSUnMapTbl[OSRdyGrp];
X = OSUnMapTbl[OSRdyTbl[Y]];
prio = Y * 8 + X;

10. 为何要看操作系统?

看懂一个操作系统,有以下好处:

  • 在开发时能够对系统的特性了解的更深,方便功能的实现
  • 学习高手写代码的思路
  • 在使用同类操作系统时能够很快上手

11. 何时开关中断?

当中断程序可能会改变一段代码中用到的某些变量的值的时候,就需要关中断来保证代码执行的正确性。执行完这段代码后要立即开中断保证系统对异步事件的处理速度(实时性)。上述的那段代码叫做临界段。

12. 2015-04-23

感觉要自己写出汇编部分的代码还是比较困难

参考资料


  1. 邵贝贝《嵌入式实时操作系统uCOS-II(第2版)》1.04 范例4
  2. 邵贝贝《嵌入式实时操作系统uCOS-II(第2版)》2.01 代码的临界段
  3. 邵贝贝《嵌入式实时操作系统uCOS-II(第2版)》2.06 任务切换
  4. 邵贝贝《嵌入式实时操作系统uCOS-II(第2版)》2.17 任务优先级分配
  5. 邵贝贝《嵌入式实时操作系统uCOS-II(第2版)》3.01 任务
  6. 解读uCos中优先级判定表OSUnMapTbl原理
  u C / O S 是一种公开源代码、结构小巧、具有可剥夺实时内核的实时操作系统,商业应用需要付费。   μC/OS-II 的前身是μC/OS,最早出自于1992 年美国嵌入式系统专家Jean J.Labrosse 在《嵌入式系统编程》杂志的5 月和6 月刊上刊登的文章连载,并把μC/OS 的源码发布在该杂志的B B S 上。   用户只要有标准的ANSI 的C交叉编译器,有汇编器、连接器等软件工具,就可以将μC/OS-II嵌人到开发的产品中。μC/OS-II 具有执行效率高、占用空间小、实时性能优良和可扩展性强等特点, 最小内核可编译至 2KB 。μC/OS-II 已经移植到了几乎所有知名的CPU 上。   严格地说uC/OS-II只是一个实时操作系统内核,它仅仅包含了任务调度,任务管理,时间管理,内存管理和任务间的通信和同步等基本功能。没有提供输入输出管理,文件系统,网络等额外的服务。但由于uC/OS-II良好的可扩展性和源码开放,这些非必须的功能完全可以由用户自己根据需要分别实现。   uC/OS-II目标是实现一个基于优先级调度的抢占式的实时内核,并在这个内核之上提供最基本的系统服务,如信号量,邮箱,消息队列,内存管理,中断管理等。 uC/OS-II工作原理   uC/OS-II是一种基于优先级的可抢先的硬实时内核。   要实现多任务机制,那么目标CPU必须具备一种在运行期更改PC的途径,否则无法做到切换。不幸的是,直接设置PC指针,目前还没有哪个CPU支持这样的指令。但是一般CPU都允许通过类似JMP,CALL这样的指令来间接的修改PC。我们的多任务机制的实现也正是基于这个出发点。事实上,我们使用CALL指令或者软中断指令来修改PC,主要是软中断。但在一些CPU上,并不存在软中断这样的概念,所以,我们在那些CPU上,使用几条PUSH指令加上一条CALL指令来模拟一次软中断的发生。   在uC/OS-II里,每个任务都有一个任务控制块(Task Control Block),这是一个比较复杂的数据结构。在任务控制快的偏移为0的地方,存储着一个指针,它记录了所属任务的专用堆栈地址。事实上,再uC/OS-II内,每个任务都有自己的专用堆栈,彼此之间不能侵犯。这点要求程序员再他们的程序中保证。一般的做法是把他们申明成静态数组。而且要申明成OS_STK类型。当任务有了自己的堆栈,那么就可以将每一个任务堆栈再那里记录到前面谈到的任务控制快偏移为0的地方。以后每当发生任务切换,系统必然会先进入一个中断,这一般是通过软中断或者时钟中断实现。然后系统会先把当前任务的堆栈地址保存起来,仅接着恢复要切换的任务的堆栈地址。由于哪个任务的堆栈里一定也存的是地址(还记得我们前面说过的,每当发生任务切换,系统必然会先进入一个中断,而一旦中断CPU就会把地址压入堆栈),这样,就达到了修改PC为下一个任务的地址的目的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值