mykernel開源項(xiàng)目見于github:mykernel
假設(shè)有如下任務(wù)結(jié)構(gòu):任務(wù)A和任務(wù)B:
void a()
{
……
};
void b()
{
……
if(當(dāng)前處于B任務(wù))
Schedule到A;
……
};
void c()
{
……
};
任務(wù)A
{
a();
Schedule到B;
b();
c();
};
任務(wù)B
{
b();
};
首先:一個任務(wù)只能有一個上下文環(huán)境,即便是反復(fù)被調(diào)度妻顶。
有如下推斷:
首先,A運(yùn)行蜒车,然后執(zhí)行
Schedule到B;
調(diào)度到B讳嘱,A的運(yùn)行環(huán)境保存(第一次保存A任務(wù)上下文),恢復(fù)為B的運(yùn)行環(huán)境酿愧,B運(yùn)行沥潭。然后調(diào)度到A的時(shí)候,B的運(yùn)行環(huán)境保存嬉挡,恢復(fù)為之前A的運(yùn)行環(huán)境钝鸽。A接著上次的位置調(diào)用函數(shù)
b()
汇恤,此時(shí)就是函數(shù)嵌套調(diào)用了。那么拔恰,在A的任務(wù)棧保存ebp因谎,esp,返回地址(所有的這些都是函數(shù)相關(guān)的堆棧指針仁连,是有棧幀結(jié)構(gòu)的蓝角,而且和之前的esp并不沖突,因?yàn)檫@些是編譯器自動生成的代碼饭冬,而且是保存到棧里面了。)揪阶,然后跳轉(zhuǎn)到b()
昌抠。因?yàn)樘幱谌蝿?wù)A中,經(jīng)過判斷鲁僚,無法調(diào)度到A炊苫。b()
做完該做的事情,棧撤除冰沙,返回侨艾。
注意:在這個過程中,由于并沒有任務(wù)切換拓挥,故A任務(wù)上下文環(huán)境并沒有變化唠梨。接著執(zhí)行
c();
,會自然地在棧中保存返回地址侥啤,形成自己的棧結(jié)構(gòu)当叭,因?yàn)闀腥缦碌拇a被執(zhí)行:
pushl %ebp
movl %ebp,%ebp```
- 假如在執(zhí)行的過程中盖灸,不幸時(shí)間片到了蚁鳖,被調(diào)度到B,A的任務(wù)上下文環(huán)境保存(**第二次保存A任務(wù)上下文**)赁炎,但是已經(jīng)被改變了(eip肯定指向了c()中將要執(zhí)行的某一指令醉箕,esp,ebp由于棧幀的形成也變化了)徙垫。
- 恢復(fù)到B任務(wù)執(zhí)行讥裤,B在執(zhí)行的時(shí)候,`b()`會執(zhí)行調(diào)度到A松邪。
- 恢復(fù)為A任務(wù)上下文環(huán)境的時(shí)候坞琴,此時(shí)A中的esp位置由于之前調(diào)用`c()`的時(shí)候向下(x86棧的增長方式是是滿減)移動了幾個位置,***那么恢復(fù)的時(shí)候逗抑,無法找到我們之前(第一次A被調(diào)度出來剧辐,A中eip保存的是`b();`的地址)保存的上下文環(huán)境(因?yàn)閟p寒亥,ip均被A中對`c();`的調(diào)用破壞掉)。***
***那么看看之前我們在堆棧中又保存了什么呢荧关?***
>實(shí)際上溉奕,由于我們采用了任務(wù)控制塊中特殊的變量sp,ip來保存esp忍啤,eip加勤,那么實(shí)際上任務(wù)上下文環(huán)境并未保存堆棧中。
***再看看我們恢復(fù)的時(shí)候又用了堆棧里面的什么同波?***
>什么也沒用到鳄梅,還是只用到了tcb的特殊變量sp,ip未檩。實(shí)際上戴尸,在任務(wù)切換的時(shí)候,堆棧僅僅被我們用來迂回地改變eip冤狡,進(jìn)行函數(shù)跳轉(zhuǎn)執(zhí)行略贮,而這些動作灵莲,對esp并無影響。
>這樣一來,恢復(fù)到任務(wù)A的過程是沒有問題的府树。但是要記住的是粱快,恢復(fù)到的并不是第一次調(diào)度的地方伸眶,而是恢復(fù)到`c();`中被中斷的地方杨凑。
- 恢復(fù)到A之后,因?yàn)閌c();`還沒有執(zhí)行完成歪玲,所以迁央,要等待`c();`執(zhí)行完,當(dāng)`c();`執(zhí)行完成之后滥崩,`c();`用的棧撤除岖圈,返回。
- 至此A執(zhí)行完畢钙皮,可見蜂科,在任務(wù)的棧sp,ip被函數(shù)的調(diào)用或者其他破壞掉的時(shí)候短条,也不會影響任務(wù)的正常執(zhí)行流程导匣。
*任務(wù)會有很多個調(diào)度點(diǎn),但是這很多個調(diào)度點(diǎn)是按照時(shí)間的先后順序來的茸时,而且后面的調(diào)度點(diǎn)相對于前面的調(diào)度點(diǎn)也完成了很多有意義的事情贡定,所以我們只需要恢復(fù)到最近的一個任務(wù)調(diào)度點(diǎn)就可以了,恢復(fù)到以前的任務(wù)調(diào)度點(diǎn)反而是錯誤的可都。*
*之所以這樣缓待,我是這樣理解的蚓耽,任務(wù)保存的esp、eip終歸是為任務(wù)中的函數(shù)服務(wù)的旋炒,對于任務(wù)切換來說步悠,并沒有實(shí)際的意義—怎么變化都沒關(guān)系,只要能找到將要執(zhí)行的指令瘫镇,并且棧地址無誤鼎兽,而這一點(diǎn)被編譯器良好的棧的建立和撤除保證。*
在這個過程中铣除,任務(wù)的切換并沒有用到ebp谚咬,函數(shù)調(diào)用準(zhǔn)確返回,任務(wù)也能完好的切換尚粘。
所以序宦,個人覺得在任務(wù)切換中,并不需要保存和恢復(fù)ebp背苦。
###===============UPDATE================
*神逆轉(zhuǎn)來了。潘明。行剂。*
g:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
addl $3, %eax
popl %ebp
ret
f:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
call g
leave
ret
main:
pushl %ebp
movl %esp, %ebp
subl 8, (%esp)
call f
addl $1, %eax
leave
ret
千算萬算,沒有考慮到上面的這種情況钳降,如果兩個任務(wù)的入口都是上面的這個函數(shù)厚宰,如果在上下文切換的時(shí)候,不對ebp保存遂填、恢復(fù)铲觉,那么會有如下分析:
>A任務(wù)執(zhí)行完下面這幾句(ebp內(nèi)為值a),突然被調(diào)度到B吓坚,B任務(wù)也執(zhí)行完下面這幾句(ebp內(nèi)為值b)撵幽,然后被調(diào)度回A。此時(shí)值得注意的是礁击,B任務(wù)已經(jīng)更新了ebp內(nèi)容盐杂,a肯定不等于b。
g:
pushl %ebp
movl %esp, %ebp
切換回去的時(shí)候哆窿,因?yàn)閑bp的值已經(jīng)錯亂掉了链烈,那么在`movl 8(%ebp), %eax`引用參數(shù)的時(shí)候,肯定會引用錯誤(可能非法訪問不屬于自己的內(nèi)存)挚躯。
這種情況强衡,可以自己寫代碼模擬下。
>總結(jié)下來码荔,ebp的保存還是有必要的漩勤,只不過自己的理解不夠深入感挥,隨著自己慢慢的挖掘,可能會發(fā)現(xiàn)锯七,會有更多沒想到的地方链快。
***--update于2017/3/8 22:45***