为什么说Ucosii是可剥夺型内核?

2023-10-12 20:10
文章标签 内核 ucosii 剥夺

本文主要是介绍为什么说Ucosii是可剥夺型内核?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

为什么说Ucosii是可剥夺型内核?
因为它不仅可以实现高优先级任务可以剥夺低优先级任务的CPU使用权,还可以实现低优先级任务也可以剥夺高优先级任务的CPU使用权。
首先,在ucosii中,任务只有5种状态,分别是睡眠状态、就绪状态、运行状态、等待状态、中断状态。其次,在ucosii中有两种调度器:一种是任务级调度器OS_Sched。在OS_Sched中,会先查找优先级最高的任务,接着调用OSCtxSw实现任务切换,OSCtxSw由汇编语言实现。另一种是中断级调度器OSIntExit。在OSIntExit中,也是会先查找优先级最高的任务,接着调用OSIntCtxSw实现任务切换,OSIntCtxSw通常位于OS_CPU_A.ASM中,由汇编语言实现。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
先说高优先级任务可以剥夺低优先级任务的CPU使用权。 它是怎么实现这一点的呢?依靠ucosii的时钟中断来实现。ucosii与大多数计算机系统一样,用硬件定时器产生一个周期为毫秒级的周期性中断来实现系统时钟。两次中断之间相隔的时间就是最小时钟单位,称为时钟节拍。硬件定时器以时钟节拍为周期定时产生中断。系统接收到时钟中断请求后,这时硬件层面的CPU正常情况下都是处于中断允许状态的,系统会通过强行中止当前正在运行的任务并跳转至中断服务子程序OSTickISR(题外话:以51单片机为例,中断响应的过程首先是由硬件自动生成一条长调用指令“LCALL addr16”,紧接着就由CPU执行该指令。这里的addr16就是程序存储区中相应的中断入口地址,对应这里的OSTickISR。),被中止的任务便处于中断状态。在OSTickISR中,首先保存CPU的内容至被中断的任务的任务堆栈,接着调用时钟节拍服务函数OSTimeTick。OSTimeTick不会做任务调度,它只会做两件事:一是给计数器OSTime加1;二是了解每个任务的延时状态,使已经到了延时时限的非挂起的任务进入就绪状态。OSTimeTick完事返回OSTickISR后,接着调用OSIntExit做任务调度(注意:OSIntExit做任务调度时,不一定会做任务切换。),之后中断返回,退出了OSTickISR。这时候,系统却不一定会继续运行之前被中断的任务,而是根据OSIntExit任务调度的结果有可能去运行优先级更高的任务。说白了,这是借助硬件层面的硬件定时器的时钟中断对CPU进行强行剥夺,再通过时钟中断服务子程序OSTickISR来实现软件层面对任务的CPU使用权的剥夺。这种情形可以理解为顺优先级剥夺。
在这里插入图片描述
在这里插入图片描述

这里需要强调一点:除了时钟中断OSTickISR,ucosii还存在其他类型的中断(比如OSCtxSw和OSIntCtxSw也是中断服务程序,这点要非常重视!),而且中断之间可以相互嵌套,称之为中断嵌套。在编写ucosii的中断服务程序时,按照官方的要求是要用到两个重要的函数OSIntEnter和OSIntExit。其中,OSIntEnter的作用是把全局变量OSIntNesting加1,从而用它来记录中断嵌套的层数。OSIntEnter的调用通常发生在中断服务程序(如OTTickISR)保护了中断任务的断点数据(也就是CPU相关寄存器的内容存入任务堆栈)之后,运行中断服务代码(如OSTimeTick)之前,所以又称之为进入中断服务函数。相对的,OSIntExit被称之为退出中断服务函数。这个函数在中断嵌套层数为0、调度器未被锁定且从任务就绪表中查找到的最高级别就绪任务又不是被中断的任务的情况下需要进行任务切换,否则返回被中断的服务子程序。
在这里插入图片描述
在这里插入图片描述
接着说低优先级任务也可以剥夺高优先级任务的CPU使用权。ucosii可以通过OSTimeDly将高优先级的任务强行转为等待状态,从而让出了CPU,使得低优先级的任务有机会先于高优先级的任务执行。这时用到的就是任务级调度器OS_Sched了。在OSTimeDly中,会调用OS_Sched。OS_Sched内部会先查找优先级最高的任务,然后有可能调用OS_TASK_SW做任务切换。这种情形可以理解为逆优先级剥夺。
重要细节:任务切换时,断点数据是如何被保存和恢复的?
1.断点数据主要包括PC寄存器、Rn寄存器、PSW寄存器的内容,其中PC寄存器的内容又称为断点指针。
2.无论是OSTickISR,还是OSCtxSw和OSIntCtxSw,本质上都是中断服务程序,且OSTickISR在内部执行过程中调用了OSIntCtxSw,它们都是由汇编语言编写的。它们之间的显著区别在于:OSCtxSw内部既有保护断点数据的功能代码,也有恢复断点数据的功能代码,而OSIntCtxSw只有恢复断点数据的功能代码,因此OSTickISR在内部还需要额外编写保护断点数据的功能代码。
3.先来分析在中断服务程序怎么实现将CPU中非常重要的内容保存至被中断的任务的任务堆栈的?以51单片机为例,CPU在执行LCALL指令响应中断时,会自动将断点指针压入堆栈,也就是说断点指针不需要再额外编写指令去保存。至于剩下的,可以借助push指令,比如push r1,r7 to [SP],将CPU通用寄存器的内容保存到任务堆栈。那为什么push指令能将CPU通用寄存器的内容保存到任务堆栈?因为处于运行状态中的任务的任务堆栈就设置在片内RAM中。至于如何将处于运行状态中的任务的任务堆栈就设置在片内RAM中,则是通过具体的汇编指令实现的。
4.同理,在中断返回时,CPU在执行IRET指令或者有相同功能的指令时,能把断点指针自动推入PC寄存器,剩余的数据则用pop指令来恢复,如 pop r1,r7 from [sp],将Rn弹入CPU通用寄存器,具体的汇编指令实现的。
5.ucosii的每一个任务都有一个私有的任务堆栈,通常这个堆栈是放在RAM中的。但遗憾的是,有些单片机处理器的片内RAM的存储空间极其有限,不可能把应用程序中所有任务的任务堆栈同时都放入片内RAM中。但凑巧的是,由于这种处理器只有一个CPU在某个时刻只能运行一个任务,所以只要保证这个处于运行中的任务的任务堆栈存放在片内RAM即可。于是,对于这种片内RAM较小的系统,可以把所有任务的任务堆栈内容都存放在片外RAM,而把片内RAM当做共用堆栈。每当有一个任务被调度器选中时,则在任务切换时把该任务在片外RAM中存储的任务堆栈内容复制到片内RAM中。为了方便起见,把用来存储任务堆栈内容的片外RAM空间称为任务堆栈,而将共用堆栈称为系统堆栈。
以上便是ucosii实现可剥夺的原理。
最后,想学ucosii的,可以将ucosii移植到pc机上进行研究。需要源代码和相关编译器borland的可以从这里下载。欢迎大家来提问!

这篇关于为什么说Ucosii是可剥夺型内核?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/198159

相关文章

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

笔记整理—内核!启动!—kernel部分(2)从汇编阶段到start_kernel

kernel起始与ENTRY(stext),和uboot一样,都是从汇编阶段开始的,因为对于kernel而言,还没进行栈的维护,所以无法使用c语言。_HEAD定义了后面代码属于段名为.head .text的段。         内核起始部分代码被解压代码调用,前面关于uboot的文章中有提到过(eg:zImage)。uboot启动是无条件的,只要代码的位置对,上电就工作,kern

Ubuntu22.04回退系统内核

文章目录 起因回退操作卸载内核禁止内核升级 起因 最近因为系统内核自动升级,导致显卡驱动检测不到,炼丹环境被破坏。无奈只能重装驱动,于是跟着手册操作发现驱动要求的是内核版本是5.15.0-25-generic,而我通过uname -r发现这时候的内核版本是6.8.0-40-generic,看来只能回退了。 我搜索了网上很多的文章,没有一篇文章能够完全解决这个问题,所以在我多次尝

跟我一起玩《linux内核设计的艺术》第1章(四)——from setup.s to head.s,这回一定让main滚出来!(已解封)

看到书上1.3的大标题,以为马上就要见着main了,其实啊,还早着呢,光看setup.s和head.s的代码量就知道,跟bootsect.s没有可比性,真多……这确实需要包括我在内的大家多一些耐心,相信见着main后,大家的信心和干劲会上一个台阶,加油! 既然上篇已经玩转gdb,接下来的讲解肯定是边调试边分析书上的内容,纯理论讲解其实我并不在行。 setup.s: 目标:争取把setup.

编译linux内核出现 arm-eabi-gcc: error: : No such file or directory

external/e2fsprogs/lib/ext2fs/tdb.c:673:29: warning: comparison between : In function 'max2165_set_params': -。。。。。。。。。。。。。。。。。。 。。。。。。。。。。。。。 。。。。。。。。 host asm: libdvm <= dalvik/vm/mterp/out/Inte

linux 内核提权总结(demo+exp分析) -- 任意读写(四)

hijack_modprobe_path篇 本文转自网络文章,内容均为非盈利,版权归原作者所有。 转载此文章仅为个人收藏,分享知识,如有侵权,马上删除。 原文作者:jmpcall 专栏地址:https://zhuanlan.kanxue.com/user-815036.htm     原理同hijack_prctl, 当用户执行错误格式的elf文件时内核调用call_usermod

linux 内核提权总结(demo+exp分析) -- 任意读写(三)

hijack_prctl篇 本文转自网络文章,内容均为非盈利,版权归原作者所有。 转载此文章仅为个人收藏,分享知识,如有侵权,马上删除。 原文作者:jmpcall 专栏地址:https://zhuanlan.kanxue.com/user-815036.htm   prctl函数: 用户态函数,可用于定制进程参数,非常适合和内核进行交互 用户态执行prctl函数后触发prctl系统

linux 内核提权总结(demo+exp分析) -- 任意读写(二)

hijack_vdso篇 本文转自网络文章,内容均为非盈利,版权归原作者所有。 转载此文章仅为个人收藏,分享知识,如有侵权,马上删除。 原文作者:jmpcall 专栏地址:https://zhuanlan.kanxue.com/user-815036.htm     vdso: 内核实现的一个动态库,存在于内核,然后映射到用户态空间,可由用户态直接调用 内核中的vdso如果被修改

linux 内核提权总结(demo+exp分析) -- 任意读写(一)

cred篇 本文转自网络文章,内容均为非盈利,版权归原作者所有。 转载此文章仅为个人收藏,分享知识,如有侵权,马上删除。 原文作者:jmpcall 专栏地址:https://zhuanlan.kanxue.com/user-815036.htm   每个线程在内核中都对应一个线程结构块thread_infothread_info中存在task_struct类型结构体 struct t