天天看點

使用者級線程和核心級線程,你厘清楚了嗎?

前天晚上有個夥伴私信我說在學程序和線程,問我有沒有好的方法和學習教程,剛好我最近也在備相關的課。

班上不少學生學的還是很不錯的。拿班上小白和小明的例子吧(藝名哈)。小明接受能力很強,小白則稍差些。

關于多線程的資料很多,小白把線程的基本概念弄懂了,但關于「使用者級線程和核心級線程」的概念,她卻怎麼也搞不清楚,隻好向作業系統基礎紮實的小明請教。

對于小白的問題,小明總會耐心解答:“線程裡面這兩個概念确實比較難了解,我先給你講使用者級線程吧。”

使用者級線程

“既然你已經看過線程的基本概念,那我就直接跳過這一部分了。很久很久之前,線程的概念是出現了,但作業系統廠商可不能直接就去修改作業系統的核心,因為對他們來說,穩定性是最重要的。貿然把未經驗證的東西加入核心,出問題了怎麼辦?是以想要驗證線程的可用性,得另想辦法。”

“我知道我知道,那些研究人員就編寫了一個關于線程的函數庫,用函數庫來實作線程!”小白得意的說:“這個我剛剛在網上看到了。”

“是的,他們把建立線程、終止線程等功能放在了這個線程庫内,使用者就可以通過調用這些函數來實作所需要的功能。”小明找了張紙,寫上了幾個函數:pthread_creat,pthread_exit ,pthread_join ,pthread_yield ,接着說:“這是幾個重要的功能,我馬上會講到,你應該能大概猜出這些函數的功能吧?”

“emmmm,讓我想想,pthread_creat 是建立一個新線程,pthread_exit 是結束線程,pthread_join 嘛,我猜是準備運作,最後一個,我就不知道了。”

“不知道也沒關系,一會你就清楚了。”小明接着講:“要知道,剛剛我們說的線程庫,是位于使用者空間的,作業系統核心對這個庫一無所知,是以從核心的角度看,它還是按正常的方式管理。”

小白問道:“也就是說作業系統眼裡還是隻有程序喽?那我用線程庫寫的多線程程序,隻能一次在一個 CPU 核心上運作?”

小明點點頭,說:“你說的沒錯,這其實是使用者級線程的一個缺點,這些線程隻能占用一個核,是以做不到并行加速,而且由于使用者線程的透明性,作業系統是不能主動切換線程的,換句話講,如果線程 A 正在運作,線程 B 想要運作的話,隻能等待 A 主動放棄 CPU,也就是主動調用 pthread_yield 函數。”

tobe 注:對作業系統來說,使用者級線程具有不可見性,也稱透明性。

“停一下,讓我想一想,”小白飛速思考着小明的話,“是不是說,即使有線程庫,使用者級線程也做不到像程序那樣的輪轉排程?”

“非常正确!看來你對程序的概念很清楚嘛。不過呢,雖然不能做到輪轉排程,但使用者級線程也有他自己的好處——你可以為你的應用程式定制排程算法,畢竟什麼時候退出線程你自己說了算。剛剛說了,因為作業系統隻能看到程序的存在,那如果某一個線程阻塞了,你覺得會發生什麼?”

“在作業系統眼裡,是程序阻塞了,那麼整個程序就會進入阻塞态,在阻塞操作結束前,這個程序都無法得到 CPU 資源。那就相當于,所有的線程都被阻塞了。”小白得意的回答。

“沒錯,是以如果任由線程進行阻塞操作,程序的效率将受到很大的影響,是以在這個過程中,出現了一個替代方案——jacket。所謂 jacket,就是把一個産生阻塞的系統調用轉化成一個非阻塞的系統調用。”

小白驚訝地問:“這怎麼做得到?該阻塞的調用,還能變得不阻塞?”

小明答道:“我來舉個例子吧,不是直接調用一個系統 I/O 例程,而是調用一個應用級别的 I/O jacket 例程,這個 jacket 例程中的代碼會檢查并且确定 I/O 裝置是不是正忙,如果忙的話,就在使用者态下将該線程阻塞,然後把控制權交給另一個線程。隔一段時間後再次檢查 I/O 裝置。就像你說的,最後還是會執行阻塞調用,但使用 jacket 可以縮短被阻塞的時間。不過有些情況下是可以不被阻塞的,取決于具體的實作。”

小明停頓了一會,說:“使用者級線程的概念大概就這麼多,我們接下來講核心級線程吧。”

核心級線程

“有了使用者級線程的鋪墊,核心級線程就好講多了。現在我們知道,許多作業系統都已經支援核心級線程了。為了實作線程,核心裡就需要有用來記錄系統裡所有線程的線程表。當需要建立一個新線程的時候,就需要進行一個系統調用,然後由作業系統進行線程表的更新。當然了,傳統的程序表也還是有的。你想想看,如果作業系統「看得見」線程,有什麼好處?“

小白自信的回答:“作業系統核心如果知道線程的存在,就可以像排程多個程序一樣,把這些線程放在好幾個 CPU 核心上,就能做到實際上的并行了。”

“還有一點你沒有說到,如果線程可見,那麼假如線程 A 阻塞了,與他同屬一個程序的線程也不會被阻塞。這是核心級線程的絕對優勢。”

“那核心級線程就沒有什麼缺點嗎?”

“缺點當然是有的,你想想看,讓作業系統進行線程排程,那意味着每次切換線程,就需要「陷入」核心态,而作業系統從使用者态到核心态的轉變是有開銷的,是以說核心級線程切換的代價要比使用者級線程大。還有很重要的一點——線程表是存放在作業系統固定的表格空間或者堆棧空間裡,是以核心級線程的數量是有限的,擴充性比不上使用者級線程。”

"核心級線程就這麼點東西,我最後給你留一張圖,你要是能看得懂,就說明你了解今天的概念了。"

使用者級線程和核心級線程,你厘清楚了嗎?

“我肯定能看懂!”

在學程序線程的夥伴,你們懂了嗎?不清楚的地方,可以留言,相關的視訊教程也可以留言或者私信我要!

繼續閱讀