simplorer 发表于 2010-3-19 17:15:03

问一个有关堆栈的问题,我已经考虑了一天,还是没头绪,希望论坛的朋友可以给我解惑

这几天在阅读uC/OS II,2.52版本的源码,已经进行到了任务切换这一块,我现在知道了uC/OS II是利用软件模拟中断的方式实现任务的调度,中断进入后就是压栈操作,然后弹出就绪表中优先级最高的任务的堆栈地址等,完成任务切换,这些都可以理解!
我现在不明白的地方是,中断发生的时候,CPU如何确定要把相关的寄存器压栈到什么地址,也就是说中断发生时,CPU怎么知道这个任务堆栈的SP指向了何处,软硬件是如何联系起来的。
书籍上一般介绍中断的时候,都是说要执行压栈操作,但是没有详细说明,堆栈到底位于何处,地址如何确定。
这个问题困扰了一整天了,还是没头绪,发帖询问!请大家给提供一下线索。

voidx 发表于 2010-3-19 17:57:57

每个任务都有自己的堆栈

任务切换出去的时候,保护上下文环境,并把上下文存储的地址 赋值给一个特殊变量(这里我称为上下文出入口),这个上下文出入口是存放在任务控制块里面。

通常,上下文保护的时候,都存储到堆栈里面了,上下文出入口的值就是SP的值。

当A任务切换到B任务:
A任务保护当前上下文到堆栈里,
记录此时的sp值到任务A控制块里面的上下文出入口,
从就需列表取出任务B的控制块
从B任务控制块里面的上下文出入口的值赋值给SP
从堆栈恢复任务B的上下文,
任务B继续运行

Gorgon_Meducer 发表于 2010-3-19 18:04:57

发生中断的时候,PC指针的位置信息会被自动压到栈中,这个栈是CPU硬件实现的,简单说是通过一个
SP指针实现的。用户对这个这个硬件栈的操作 通过 pop和push两个汇编语句来实现。

simplorer 发表于 2010-3-19 18:29:55

回复【1楼】voidx
每个任务都有自己的堆栈
-----------------------------------------------------------------------
这个我理解,任务控制块的第一个变量就是指向这个堆栈的指针

任务切换出去的时候,保护上下文环境,并把上下文存储的地址 赋值给一个特殊变量(这里我称为上下文出入口),这个上下文出入口是存放在任务控制块里面。
-----------------------------------------------------------------------
我看了下任务控制块的定义,没找到哪个变量时存储这个地址的,可否详细说明?

通常,上下文保护的时候,都存储到堆栈里面了,上下文出入口的值就是SP的值。
当A任务切换到B任务:
A任务保护当前上下文到堆栈里,
记录此时的sp值到任务A控制块里面的上下文出入口,
从就需列表取出任务B的控制块
从B任务控制块里面的上下文出入口的值赋值给SP
从堆栈恢复任务B的上下文,
任务B继续运行
-----------------------------------------------------------------------
寄存器压栈之类的操作我可以理解,下面是中断的一段代码
_OSCtxSw    PROC   FAR
;
            PUSHA                                  ; Save current task's context
            PUSH   ES                              ;
            PUSH   DS                              ;
;
            MOV    AX, SEG _OSTCBCur               ; Reload DS in case it was altered
            MOV    DS, AX                        ;
;
            LES    BX, DWORD PTR DS:_OSTCBCur      ; OSTCBCur->OSTCBStkPtr = SS:SP
            MOV    ES:, SS                   ;
            MOV    ES:, SP                   ;
;
            CALL   FAR PTR _OSTaskSwHook         ; Call user defined task switch hook
;
            MOV    AX, WORD PTR DS:_OSTCBHighRdy+2 ; OSTCBCur = OSTCBHighRdy
            MOV    DX, WORD PTR DS:_OSTCBHighRdy   ;
            MOV    WORD PTR DS:_OSTCBCur+2, AX   ;
            MOV    WORD PTR DS:_OSTCBCur, DX       ;
;
            MOV    AL, BYTE PTR DS:_OSPrioHighRdy; OSPrioCur = OSPrioHighRdy
            MOV    BYTE PTR DS:_OSPrioCur, AL      ;
;
            LES    BX, DWORD PTR DS:_OSTCBHighRdy; SS:SP = OSTCBHighRdy->OSTCBStkPtr
            MOV    SS, ES:                   ;
            MOV    SP, ES:                     ;
;
            POP    DS                              ; Load new task's context
            POP    ES                              ;
            POPA                                 ;
;
            IRET                                 ; Return to new task
;
_OSCtxSw    ENDP

可以看到代码上来就指向PUSHA(通用寄存器压栈),问题是,这个时候CPU是如何知道SP指向何处了?

simplorer 发表于 2010-3-19 18:34:37

回复【2楼】Gorgon Meducer傻孩子
发生中断的时候,PC指针的位置信息会被自动压到栈中,这个栈是CPU硬件实现的,简单说是通过一个
SP指针实现的。用户对这个这个硬件栈的操作 通过 pop和push两个汇编语句来实现。
-----------------------------------------------------------------------

执行POP,PUSH,PUSHA,POPA之类的操作,我理解,只要注意出入堆栈的顺序就可以了。
问题是这个SP指针式通过什么方式指向这个任务的堆栈呢?因为uC/OS II是多任务系统,任务之间可能需要来回切换,CPU用什么方式才能找到属于每个任务的堆栈,并指向正确的SP呢?
这个堆栈中不仅保存寄存器,还可能有一些局部变量,所以说每次指向的时候,SP的值很可能会不同。

simplorer 发表于 2010-3-19 18:41:21

我说的更通俗一点,大家都知道中断后要压栈,这个堆栈应该是一段连续的RAM空间,按照设计意图(实际也是这么实现的,只是暂时我不理解),当特定任务被中断时,它的堆栈起始地址都是确定的,只是SP值可能不同。
我的问题是,中断的硬件操作是如何确定这个地址的?如果我定义一个变量i,如果是全局变量,编译完成后,就可以通过i这个标志,操作i变量被分配的存储空间。也就是说通过i可以找到这个空间。那么uC/OS II程序是如何实现的,每个任务被中断时,都能找到自己的堆栈?

TBN1 发表于 2010-3-19 19:45:20

王小二的任务执行的时候,SP这个寄存器被赋予了王小二任务堆栈的栈顶值,所以中断来的时候,自然是压在王小二的任务堆栈里了。

simplorer 发表于 2010-3-19 19:56:36

谢谢楼上各位,重新阅读了源码,这回明白了,由于任务堆栈都是使用了全局变量定义,所以堆栈的地址在main()之间已经被编译器分配好了,每个任务的创建都会调用OSTaskStkInit函数,这个函数会把SP的地址保存起来,并赋值给TCB的OSTCBStkPtr变量,这样SP的值就被每个任务保存了起来,任务切换的时候,只要弹出这个值就可以了。

voidx 发表于 2010-3-19 21:20:31

OSTCBCur->OSTCBStkPtr;

这就是上下文出入口。

simplorer 发表于 2010-3-22 14:18:16

回复【8楼】voidx
OBCur->OBStkPtr;
这就是上下文出入口。

-----------------------------------------------------------------------

谢谢关注,OSTCBStkPtr确实是保存了当前SP的指针地址。

ba_wang_mao 发表于 2010-3-22 15:31:46

1.假定系统初始化时,会创建"王小二","张小三","李小四"三个任务.

2.首先创建"王小二"任务时,调用堆栈初始化时,就会记录 "王小二"任务的堆栈栈顶指针到任务控制的成员中.
3.首先创建"张小三"任务时,调用堆栈初始化时,就会记录 "张小三"任务的堆栈栈顶指针到任务控制的成员中.
4.首先创建"李小四"任务时,调用堆栈初始化时,就会记录 "李小四"任务的堆栈栈顶指针到任务控制的成员中.


5.当准备从"王小二"切换到","张小三"时
    由于堆栈指针SP已经指向了 "王小二"的堆栈区域,因此切换时自然是压入了"王小二"的任务堆栈里.

ba_wang_mao 发表于 2010-3-22 15:35:39

uC/OS-II用来记录任务的堆栈指针,任务的当前状态,任务的优先级等一些与任务管理有关的属性表,就叫做任务控制块(OS_TCB)

l19830312 发表于 2010-4-1 15:23:28

你应该问中断堆栈的位置,任务栈理解很简单,通过每个任务控制块定位任务栈。
以ARM7为例有自己的IRQ栈,当IRQ发生时直接压到IRQ栈中,这个栈的大小关系到嵌套的层数。

simplorer 发表于 2010-4-8 17:09:13

uCOS中的堆栈是需要自己指定的,而不是使用硬件堆栈,拿DSP F2812为例,每个堆栈必须分配空间,然后在CMD文件中映射,现在,通过跟踪源码,我已经理解了任务堆栈的保存过程。

wukaka 发表于 2010-7-23 23:11:00

谢谢各位的讲解,我也是这个问题不理解。

bsz84 发表于 2010-7-24 15:56:01

学习了

wangff2531 发表于 2010-10-22 17:30:05

学习了

wantao0916 发表于 2010-10-23 15:01:51

学习~

xizi 发表于 2010-10-25 03:22:31

谢谢楼主自己的回答,也帮我搞明白了这个问题。看了别人的回答总是像在雾里,似乎都没搞清楚楼主的疑问在哪里。

xph114 发表于 2011-3-18 22:20:38

mark

qt_girl 发表于 2011-3-22 19:06:51

关注中......

blavy 发表于 2012-5-5 12:01:30

{:lol:}学习,收藏。

shenmo2012 发表于 2012-5-5 12:13:28

追根溯源,才能知其所以然,这样的帖子好。
页: [1]
查看完整版本: 问一个有关堆栈的问题,我已经考虑了一天,还是没头绪,希望论坛的朋友可以给我解惑