內(nèi)核線程的作用主要有:
周期性的將dirty內(nèi)存頁同步到磁盤設(shè)備上。 比如 bpflush線程周期性的把dirty數(shù)據(jù)寫回磁盤內(nèi)存頁很少的情況下,把內(nèi)存page 交換到磁盤空間。 比如kswapd,系統(tǒng)會(huì)為每一個(gè)NUMA創(chuàng)建一個(gè)kswapd進(jìn)程,但是在非NUMA系統(tǒng)上,則僅有一個(gè)kswapd管理延時(shí)動(dòng)作實(shí)現(xiàn)文件系統(tǒng)的事物日志主要包括兩種類型的內(nèi)核線程:
線程按周期性間隔運(yùn)行,檢測特定資源的使用,在用量超出或者低于預(yù)置的限制時(shí)采取行動(dòng)在線程啟動(dòng)后則一直等待,直到內(nèi)核線程請求執(zhí)行某一特定的操作。同時(shí)內(nèi)核線程是由內(nèi)核自己創(chuàng)建的線程,也叫做守護(hù)線程(deamon)。在終端上用命令”ps -Al”列出的所有進(jìn)程中,名字以k開關(guān)以d結(jié)尾的往往都是內(nèi)核線程,比如kthreadd、kswapd。
既然是內(nèi)核線程是一個(gè)單獨(dú)的概念,那么與用戶線程之間一定存在某些區(qū)別,
內(nèi)核線程與用戶線程的相同點(diǎn)是:
都由do_fork()創(chuàng)建,每個(gè)線程都有獨(dú)立的task_struct和內(nèi)核棧;都參與調(diào)度,內(nèi)核線程也有優(yōu)先級(jí),會(huì)被調(diào)度器平等地?fù)Q入換出
不同之處在于:
內(nèi)核線程只工作在內(nèi)核態(tài)中;而用戶線程則既可以運(yùn)行在內(nèi)核態(tài)(執(zhí)行系統(tǒng)調(diào)用時(shí)),也可以運(yùn)行在用戶態(tài);內(nèi)核線程沒有用戶空間,所以對于一個(gè)內(nèi)核線程來說,它的0~3G的內(nèi)存空間是空白的,它的current->mm是空的,與內(nèi)核使用同一張頁表;而用戶線程則可以看到完整的0~4G內(nèi)存空間。在linux內(nèi)核啟動(dòng)的最后階段,系統(tǒng)會(huì)創(chuàng)建兩個(gè)內(nèi)核線程,一個(gè)是init,一個(gè)是kthreadd。其中init線程的作用是運(yùn)行文件系統(tǒng)上的一系列”init”腳本,并啟動(dòng)shell進(jìn)程,所以init線程稱得上是系統(tǒng)中所有用戶進(jìn)程的祖先,它的pid是1。kthreadd線程是內(nèi)核的守護(hù)線程,在內(nèi)核正常工作時(shí),它永遠(yuǎn)不退出,是一個(gè)死循環(huán),它的pid是2。
通過上述不同之處的對比可以發(fā)現(xiàn),內(nèi)核線程沒有用戶內(nèi)存空間,與之相對的是用戶進(jìn)程(注意,此處不是用戶線程,用戶線程內(nèi)存空間與用戶進(jìn)程空間之間存在的一定差異,具體差異可以參考我之前的博文),用戶進(jìn)程同時(shí)具備內(nèi)核空間與用戶空間,在進(jìn)行系統(tǒng)調(diào)用時(shí)用戶進(jìn)程會(huì)由用戶內(nèi)存空間陷入內(nèi)核內(nèi)存空間。之所以此處采用內(nèi)核線程與用戶進(jìn)程(而非用戶線程)進(jìn)行對比,是由于內(nèi)核線程(kernel thread)是“獨(dú)立運(yùn)行在內(nèi)核空間的標(biāo)準(zhǔn)進(jìn)程”(以上這句話摘自《linux內(nèi)核設(shè)計(jì)與實(shí)現(xiàn)》),因此從功能上看內(nèi)核線程一方面具有進(jìn)程的概念特點(diǎn)——具有獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合上的一次執(zhí)行活動(dòng),是系統(tǒng)進(jìn)行資源分配的單位,同時(shí)內(nèi)核線程又具有線程的概念特點(diǎn)——進(jìn)程內(nèi)的一個(gè)可調(diào)度實(shí)體。
好了通過以上分析基本可以分清“內(nèi)核線程(kernel thread)”、“用戶進(jìn)程”、“用戶線程”這幾個(gè)基本概念。這里要補(bǔ)充的一點(diǎn)是以上幾個(gè)概念全部是在邏輯層面上的,從實(shí)現(xiàn)的角度來看linux內(nèi)核本身并不支持線程這一概念,linux 將所有的線程都當(dāng)作進(jìn)程來實(shí)現(xiàn)。內(nèi)核并沒有準(zhǔn)備特別的調(diào)度算法或是定義特別的數(shù)據(jù)結(jié)構(gòu)來表征線程。相反,線程僅僅被視為一個(gè)與其他進(jìn)程(概念上應(yīng)該是線程)共享某些資源的進(jìn)程(概念上應(yīng)該是線程)。每個(gè)線程都擁有唯一隸屬于自己的task_struct,所以在內(nèi)核中,它看起來就像是一個(gè)普通的進(jìn)程(只是線程和其他一些進(jìn)程共享某些資源,如地址空間)。關(guān)于這一點(diǎn)可以通過系統(tǒng)調(diào)用clone的實(shí)現(xiàn)來驗(yàn)證,無論是fork、vfork、kthread_create最后都是要調(diào)用do_fork,而do_fork就是根據(jù)不同的函數(shù)參數(shù),對一個(gè)進(jìn)程所需的資源進(jìn)行分配。
在linux2.6之前,內(nèi)核并不支持線程的概念,僅通過輕量級(jí)進(jìn)程(lightweight PRocess)模擬線程,一個(gè)用戶線程對應(yīng)一個(gè)內(nèi)核線程(內(nèi)核輕量級(jí)進(jìn)程),這種模型最大的特點(diǎn)是線程調(diào)度由內(nèi)核完成了,而其他線程操作(同步、取消)等都是核外的線程庫(LinuxThread)函數(shù)完成的。但這個(gè)問題還存在很多的問題。
在linux2.6之后,為了完全兼容posix標(biāo)準(zhǔn),linux2.6首先對內(nèi)核進(jìn)行了改進(jìn),引入了線程組的概念(仍然用輕量級(jí)進(jìn)程表示線程),有了這個(gè)概念就可以將一組線程組織稱為一個(gè)進(jìn)程,如此通過這個(gè)改變,linux內(nèi)核正式支持多線程特性。以上是邏輯上的改變,在實(shí)現(xiàn)上主要的改變就是在task_struct中加入tgid字段,這個(gè)字段就是用于表示線程組id的字段。在用戶線程庫方面,也使用NPTL代替LinuxThread。不同調(diào)度模型上仍然采用“1對1”模型
現(xiàn)在在支持多線程的操作系統(tǒng)中一般采用三種調(diào)度模型,分別是“一對一模型”、“多對一模型”、“多對多模型”。
1)“一對一模型”
一對一模型中,每個(gè)用戶線程都對應(yīng)各自的內(nèi)核調(diào)度實(shí)體。內(nèi)核會(huì)對每個(gè)線程進(jìn)行調(diào)度,可以調(diào)度到其他處理器上面。當(dāng)然由內(nèi)核來調(diào)度的結(jié)果就是:線程的每次操作會(huì)在用戶態(tài)和內(nèi)核態(tài)切換。另外,內(nèi)核為每個(gè)線程都映射調(diào)度實(shí)體,如果系統(tǒng)出現(xiàn)大量線程,會(huì)對系統(tǒng)性能有影響。但該模型的實(shí)用性還是高于多對一的線程模型。LinuxThread與NPTL都是采用這種模型。
在linux中通過LWP(lightweight process)作為線程概念的支持,輕量級(jí)線程(LWP)是一種由內(nèi)核支持的用戶線程。它是基于內(nèi)核線程的高級(jí)抽象,因此只有先支持內(nèi)核線程,才能有LWP。每一個(gè)進(jìn)程有一個(gè)或多個(gè)Lwps,每個(gè)LWP由一個(gè)內(nèi)核線程支持。這種模型實(shí)際上就是恐龍書上所提到的一對一線程模型。在這種實(shí)現(xiàn)的操作系統(tǒng)中,LWP就是用戶線程。
由于每個(gè)LWP都與一個(gè)特定的內(nèi)核線程關(guān)聯(lián),因此每個(gè)LWP都是一個(gè)獨(dú)立的線程調(diào)度單元。即使有一個(gè)LWP在系統(tǒng)調(diào)用中阻塞,也不會(huì)影響整個(gè)進(jìn)程的執(zhí)行。
輕量級(jí)進(jìn)程具有局限性。首先,大多數(shù)LWP的操作,如建立、析構(gòu)以及同步,都需要進(jìn)行系統(tǒng)調(diào)用。系統(tǒng)調(diào)用的代價(jià)相對較高:需要在user mode和kernel mode中切換。其次,每個(gè)LWP都需要有一個(gè)內(nèi)核線程支持,因此LWP要消耗內(nèi)核資源(內(nèi)核線程的棧空間)。因此一個(gè)系統(tǒng)不能支持大量的LWP。圖也是盜的。

2)“多對一模型”
多對一線程模型中,線程的創(chuàng)建、調(diào)度、同步的所有細(xì)節(jié)全部由進(jìn)程的用戶空間線程庫來處理。用戶態(tài)線程的很多操作對內(nèi)核來說都是透明的,因?yàn)椴恍枰獌?nèi)核來接管,這意味不需要內(nèi)核態(tài)和用戶態(tài)頻繁切換。線程的創(chuàng)建、調(diào)度、同步處理速度非常快。當(dāng)然線程的一些其他操作還是要經(jīng)過內(nèi)核,如IO讀寫。這樣導(dǎo)致了一個(gè)問題:當(dāng)多線程并發(fā)執(zhí)行時(shí),如果其中一個(gè)線程執(zhí)行IO操作時(shí),內(nèi)核接管這個(gè)操作,如果IO阻塞,用戶態(tài)的其他線程都會(huì)被阻塞,因?yàn)檫@些線程都對應(yīng)同一個(gè)內(nèi)核調(diào)度實(shí)體。在多處理器機(jī)器上,內(nèi)核不知道用戶態(tài)有這些線程,無法把它們調(diào)度到其他處理器,也無法通過優(yōu)先級(jí)來調(diào)度。這對線程的使用是沒有意義的!

3)“多對多模型”
用戶線程庫還是完全建立在用戶空間中,因此用戶線程的操作還是很廉價(jià),因此可以建立任意多需要的用戶線程。操作系統(tǒng)提供了 LWP 作為用戶線程和內(nèi)核線程之間的橋梁。 LWP 還是和前面提到的一樣,具有內(nèi)核線程支持,是內(nèi)核的調(diào)度單元,并且用戶線程的系統(tǒng)調(diào)用要通過 LWP ,因此進(jìn)程中某個(gè)用戶線程的阻塞不會(huì)影響整個(gè)進(jìn)程的執(zhí)行。用戶線程庫將建立的用戶線程關(guān)聯(lián)到 LWP 上, LWP 與用戶線程的數(shù)量不一定一致。當(dāng)內(nèi)核調(diào)度到某個(gè) LWP 上時(shí),此時(shí)與該 LWP 關(guān)聯(lián)的用戶線程就被執(zhí)行。

新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注