自從多線程編程的概念出現(xiàn)在 Linux 中以來,Linux
多線應(yīng)用的發(fā)展總是與兩個(gè)問題脫不開干系:兼容性、效率。本文從線程模型入手,通過分析目前 Linux 平臺上最流行的 LinuxThreads
線程庫的實(shí)現(xiàn)及其不足,描述了 Linux 社區(qū)是如何看待和解決兼容性和效率這兩個(gè)問題的。
一.基礎(chǔ)知識:線程和進(jìn)程
按照教科書上的定義,進(jìn)程是資源管理的最小單位,線程是程序執(zhí)行的最小單位。在操作系統(tǒng)設(shè)計(jì)上,從進(jìn)程演化出線程,最主要的目的就是更好的支持SMP以及減小(進(jìn)程/線程)上下文切換開銷。
無
論按照怎樣的分法,一個(gè)進(jìn)程至少需要一個(gè)線程作為它的指令執(zhí)行體,進(jìn)程管理著資源(比如cpu、內(nèi)存、文件等等),而將線程分配到某個(gè)cpu上執(zhí)行。一個(gè)
進(jìn)程當(dāng)然可以擁有多個(gè)線程,此時(shí),如果進(jìn)程運(yùn)行在SMP機(jī)器上,它就可以同時(shí)使用多個(gè)cpu來執(zhí)行各個(gè)線程,達(dá)到最大程度的并行,以提高效率;同時(shí),即使
是在單cpu的機(jī)器上,采用多線程模型來設(shè)計(jì)程序,正如當(dāng)年采用多進(jìn)程模型代替單進(jìn)程模型一樣,使設(shè)計(jì)更簡潔、功能更完備,程序的執(zhí)行效率也更高,例如采
用多個(gè)線程響應(yīng)多個(gè)輸入,而此時(shí)多線程模型所實(shí)現(xiàn)的功能實(shí)際上也可以用多進(jìn)程模型來實(shí)現(xiàn),而與后者相比,線程的上下文切換開銷就比進(jìn)程要小多了,從語義上
來說,同時(shí)響應(yīng)多個(gè)輸入這樣的功能,實(shí)際上就是共享了除cpu以外的所有資源的。
針對線程模型的兩大意義,分別開發(fā)出了核
心級線程和用戶級線程兩種線程模型,分類的標(biāo)準(zhǔn)主要是線程的調(diào)度者在核內(nèi)還是在核外。前者更利于并發(fā)使用多處理器的資源,而后者則更多考慮的是上下文切換
開銷。在目前的商用系統(tǒng)中,通常都將兩者結(jié)合起來使用,既提供核心線程以滿足smp系統(tǒng)的需要,也支持用線程庫的方式在用戶態(tài)實(shí)現(xiàn)另一套線程機(jī)制,此時(shí)一
個(gè)核心線程同時(shí)成為多個(gè)用戶態(tài)線程的調(diào)度者。正如很多技術(shù)一樣,"混合"通常都能帶來更高的效率,但同時(shí)也帶來更大的實(shí)現(xiàn)難度,出于"簡單"的設(shè)計(jì)思路,
Linux從一開始就沒有實(shí)現(xiàn)混合模型的計(jì)劃,但它在實(shí)現(xiàn)上采用了另一種思路的"混合"。
在線程機(jī)制的具體實(shí)現(xiàn)上,可以在
操作系統(tǒng)內(nèi)核上實(shí)現(xiàn)線程,也可以在核外實(shí)現(xiàn),后者顯然要求核內(nèi)至少實(shí)現(xiàn)了進(jìn)程,而前者則一般要求在核內(nèi)同時(shí)也支持進(jìn)程。核心級線程模型顯然要求前者的支
持,而用戶級線程模型則不一定基于后者實(shí)現(xiàn)。這種差異,正如前所述,是兩種分類方式的標(biāo)準(zhǔn)不同帶來的。
當(dāng)核內(nèi)既支持進(jìn)程也
支持線程時(shí),就可以實(shí)現(xiàn)線程-進(jìn)程的"多對多"模型,即一個(gè)進(jìn)程的某個(gè)線程由核內(nèi)調(diào)度,而同時(shí)它也可以作為用戶級線程池的調(diào)度者,選擇合適的用戶級線程在
其空間中運(yùn)行。這就是前面提到的"混合"線程模型,既可滿足多處理機(jī)系統(tǒng)的需要,也可以最大限度的減小調(diào)度開銷。絕大多數(shù)商業(yè)操作系統(tǒng)(如Digital
Unix、Solaris、Irix)都采用的這種能夠完全實(shí)現(xiàn)POSIX1003.1c標(biāo)準(zhǔn)的線程模型。在核外實(shí)現(xiàn)的線程又可以分為"一對一"、"多對
一"兩種模型,前者用一個(gè)核心進(jìn)程(也許是輕量進(jìn)程)對應(yīng)一個(gè)線程,將線程調(diào)度等同于進(jìn)程調(diào)度,交給核心完成,而后者則完全在核外實(shí)現(xiàn)多線程,調(diào)度也在用
戶態(tài)完成。后者就是前面提到的單純的用戶級線程模型的實(shí)現(xiàn)方式,顯然,這種核外的線程調(diào)度器實(shí)際上只需要完成線程運(yùn)行棧的切換,調(diào)度開銷非常小,但同時(shí)因
為核心信號(無論是同步的還是異步的)都是以進(jìn)程為單位的,因而無法定位到線程,所以這種實(shí)現(xiàn)方式不能用于多處理器系統(tǒng),而這個(gè)需求正變得越來越大,因
此,在現(xiàn)實(shí)中,純用戶級線程的實(shí)現(xiàn),除算法研究目的以外,幾乎已經(jīng)消失了。
Linux內(nèi)核只提供了輕量進(jìn)程的支持,限制了
更高效的線程模型的實(shí)現(xiàn),但Linux著重優(yōu)化了進(jìn)程的調(diào)度開銷,一定程度上也彌補(bǔ)了這一缺陷。目前最流行的線程機(jī)制LinuxThreads所采用的就
是線程-進(jìn)程"一對一"模型,調(diào)度交給核心,而在用戶級實(shí)現(xiàn)一個(gè)包括信號處理在內(nèi)的線程管理機(jī)制。Linux-LinuxThreads的運(yùn)行機(jī)制正是本
文的描述重點(diǎn)。