歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> 關於Linux >> 用戶級線程

用戶級線程

日期:2017/3/1 12:05:46   编辑:關於Linux

線程的切換

在看進程切換前,我們先來看線程的切換吧。這一篇主要說的是用戶級線程的切換。因為 進程的切換=資源切換+指令執行序列切換。將資源和指令序列分開看,如果只是從一個執行指令序列切換到另一個執行指令序列,那麼這就是線程的切換。

線程保留了並發(一個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()
{
  顯示文本和連接;
  ...
}

用戶級線程只能在用戶級切來切去,不進入內核。

這裡寫圖片描述

Copyright © Linux教程網 All Rights Reserved