有的资料说,ret 和call是配对的,但是我下面的ret怎么没配对的call?
另外,我把ret修改成了的jmp后,程序貌似能调到我要跳的函数去执行,例如我下面那个函数It_Task_001_f01
但是值得一提的是,我用ret执行是正常的,但是用jum替换ret后,printf()好像执行了2次,为什么呢
_asm{
mov ebx, [g_pRunningTask]
mov esp, [ebx] ;得到g_pHighestTask的esp
popad ;恢复所有通用寄存器,共8个
popfd ;恢复标志寄存器
ret ;跳转到指定任务运行 ;
nextstart:
}
VOID It_Task_001_f01()
{
printf("I am Task_001_f01!\n");
// uniTskSuspend(g_TestTaskID01)
......
}
------解决方案--------------------------------------------------------
ret 和 call 的配对,这个是对普通的、规范的子程/函数的程序设计上来说的。
从你这个代码的些名字来看,是任务切换类的功能实现吧。这方面,就有一定的规定和使用要求了。我没有接触过这方面的内容,所以具体的也不了解。从你说的表现来看,应该是 g_pRunningTask 指向任务的环境栈,栈中依次是保存的该任务的通用寄存器、标记寄存器以及返回到任务的 ip 。所以,要将执行权交还给这个任务,就需要在恢复了相应寄存器的内容后,通过 ret 来恢复 ip 以将执行流程切入到该任务。如果是 jmp ,那么该任务的返回 ip 仍然停留在栈里,堆栈出于不平衡的错误状态,必然会被下一个 ret 重新进入。要用 jmp 进行调整,就需要将这个 ip 先出栈,再 jump 过去;当然了,这样做,显然远不如 ret 方便。
------解决方案--------------------------------------------------------
我直接给你复制粘贴:
根据段内和段间、有无参数,RET分成4种类型
RET ;无参数段内返回
RET i16 ;有参数段内返回
RET ;无参数段间返回
RET i16 ;有参数段间返回
需要弹出CALL指令压入堆栈的返回地址
段内返回——出栈偏移地址IP
IP←SS:[SP], SP←SP+2
段间返回——出栈偏移地址IP和段地址CS
IP←SS:[SP],SP←SP+2
CS←SS:[SP],SP←SP+2
而JMP是无条件转移指令,近跳转只修改IP的值,远跳转则将CS和IP的值都修改,其它的不会做修改
------解决方案--------------------------------------------------------
可以这样理解:假设你是一个导演(程序员),只有一个舞台(CPU),要演出多幕话剧,就需要两个工作人员来切换场景,一个负责把当前舞台的道具搬下来,放到不同的箱子里(call,入栈);另外一个负责把某个箱子里的道具搬到舞台上(ret,出栈)。
正常情况下,你可以调用call和ret来切换场景,你也可以自己把道具搬到箱子里(用push或mov指令把寄存器内容存入堆栈),这样就不用call了。
你的例子是一个多任务调度的程序,就像舞台场景切换,调度员就是那段汇编代码。换幕时,就调用uniTskSuspend(注意,是call uniTskSuspend),把当前的舞台布景存到箱子(堆栈)里,把权利交给调度员。调度员从黑板上找到下一幕的布景保存的箱子号(g_pRunningTask),通过ret恢复布景,将舞台(CPU)交给下一幕剧目。
千万不要把ret改成jmp,否则,道具搬出了一半(popad,popfd),留下了一半(返回地址),执行结果不可预知。
以上纯属猜测,具体请根据实际情况分析。
------解决方案--------------------------------------------------------
正常的途径是配对,但意外也非常多鸟
------解决方案--------------------------------------------------------
参考一下这篇文章:http://blog.csdn.net/bloodkain/article/details/4306220
是不是文中所描述的堆栈切换的原因?