1.什么是進程?為什么要有進程?
進程有一個相當精簡的解釋:進程是對操作系統(tǒng)上正在運行程序的一個抽象。
這個概念確實挺抽象,仔細想想?yún)s也挺精準。
我們平常使用計算機,都會在同一時間做許多事,比如邊看電影,邊微信聊天,順便打開瀏覽器百度搜索一下,我們所做的這么多事情背后都是一個個正在運行中的軟件程序;這些軟件想要運行起來,首先在磁盤上需要有各自的程序代碼,然后將代碼加載到內存中,CPU會去執(zhí)行這些代碼,運行中會產生很多數(shù)據(jù)需要存放,也可能需要和網(wǎng)卡、顯卡、鍵盤等外部設備交互,這背后其實就涉及到程序對計算機資源的使用,存在這么多程序,我們當然需要想辦法管理程序資源的使用。并且CPU如果只有一個,那么還需要操作系統(tǒng)調度CPU分配給各個程序使用,讓用戶感覺這些程序在同時運行,不影響用戶體驗。
理所當然,操作系統(tǒng)會把每個運行中的程序封裝成獨立的實體,分配各自所需要的資源,再根據(jù)調度算法切換執(zhí)行。這個抽象程序實體就是進程。
所以很多對進程的官方解釋中都會提到:進程是操作系統(tǒng)進行資源分配和調度的一個基本單位。
2.什么是線程?為什么要有線程?
在早期的操作系統(tǒng)中并沒有線程的概念,進程是擁有資源和獨立運行的最小單位,也是程序執(zhí)行的最小單位。任務調度采用的是時間片輪轉的搶占式調度方式,而進程是任務調度的最小單位,每個進程有各自獨立的內存空間,使得各個進程之間內存地址相互隔離。
后來,隨著計算機行業(yè)的發(fā)展,程序的功能設計越來越復雜,我們的應用中同時發(fā)生著多種活動,其中某些活動隨著時間的推移會被阻塞,比如網(wǎng)絡請求、讀寫文件(也就是IO操作),我們自然而然地想著能不能把這些應用程序分解成更細粒度、能 準并行運行 多個順序執(zhí)行實體,并且這些細粒度的執(zhí)行實體可以共享進程的地址空間,也就是可以共享程序代碼、數(shù)據(jù)、內存空間等,這樣程序設計模型會變得更加簡單。
其實很多計算機世界里的技術演變,都是模擬現(xiàn)實世界。比如我們把一個進程當成一個項目,當項目任務變得復雜時,自然想著能不能將項目按照業(yè)務、產品、工作方向等分成一個個任務模塊,分派給不同人員各自并行完成,再按照某種方式組織起各自的任務成果,最終完成項目。
需要多線程還有一個重要的理由就是:每個進程都有獨立的代碼和數(shù)據(jù)空間(程序上下文),程序之間的切換會有較大的開銷;線程可以看做輕量級的進程,同一類線程共享代碼和數(shù)據(jù)空間,每個線程都有自己獨立的運行棧和程序計數(shù)器,線程之間切換的開銷小。所以線程的創(chuàng)建、銷毀、調度性能遠遠優(yōu)于進程。
在引入多線程模型后,進程和線程在程序執(zhí)行過程中的分工就相當明確了,進程負責分配和管理系統(tǒng)資源,線程負責CPU調度運算,也是CPU切換時間片的最小單位。對于任何一個進程來講,即便我們沒有主動去創(chuàng)建線程,進程也是默認有一個主線程的。
3.它們在Linux內核中實現(xiàn)方式有何不同?
在Linux 里面,無論是進程,還是線程,到了內核里面,我們統(tǒng)一都叫任務(Task),由一個統(tǒng)一的結構 task_struct 進行管理,這個task_struct 數(shù)據(jù)結構非常復雜,囊括了進程管理生命周期中的各種信息。
在Linux操作系統(tǒng)內核初始化時會創(chuàng)建第一個進程,即0號創(chuàng)始進程。隨后會初始化1號進程(用戶進程祖宗:/usr/lib/systemd/systemd),2號進程(內核進程祖宗:[kthreadd]),其后所有的進程線程都是在他們的基礎上fork出來的。
我們一般都是通過fork系統(tǒng)調用來創(chuàng)建新的進程,fork 系統(tǒng)調用包含兩個重要的事件,一個是將 task_struct 結構復制一份并且初始化,另一個是試圖喚醒新創(chuàng)建的子進程。
我們說無論是進程還是線程,在內核里面都是task,管起來不是都一樣嗎?到底如何區(qū)分呢?其實,線程不是一個完全由內核實現(xiàn)的機制,它是由內核態(tài)和用戶態(tài)合作完成的。
創(chuàng)建進程的話,調用的系統(tǒng)調用是 fork,會將五大結構 files_struct、fs_struct、sighand_struct、signal_struct、mm_struct 都復制一遍,從此父進程和子進程各用各的數(shù)據(jù)結構。而創(chuàng)建線程的話,調用的是系統(tǒng)調用 clone,五大結構僅僅是引用計數(shù)加一,也即線程共享進程的數(shù)據(jù)結構。
4.所以它們到底有哪些區(qū)別?
功能:進程是操作系統(tǒng)資源分配的基本單位,而線程是任務調度和執(zhí)行的基本單位
開銷:每個進程都有獨立的內存空間,存放代碼和數(shù)據(jù)段等,程序之間的切換會有較大的開銷;線程可以看做輕量級的進程,共享內存空間,每個線程都有自己獨立的運行棧和程序計數(shù)器,線程之間切換的開銷小。
運行環(huán)境:在操作系統(tǒng)中能同時運行多個進程;而在同一個進程(程序)中有多個線程同時執(zhí)行(通過CPU調度,在每個時間片中只有一個線程執(zhí)行)
創(chuàng)建過程:在創(chuàng)建新進程的時候,會將父進程的所有五大數(shù)據(jù)結構復制新的,形成自己新的內存空間數(shù)據(jù),而在創(chuàng)建新線程的時候,則是引用進程的五大數(shù)據(jù)結構數(shù)據(jù),但是線程會有自己的私有數(shù)據(jù)、??臻g。
進程和線程其實在cpu看來都是task_struct結構的一個封裝,執(zhí)行不同task即可,而且在cpu看來就是在執(zhí)行這些task時候遵循對應的調度策略以及上下文資源切換定義,包括寄存器地址切換,內核棧切換。所以對于cpu而言,進程和線程是沒有區(qū)別的。
附:我們通常所說的上下文切換具體指什么?
操作系統(tǒng)抽象出一個進程的概念,讓應用程序專心于實現(xiàn)自己的業(yè)務邏輯既可,對應用程序屏蔽了CPU調度、內存管理等硬件細節(jié),而且在有限的CPU上可以“同時”進行許多個任務。但是它為用戶帶來方便的同時,也引入了一些額外的開銷。
在操作系統(tǒng)中,由于CPU的時間片調度策略,從一個進程切換到另一個進程需要保存當前進程的狀態(tài)并恢復另一個進程的狀態(tài):當前運行任務轉為就緒(或者掛起、刪除)狀態(tài),另一個被選定的就緒任務成為當前任務。上下文切換包括保存當前任務的運行環(huán)境,恢復將要運行任務的運行環(huán)境。
在上下文切換過程中,CPU會停止處理當前運行的程序,并保存當前程序運行的具體位置以便之后繼續(xù)運行。從這個角度來看,上下文切換有點像我們同時閱讀幾本書,在來回切換書本的同時我們需要記住每本書當前讀到的頁碼。
在三種情況下可能會發(fā)生上下文切換:中斷處理,多任務處理,內核/用戶態(tài)切換。
在中斷處理中,其他程序”打斷”了當前正在運行的程序。當CPU接收到中斷請求時,會在正在運行的程序和發(fā)起中斷請求的程序之間進行一次上下文切換。
在多任務處理中,CPU會在不同程序之間來回切換,每個程序都有相應的處理時間片,CPU在兩個時間片的間隔中進行上下文切換。
在Linux中進行內核/用戶態(tài)切換也會進行上下文切換,進行系統(tǒng)調用時,CPU寄存器里原來用戶態(tài)的指令位置需要先保存起來。接著,為了執(zhí)行內核態(tài)代碼,CPU寄存器需要更新為內核態(tài)指令的新位置。最后才是跳轉到內核態(tài)運行內核任務。而系統(tǒng)調用結束后,CPU寄存器需要恢復原來保存的用戶態(tài),然后再切換到用戶空間,繼續(xù)運行進程,所以一次系統(tǒng)調用的過程,其實是發(fā)生了兩次CPU上下文切換。
CPU上下文切換,是保證Linux系統(tǒng)正常工作的核心功能之一,一般情況下不需要我們特別關注。
但過多的上下文切換,會把CPU時間消耗在寄存器、內核棧以及虛擬內存等數(shù)據(jù)的保存和恢復上,從而縮短進程真正運行的時間,導致系統(tǒng)的整體性能大幅下降。
-
cpu
+關注
關注
68文章
10804瀏覽量
210826 -
操作系統(tǒng)
+關注
關注
37文章
6684瀏覽量
123140 -
線程
+關注
關注
0文章
503瀏覽量
19634 -
進程
+關注
關注
0文章
201瀏覽量
13938
發(fā)布評論請先 登錄
相關推薦
評論