⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 uc-os-ii任务栈处理的一种改进方法--电子门.htm

📁 ucos的资料以及单片机dsp的经典移植程序
💻 HTM
📖 第 1 页 / 共 3 页
字号:
        <TR>
          <TD><IMG height=53 alt="" 
            src="uC-OS-II任务栈处理的一种改进方法--电子门.files/index_05.jpg" 
        width=752></TD></TR></TBODY></TABLE>
      <TABLE class=content3 cellSpacing=0 cellPadding=0 width="100%" border=0>
        <TBODY>
        <TR>
          <TD width="3%">&nbsp;</TD>
          <TD vAlign=top>
            <DIV id=topMenu>
            <DIV id=subject_l>MCU博客数据载入中, 请稍候...</DIV>
            <SCRIPT type=text/javascript><!--
google_ad_client = "pub-0269824239044964";
google_ad_width = 468;
google_ad_height = 15;
google_ad_format = "468x15_0ads_al_s";
google_ad_channel = "";
//-->
</SCRIPT>

            <SCRIPT src="uC-OS-II任务栈处理的一种改进方法--电子门.files/show_ads.js" 
            type=text/javascript>
</SCRIPT>
            </DIV></TD>
          <TD width="3%">&nbsp;</TD></TR></TBODY>
        <SCRIPT type=text/javascript><!--
google_ad_client = "pub-0269824239044964";
google_ad_width = 468;
google_ad_height = 15;
google_ad_format = "468x15_0ads_al_s";
google_ad_channel = "";
//-->
</SCRIPT>

        <SCRIPT src="uC-OS-II任务栈处理的一种改进方法--电子门.files/show_ads.js" 
        type=text/javascript>
</SCRIPT>
      </TABLE>
      <TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
        <TBODY>
        <TR>
          <TD vAlign=top width=509>
            <DIV id=content>
            <TABLE height=13 cellSpacing=0 cellPadding=0 width=490 align=center 
            border=0>
              <TBODY>
              <TR>
                <TD>
                  <TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
                    <TBODY>
                    <TR>
                      <TD>
                        <TABLE class=seyle4 cellSpacing=0 cellPadding=0 
                        width="100%" border=0>
                          <TBODY>
                          <TR>
                            <TD>&nbsp;</TD></TR>
                          <TR>
                            <TD class=content9 vAlign=center 
                              bgColor=#bbccde><STRONG>uC/OS-II任务栈处理的一种改进方法</STRONG></TD></TR>
                          <TR>
                            <TD>
                              <DIV align=right><SPAN class=textbox-label>[ 
                              2006-4-25 16:40:11 | By: <SPAN 
                              class=style3>电子门</SPAN> 
                          ]</SPAN></DIV></TD></TR></TBODY></TABLE>
                        <TABLE cellSpacing=0 cellPadding=0 width="100%" 
border=0>
                          <TBODY>
                          <TR>
                            <TD height=3>&nbsp;</TD></TR></TBODY></TABLE>
                        <TABLE style="TABLE-LAYOUT: fixed" cellSpacing=0 
                        cellPadding=0 width="100%" align=center border=0>
                          <TBODY>
                          <TR>
                            <TD><SPAN class=oblog_text><SPAN 
                              id=ob_logd10724></SPAN><FONT 
                              face=宋体><B>摘要:</B></FONT><SPAN 
                              class=p4>在uC/OS-II内核中,各个不同的任务使用独立的堆栈空间,堆栈的大小按每个任务所需要的最大堆栈深度来定义,这种方法可能会造成堆栈空间浪费。本文叙述如何在RTOS中多个任务共用连续存储空间作为任务栈的方法,并详细比较二者的优缺点和适用性。</SPAN> 

                              <P class=MsoNormal><SPAN class=p4 
                              style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">&nbsp;&nbsp;&nbsp; 
                              <B>关键词:</B></SPAN><SPAN class=p4>uC/OS-II 任务堆栈 
                              RTOS 共用空间堆栈</SPAN></P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">关于uC/OS-II这个实时内核及其应用已经有很多文章介绍了,对于学习RTOS的人来说,这个系统是很好的学习起点。虽然文献[1]的源代码没有行号和函数名交叉索引表等,给源代码阅读造成一些困难(可使用BC31的grep查找功能,提高阅读效率),好在代码不是很长,前面又有详细的中文说明,对于有一定X86汇编和C语言基础的人来说,仍然可以在不长的时间内掌握。</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">uC/OS-II内核是一个抢先式内核,可以进行任务间切换,也可以让一个任务在得不到某个资源时休眠一定时间后再继续运行;提供了用于共享资源管理的信号灯,用于进程通信的消息队列和邮箱,甚至提供了存储器管理机制,一个比较全面的系统。</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">uC/OS-II内核有些地方仍然值得改进,比如该系统不支持时间片调度。如果有一个任务中一段死循环代码(或者条件循环代码),代码就会永远(或长时间)在此处执行,调度程序无法控制,其它任务也就是不到及时执行。这种抢先式实际上和非抢先式系统存在着同样问题。当然,如果这种代码不一个BUG,问题是可以解决的,在不提供时间片调度的抢先式系统中,一般采取信号灯,或者任务主动休眠的方法(对于uC/OS-II,很容易改造成支持时间片调度,只要在定时中断服务程序调用OSIntCtxSw()函数即可);非抢先式系统一般采取有限状态机方法,不使用这种耗时很长的循环代码。不过,无论如何,对RTOS的使用者来说,这毕竟会使得任务函数的编码不能随心所欲。</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">uC/OS-II内核的另外一个值得改进的地方就是其任务栈管理方法。在uC/OS-II内核中,各个不同的任务使用独立的堆栈空间,堆栈的大小按每个任务所需要的最大堆栈深度来定义,这种方法可能会造成堆栈空间的浪费。下面讨论如何在RTOS中多个任务共用一段连续存储空间作为傻堆栈。<B><BR><IMG 
                              onmousewheel="return bbimg(this)" 
                              style="CURSOR: pointer" 
                              onclick=javascript:window.open(this.src); 
                              src="uC-OS-II任务栈处理的一种改进方法--电子门.files/5a.gif" 
                              onload=rsimg(this,300)><BR>1 任务切换要保存的数据</B></P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">简单地说,一个任务可看作一个运行中的C函数。对于抢先式RTOS来说,在任务切换时,应保存当前任务的各种现场数据。现场数据包括局部变量、各个CPU寄存器、堆栈指针和程序被中止的任务指针。CPU寄存器是任何任务代码均会用到的;而局部变量,一般的编译器是将其它安排在堆栈空间中,堆栈指针也是各任务公用的,所以也需要保存。</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">对于全局变量,由于一般是在内存中的固定位置,各任务所占用的空间完全独立,所以不需要保存。</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">在X86环境中,要保存的CPU寄存器共14个16位寄存器;通用寄存器8个(AX、BX、CX、DX、SP、BP、SI、BI)、段寄存器4个(CS、DS、ES、SS)以及指令指针IP和标志寄存器FR各1个。</P>
                              <P class=p4 style="TEXT-INDENT: 0px"><B>2 
                              C编译器中变量在堆栈中的位置</B></P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">对于一个存在函数调用嵌套的C程序来说,大部分编译器将传递的参数和函数本身的局部变量放在了堆栈中,编译器会自动生成压栈(push)和弹栈(pop)代码,以保存上级函数的运行寄存器。</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">假设函数main()调用funl(),而funl()调用fun2(),则在执行fun2()中的代码时,堆栈映像如图1所示(X86 
                              CPU的情况)。</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">对于RTOS软件,堆栈中的各种数据就是一个任务的作现场。一般CPU的堆栈指针SP只有一个,在进行任务切换时,必须将挂起任务所使用的堆栈内容保存起来,以便使该任务在下次唤醒时能从原地继续运行。</P>
                              <P class=p4 style="TEXT-INDENT: 0px"><B>3 
                              uC/OS-II对任务栈的处理方法与缺陷</B></P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">uC/OS-II为了保存任务堆栈中的数据,对每个任务定义一个数组变量作为堆栈,在任务切换时,将CPU堆栈指针SP指向该数组中的某个元素,即栈顶,如图2所示。</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">比如,在其ex21.c文件中定义的任务堆栈语句为:</P>
                              <P class=p4 style="TEXT-INDENT: 30px">OS_STK 
                              TaskStartStk[TASK_STK_SIZE]; /*启动任务堆栈*/</P>
                              <P class=p4 style="TEXT-INDENT: 30px">OS_STK 
                              TaskClkStk[TASK_STK_SIZE]; /*时钟任务堆栈*/</P>
                              <P class=p4 style="TEXT-INDENT: 30px">OS_STK 
                              TasklStk[TASK_STK_SIZE]; /*任务1#,任务堆栈*/</P>
                              <P class=p4 style="TEXT-INDENT: 30px">……</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">以上各任务堆栈数组变量在初始化函数OSTCBInit()中被会给了任务控制块OS_TCB的OSTCBStkPtr变量。在任务切换时,uC/OS-II调用OSCtxSw汇编过程(OS_CPU_A.ASM文件),将CPU的SP指针指向该变量,从而使每个任务使用独立的任务堆栈。</P>
                              <P class=p4 style="TEXT-INDENT: 30px">LES BX,DWORD 
                              PTR DS:_OSTCBCur</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">;保存挂起任务的堆栈指针SP</P>
                              <P class=p4 style="TEXT-INDENT: 30px">MOV 
                              ES:[BX+2],SS</P>
                              <P class=p4 style="TEXT-INDENT: 30px">MOV 
                              ES:[BX+0],SP</P>
                              <P class=p4 style="TEXT-INDENT: 30px">……</P>
                              <P class=p4 style="TEXT-INDENT: 30px">LESB X,DWORD 
                              PTR DS:_OSTCBHighRdy ;切换SP到要运行任务的堆栈空间</P>
                              <P class=p4 style="TEXT-INDENT: 30px">MOV 
                              SS,ES:[BX+2]</P>
                              <P class=p4 style="TEXT-INDENT: 30px">MOV 
                              SP,ES:[BX]</P>
                              <P class=p4 style="TEXT-INDENT: 30px">……<BR><IMG 
                              onmousewheel="return bbimg(this)" 
                              style="CURSOR: pointer" 
                              onclick=javascript:window.open(this.src); 
                              src="uC-OS-II任务栈处理的一种改进方法--电子门.files/5b.gif" 
                              onload=rsimg(this,300)><BR>&nbsp;&nbsp;&nbsp; 
                              在代码中,变量OSTCBHighRdy(OSTCBCur)和堆栈指针变量OSTCBStkPtr的数值是同同的,因为OSTCBStkPtr是结构OSTCBHighRdy的第一个变量。</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">这种任务栈处理方法的缺点是可能造成空间的浪费。因为一个任务如果堆栈满了,该任务也就无法运行,即使其它任务的堆栈还有空间可用。当然,这种方法的好处是任务栈切换的时间非常短,只需要几条指令。</P>
                              <P class=p4 style="TEXT-INDENT: 0px"><B>4 
                              共用空间的堆栈处理方法</B></P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">(1)栈共用连续存储空间</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">如果多个任务使用同一段连续空间作为堆栈,这样各个堆栈之间就可以互补使用。在前面说过,共用空间的问题在于一个任务运行时不能破坏其它任务的堆栈数据。为简单起见,先看图3所示两个任务的情况。</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">假定任务1首次运行时任务栈为空。运行一段时间后任务2运行,堆栈空间继续往上生长。这次任务切换不需要修改CPU的SP数值,但需要记下任务1的栈顶位置SP1(图3中)。</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">在任务2运行一段时间后,RTOS又切换到任务1运行。在切换时,不能简单地将SP指针修改回SP1的数值,因为这样堆栈向上生长时会破坏任务2堆栈中的数据。办法是将原来任1务堆栈保存的数据移动到靠栈顶的位置,而将任务2堆栈数据下移到靠栈底的位置,堆栈指针SP实际上不需要修改(图3右)。</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">考虑到更为一般的情况,有N个任务,当前运行的任务为k,下一个运行的任务为j,在共用任务堆栈时必须做的工作有:</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">*为每个任务定义栈顶和栈底2个堆栈指针;</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">*在任务切换时,将待运行任务j的堆栈内容移动到靠栈顶位置,同时将其堆栈上方的任务堆栈下移,修改被移动推栈的任务堆栈指针。</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">假设我们定义的任务栈空间和任务的栈指针变量为:</P>
                              <P class=p4 style="TEXT-INDENT: 30px">void 
                              TaskSTK[MAX_STK_LEN];/*任务堆栈空间*/</P>
                              <P class=p4 style="TEXT-INDENT: 30px">typedef 
                              struct TaskSTKPoint{</P>
                              <P class=p4 style="TEXT-INDENT: 30px">int 
                              TaskID;</P>
                              <P class=p4 style="TEXT-INDENT: 30px">int 
                              pTopSTK;</P>
                              <P class=p4 style="TEXT-INDENT: 30px">int 
                              pBottomSTK;</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">}TASK_STK_POINT;</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">TASK_STK_POINT 
                              pTaskSTK[MAX_TASK_NUM]; /*存放每个任务的栈顶和栈底指针*/</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">任务栈指针数组pTaskSTK的元素个数同任务个数。为了堆栈交换,需要另外一块临时存储空间,其大小可按单个任务栈最大长度定义,用于中转堆栈交换的内容。堆栈内容交换的伪C算法可写为:</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">StkEechange(int 
                              CurTaskID,int RunTaskID)</P>
                              <P class=p4 style="TEXT-INDENT: 30px">{ 
                              /*2个参数为当前运行任务号和下一运行任务号*/</P>
                              <P class=p4 style="TEXT-INDENT: 30px">void 
                              TempSTK[MAX_PER_STK_LEN]; 
/*注意该变量长度可小于TaskSTK*/</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">L=任务RunTaskTD的堆栈长度;</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">①将TaskSTK顶部的L字节移动到TempSTK中;</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">②将RunTaskID任务的堆栈内容移动到TaskSTK顶部;</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">③将RunTaskID堆栈上方(移动前位置)所有内容下移L个字节;</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">④修改RunTask堆栈上方(移动前位置)所有任务栈顶和栈底指针(pTaskSTK变量);</P>
                              <P class=p4 style="TEXT-INDENT: 30px">};<IMG 
                              onmousewheel="return bbimg(this)" 
                              style="CURSOR: pointer" 
                              onclick=javascript:window.open(this.src); 
                              src="uC-OS-II任务栈处理的一种改进方法--电子门.files/5c.gif" 
                              onload=rsimg(this,300)></P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">该算法的平均时间复杂度可计算如下:</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">O(T)=SL/2+SL/2+SL×N/2</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">式中,第一、二项为步骤①和步骤②时间,第三项为步骤③时间;SL表示每个任堆栈的最大长度(即MAX_PER_STK_LEN),N表示任务数。</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">取SL为64字节,任务数为16个,则数据项平均移动次数为576。假设每次移动指令时间为2μs,则一次任务栈移动时间长达约1ms。所以在使用该方法时,为了执行时间尽量短,编码时应仔细推敲。</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">从空间上说,共用任务栈比独立任务栈优越。假设独立任务栈方法中每个堆栈空间为K,任务数为N,则独立任务栈方式的堆栈总空间为N×K。在共用任务栈时,考虑各任务互补的情况,TaskSTK变量不需要定义为N×K长度,可能定义为二分之一或者更小就可以了。</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">另外,这种方法不需要在任务切换时修改CPU的SP指针。</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">(2)工作栈和任务堆栈</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">上节共用任务栈算法的缺点是:任务切换时的堆栈内容交换算法复杂,占用时间长。另外一个折中的方法是设计一个工作堆栈,用于给当前运行的任务使用;在任务切换时,将工作栈内容换出得另外的存储空间,该空间可以动态申请,其大小按实际需要即可。</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">这种方法看起来和独立任务栈的方法类似,需要N+1块存储空间,其中一块用于工作栈空间。和独立任务堆栈相比,其区别有2点:</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">①SP指针所指向的空间始终是同一块存储空间,即工作栈;</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">②每个任务栈的大小不需要按最大空间定义,可以动态按实际大小从内存中分配空间。</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">对于8031这种处理器结构,由于堆栈指针只能指向其内部存储器,大小十分有限。采取这种方法,可将工作栈设在内部RAM,将任务栈设在外部RAM,扩展了堆栈空间。</P>
                              <P class=p4 
                              style="TEXT-INDENT: 30px">和上一种共用堆栈方法相比,这种方法的交换时间要短,其时间复杂度约为1.5倍最大任务栈长度。</P>
                              <P class=p4 style="TEXT-INDENT: 0px"><B>5 
                              总结</B></P>
                              <P class=p4 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -