在看進程切換前,我們先來看線程的切換吧。這一篇主要說的是用戶級線程的切換。因為 進程的切換=資源切換+指令執行序列切換。將資源和指令序列分開看,如果只是從一個執行指令序列切換到另一個執行指令序列,那麼這就是線程的切換。
線程保留了並發(一個cpu上交替的執行多個程序)的優點,避免了進程切換代價,不需要切資源(映射表),只是切執行指令序列。線程切換的實質就是映射表不變而PC指針變。
一個網頁浏覽器
一個線程用來從服務器接收數據
一個線程用來顯示文本
開始實現這個游覽器…
void WebExplorer()
{
char URL[]="http://cms.hit.edu.cn";
char buffer[1000];
pthread_create(...,GetData,URL,buffer);
pthread_create(...,Show,buffer);
}
void GetData(char* URL,char *p){...};
void Show(char* p){...};
我們下載了一段時間的數據後,切出去執行另一個線程,顯示文本後,切回來繼續下載。
Yield與Create
pthread_create()讓多個線程同時觸發,yield()能完成線程的切換,使線程交替執行。
現在有兩個執行序列,我們想要其中執行了一段時間後,跳到另一個去執行,之後又切回來。(線程的切換)
Yiled從100跳到300
//B中的Yield
void Yield()
{
找到300;
jmp 300;
}
//D中的Yield
void Yield()
{
找到204;
jmp204;
}
兩個執行序列與一個棧…
從100開始執行,在A函數中遇到B函數的調用,B的返回地址即下一句指令的地址104壓棧,又在B中遇到Yield()調用,Yield()的返回地址204壓棧。在B中的Yield()jmp到300,執行C函數,在C中遇到D()調用,304壓棧,執行D(),遇到D中的Yield()調用,Yield()的返回地址404壓棧。
<喎?http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPjxjb2RlIGNsYXNzPQ=="hljs r">D中的Yield()執行後,跳到204
200:B()
{
Yield();
204:
}
204開始執行遇到”}”,會彈棧,棧頂元素404被彈出執行404,這裡就出現問題了,我們剛才已經回到100那個線程了,現在卻又切到了300那個線程。
所以兩個序列一個棧是不行的。
一個棧到兩個棧
每個序列對應一個棧,Yield切換先切棧。
//D中的Yield
void Yield()
{
TCB2.esp=esp;
esp=TCB1.esp;
jmp 204;
}
現在兩個棧的情況是:
執行D中的Yield,使棧完成了切換,esp=1000。但是遇到jmp 204,就跳到204,那麼Yield()就永遠不能返回了。
//D中的Yield
void Yield()
{
TCB2.esp=esp;
esp=TCB1.esp;
//jmp 204;
}
把jmp 204去掉,此時棧已經完成了切換esp=1000,然後Yield執行遇到“}”,彈棧,此時彈棧彈出棧頂元素 204,執行204(“}”相當於ret),再ret,彈棧就是104了。
所以兩個線程的樣子:兩個TCB、兩個棧、切換的PC在棧中。
而ThreadCreate的核心就是申請一個棧,一個TCB(線程控制塊),再將棧與TCB關聯。
void ThreadCreate(A)
{
TCB* tcb=malloc();
*stack=malloc();
*stack=A;//100 執行線程的起始地址
tcb.esp=stack;//棧和TCB關聯
}
綜上
void ThreadCreate(func,arg1)
{
申請棧;
申請TCB;
func等入棧;
關聯TCB與棧;
}
void GetData(char* URL,char *p)
{
連接URL;
下載;
Yield();
...
}
void Yield()
{
壓入現場;
esp放在當前TCB中;
Next();//調度函數,對系統影響很大,可優先調度Show
從下個TCB取出esp;
彈棧切換線程;
}
用戶級線程
因為這裡的Yield,Create是用戶程序,沒有進入內核,所以說是用戶級線程。
用戶級線程對於內核是不可見的。
如果進程的某個線程進入內核並阻塞,對於內核來說,它是看不到那個進程裡面有其他線程的,所以會直接切到別的進程去執行。此時用戶級線程的並發性就沒有什麼用了。
例如
GetData()
{
連接URL發起請求;
等待網卡IO...
進程阻塞
}
Show()
{
顯示文本和連接;
...
}
用戶級線程只能在用戶級切來切去,不進入內核。