linux線程是如何進行切換的?
問題來源於:有關Linux下線程的調度
文章第2點提到:為什麼一個線程中如果有一個循環的 話,其他線程都得不到執行,這就是因為Linux對線程的調度,策略和進程是一樣的,即,如果一個線程處於一個高密度的工作狀態的話,或者該線程的cpu slice沒用完的話,這個線程是不會被調度的。有關這個問題,還可以參看本論壇中「有關線程切換的一個問題」一帖。 沒有找到「有關線程切換的一個問題」一帖,我想知道該怎麼樣讓其它線程也能得到執行?當然不要用sleep拉。舉個栗子:void *thread(void *arg){
int n= 0; while (n&< 20) { n++; printf("%u run
",(unsigned int)pthread_self()); }}void Test(){
pthread_t tid[10]; int i = 0; for (; i &< 10; i++) { pthread_create(tid[i], NULL, thread, NULL); } for(i = 0; i &< 10; i++) { pthread_join(tid[i], NULL);}
}執行Test()函數,列印出來的內容是一個線程run完後才run下一線程。更期望各位答主能以實際例子說明。
我看了一眼這個文章,說的非常模糊,有很多地方表述都有問題。線程調度本身可以寫好幾本書,我就只回答樓主的問題好了。
首先 Linux 下麵線程是用進程實現的,這個說法是正確的。在內核裡面,進程用 struct task_struct 表示,一個進程就是一個 task_struct,一個線程也是一個 task_struct,但是同一個進程中的線程的 task_struct 裡邊某些值相同,某些指針指向相同的位置。從內核的角度來看,線程就是進程。
進程調度簡單來說,就是讓有活乾的進程做事情。這裡有活干其實就是執行指令的意思。所以 while(1) { i ++;} 這是可執行的指令,所以應該分給時間片。sleep(10000); 這個指令會使進程掛起,指令不能繼續執行了,所以會被調度。read() 的話,不管是磁碟文件還是網路IO,如果暫時沒有數據,也會掛起,跟 sleep 的情況類似,也會被調度。事實上,有 read 和 sleep 的線程得到了很多次執行的機會,但他們的指令導致了他們不能繼續,所以機會都被別人拿走了。
樓主的問題是,如果一個進程(線程)中有一個循環的話,其他進程(線程)都得不到執行。這句話的意思是,這個線程中的代碼是 while(1) {i ++;} 而其他線程中的代碼中遇到了 sleep 或者 read 了。難道事情不正應該是這樣子的嗎?真相不是他們得不到執行,而是他們得到了執行機會而無法執行。
以上的說法都是基於所有進程使用同樣的優先順序,在 Linux 下叫做 nice 值,的情形。如果你說,我這個進程就是要調用很多 read 和 sleep,但我還想看起來比另一個調用 while(1) {i ++;} 的進程執行的快,或者佔用 CPU 多。也可以,就是調整優先順序,調整 nice 值。Linux 是一個搶佔式系統,優先順序高的進程儘管有很多 read 和 sleep,也可以不斷搶佔優先順序低進程的執行。而低優先順序的進程不能搶佔高優先順序。首先, 其他線程是可以被調度的。不信的話,你可以把 thread 函數裡面的n加到一個更大的值, 比如2000。你就可以看到這些線程被交替調用了。
然後,以下這句話完全就是錯的。
如果一個線程處於一個高密度的工作狀態的話,或者該線程的cpu slice沒用完的話,這個線程是不會被調度的。
另外,其他幾個答案基本上沒有回答你的問題。
很簡單啊,列印之前做個乘法,你會發現其他線程會被調度。
你之所以看到一個線程執行完了再執行另外一個線程,完全是因為一個時間片完全可以措措有餘的執行完你的線程。大部分回答都在強行裝B?Linux具體實現不怎麼清楚,但是比較基礎的實現方式應該是這樣的:線程的切換由調度器來完成,而完成線程調度的方式非常簡單,只需要一個函數thread_switch()
void switch(thread_t *next_thread) {
CurrentThread-&>SP = SP;
CurrentThread = next_thread;
SP = CurrentThread-&>SP;
return;
}
當然這只是最簡單的版本,沒有考慮萬一這個函數執行過程中出現了interrupt等情況,不過卻可以很好的說明問題。
首先我們將現在的關於這個進程的信息存在當先thread 的棧中(包括很多寄存器的值,比如ESP,EBP,EIP等等),然後將下一個要調用的thread作為當前要執行的thread並且將這個thread的context從內存拷貝到寄存器中(比如EIP,EBP,ESP等等,當然EIP應該指向的是這個函數的return)。這個函數的神奇之處是調用這個函數是一個thread,而當這個函數結束時變成了另外一個thread。而這裡面的next_thread從哪裡來呢?從一個runnable queue里來的,這個queue是存儲一些等待CPU資源的線程,這些線程完成了自己的工作(i.e. 將磁碟的內容寫入內存),正在等候CPU。當然thread執行的順序可能不是FIFO,這個具體系統可能有具體的實現方式了。貼下我以前的寫的一篇文章中的片段:
關於上下文切換我僅僅參考linux內核的實現從技術角度來解釋:在linux中一個叫做task_struct結構體代表一個線程,linux調度器會對一個結構體:sched_entity結構體感興趣並對其進行調度,而它正好嵌入到task_struct中。因此對可以看出linux調度是線程級的。那具體怎麼調度呢?Linux用紅黑樹存所有可運行的進程(注意是可運行),使用等待隊列wait_queue記錄休眠(被阻塞)線程。用一個例子來介紹調度和上下文切換的細節,例如網卡產生一個中斷通知有網路數據,執行中的線程阻塞(從執行狀態剝離並放入等待隊列),然後再到紅黑樹裡面選一個來執行。這個過程的詳細過程是:虛擬內存映射和處理器狀態均要切換到新線程,前一個線程寄存器、棧信息還有其他狀態信息被保存。而新線程的棧信息和寄存器信息被恢復,剛好是反操作。我們把上述過程叫做上下文切換。等到網路數據讀取就緒,在等待隊列中的線程又被喚醒,接著放入紅黑樹中,成為可執行態,等待被執行。更詳細的可以參考《Linux內核設計與實現》從Linux內核角度上來說,「線程」是作為進程來實現的,「線程」的調度方式和進程一樣
推薦閱讀:
※在不改變方法簽名(method signature)的情況下, 請描述這段代碼的問題以及如何解決?
※測試線程同步中出現的阻塞問題?
※多線程執行順序控制?
※求詳解該InterlockedIncrement的實現?
※word2vec多線程優化時不加鎖的做法合理么?