用戶級線程和內(nèi)核級線程,你分得清嗎?

這篇文章是上一篇博客的補(bǔ)充,旨在把沒有講清楚的「用戶級線程和內(nèi)核級線程」補(bǔ)充完整。希望讀者能對線程有更進(jìn)一步的了解。


小白最近在學(xué)習(xí)多線程編程。

網(wǎng)上關(guān)于多線程的資料很多,小白很快就把線程的基本概念弄懂了,但關(guān)于「用戶級線程和內(nèi)核級線程」的概念,她卻怎么也搞不清楚,只好向操作系統(tǒng)基礎(chǔ)扎實(shí)的小明請教。

對于小白的問題,小明總會耐心解答:“線程里面這兩個概念確實(shí)比較難理解,我先給你講用戶級線程吧?!?/p>

用戶級線程

“既然你說你已經(jīng)看過線程的基本概念,那我就直接跳過這一部分了。

很久很久之前,線程的概念是出現(xiàn)了,但操作系統(tǒng)廠商可不能直接就去修改操作系統(tǒng)的內(nèi)核,因?yàn)閷λ麄儊碚f,穩(wěn)定性是最重要的。貿(mào)然把未經(jīng)驗(yàn)證的東西加入內(nèi)核,出問題了怎么辦?所以想要驗(yàn)證線程的可用性,得另想辦法?!?/p>

“我知道我知道,那些研究人員就編寫了一個關(guān)于線程的函數(shù)庫,用函數(shù)庫來實(shí)現(xiàn)線程!”小白得意的說:“這個我剛剛在網(wǎng)上看到了?!?/p>

“是的,他們把創(chuàng)建線程終止線程等功能放在了這個線程庫內(nèi),用戶就可以通過調(diào)用這些函數(shù)來實(shí)現(xiàn)所需要的功能?!毙∶髡伊藦埣垼瑢懮狭藥讉€函數(shù):pthread_creat ,pthread_exit ,pthread_join ,pthread_yield ,接著說:“這是幾個重要的函數(shù),你應(yīng)該能大概猜出這些函數(shù)的功能吧?”

“emmmm,讓我想想,pthread_creat 是創(chuàng)建一個新線程,pthread_exit 是結(jié)束線程,pthread_join 嘛,我猜是準(zhǔn)備運(yùn)行某一個線程,然后把它加進(jìn)就緒隊列。最后一個函數(shù)我就不知道了?!?/p>

“不知道也沒關(guān)系,一會你就清楚了?!毙∶鹘又v:“要知道,剛剛我們說的線程庫,是位于用戶空間的,操作系統(tǒng)內(nèi)核對這個庫一無所知,所以從內(nèi)核的角度看,它還是按正常的方式管理?!?/p>

小白問道:“也就是說操作系統(tǒng)眼里還是只有進(jìn)程嘍?那我用線程庫寫的一個多線程進(jìn)程,只能一次在一個 CPU 核心上運(yùn)行?”

小明點(diǎn)點(diǎn)頭,說:“你說的沒錯,這其實(shí)是用戶級線程的一個缺點(diǎn),這些線程只能占用一個核,所以做不到并行加速,而且由于用戶線程的透明性操作系統(tǒng)是不能主動切換線程的,換句話講,如果 A,B 是同一個進(jìn)程的兩個線程的話, A 正在運(yùn)行的時候,線程 B 想要運(yùn)行的話,只能等待 A 主動放棄 CPU,也就是主動調(diào)用 pthread_yield 函數(shù)。”

tobe 注:對操作系統(tǒng)來說,用戶級線程具有不可見性,也稱透明性。

“停一下,讓我想一想,”小白飛速思考著小明的話,“是不是說,即使有線程庫,用戶級線程也做不到像進(jìn)程那樣的輪轉(zhuǎn)調(diào)度?”

“非常正確!看來你對進(jìn)程的概念很清楚嘛。不過呢,雖然不能做到輪轉(zhuǎn)調(diào)度,但用戶級線程也有他自己的好處——你可以為你的應(yīng)用程序定制調(diào)度算法,畢竟什么時候退出線程你自己說了算。剛剛說了,因?yàn)椴僮飨到y(tǒng)只能看到進(jìn)程的存在,那如果某一個線程阻塞了,你覺得會發(fā)生什么?”

“在操作系統(tǒng)眼里,是進(jìn)程阻塞了,那么整個進(jìn)程就會進(jìn)入阻塞態(tài),在阻塞操作結(jié)束前,這個進(jìn)程都無法得到 CPU 資源。那就相當(dāng)于,所有的線程都被阻塞了?!毙“椎靡獾幕卮?。

“沒錯,所以如果任由線程進(jìn)行阻塞操作,進(jìn)程的效率將受到很大的影響,所以在這個過程中,出現(xiàn)了一個替代方案——jacket。所謂 jacket,就是把一個產(chǎn)生阻塞的系統(tǒng)調(diào)用轉(zhuǎn)化成一個非阻塞的系統(tǒng)調(diào)用。”

小白驚訝地問:“這怎么做得到?該阻塞的調(diào)用,還能變得不阻塞?”

小明答道:“我來舉個例子吧,不是直接調(diào)用一個系統(tǒng) I/O 例程,而是調(diào)用一個應(yīng)用級別的 I/O jacket 例程,這個 jacket 例程中的代碼會檢查并且確定 I/O 設(shè)備是不是正忙,如果忙的話,就在用戶態(tài)下將該線程阻塞,然后把控制權(quán)交給另一個線程。隔一段時間后再次檢查 I/O 設(shè)備。就像你說的,最后還是會執(zhí)行阻塞調(diào)用,但使用 jacket 可以縮短被阻塞的時間。不過有些情況下是可以不被阻塞的,取決于具體的實(shí)現(xiàn)?!?/p>

小明停頓了一會,說:“用戶級線程的概念大概就這么多,我們接下來講內(nèi)核級線程吧。”

內(nèi)核級線程

“有了用戶級線程的鋪墊,內(nèi)核級線程就好講多了。現(xiàn)在我們知道,許多操作系統(tǒng)都已經(jīng)支持內(nèi)核級線程了。為了實(shí)現(xiàn)線程,內(nèi)核里就需要有用來記錄系統(tǒng)里所有線程的線程表。當(dāng)需要創(chuàng)建一個新線程的時候,就需要進(jìn)行一個系統(tǒng)調(diào)用,然后由操作系統(tǒng)進(jìn)行線程表的更新。當(dāng)然了,傳統(tǒng)的進(jìn)程表也還是有的。你想想看,如果操作系統(tǒng)「看得見」線程,有什么好處?“

小白自信的回答:“操作系統(tǒng)內(nèi)核如果知道線程的存在,就可以像調(diào)度多個進(jìn)程一樣,把這些線程放在好幾個 CPU 核心上,就能做到實(shí)際上的并行了。”

“還有一點(diǎn)你沒有說到,如果線程可見,那么假如線程 A 阻塞了,與他同屬一個進(jìn)程的線程也不會被阻塞。這是內(nèi)核級線程的絕對優(yōu)勢?!?/p>

“那內(nèi)核級線程就沒有什么缺點(diǎn)嗎?”

“缺點(diǎn)當(dāng)然是有的,你想想看,讓操作系統(tǒng)進(jìn)行線程調(diào)度,那意味著每次切換線程,就需要「陷入」內(nèi)核態(tài),而操作系統(tǒng)從用戶態(tài)到內(nèi)核態(tài)的轉(zhuǎn)變是有開銷的,所以說內(nèi)核級線程切換的代價要比用戶級線程大。還有很重要的一點(diǎn)——線程表是存放在操作系統(tǒng)固定的表格空間或者堆??臻g里,所以內(nèi)核級線程的數(shù)量是有限的,擴(kuò)展性比不上用戶級線程?!?/p>

"內(nèi)核級線程就這么點(diǎn)東西,我最后給你留一張圖,你要是能看得懂,就說明你理解今天的概念了。"

兩個線程模型

小白得意地說:“我當(dāng)然看得懂了,謝謝小明!”


希望你在看完我的文章之后有所收獲。

感謝你的閱讀,我們后會有期!

聲明:原創(chuàng)文章,未經(jīng)授權(quán),禁止轉(zhuǎn)載

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容