拋出一個(gè)問題:進(jìn)程在競爭CPU的時(shí)候并沒有真正運(yùn)行,為什么還導(dǎo)致系統(tǒng)的負(fù)載升高?
Linux是一個(gè)多任務(wù)操作系統(tǒng),支持大于CPU數(shù)量的任務(wù)同時(shí)運(yùn)行纤勒,當(dāng)然實(shí)際上并不是真的同時(shí)進(jìn)行,而是操作系統(tǒng)在很短時(shí)間內(nèi)將CPU輪流分配給它們隆檀,造成多任務(wù)同時(shí)運(yùn)行的錯(cuò)覺摇天。
2.1 上下文
每個(gè)任務(wù)運(yùn)行前會有些依賴環(huán)境粹湃,這些環(huán)境就叫上下文,CPU需要知道任務(wù)從那里加載泉坐,又從哪里開始運(yùn)行为鳄,這個(gè)工作需要操作系統(tǒng)預(yù)先設(shè)置好CPU寄存器和程序計(jì)數(shù)器
CPU寄存器:CPU內(nèi)置的容量小、速度極快的內(nèi)存腕让。
程序計(jì)數(shù)器:存儲CPU正在執(zhí)行的指令位置孤钦、或即將執(zhí)行下一條指令的位置。
2.2上下文切換
2.2.1 定義
把前一個(gè)任務(wù)的CPU上下文保存起來纯丸,然后加載新任務(wù)的上下文到寄存器和程序計(jì)數(shù)器偏形,然后操作系統(tǒng)執(zhí)行程序計(jì)數(shù)器指的新位置,運(yùn)行新任務(wù)
保存下來的上下文輝存儲在系統(tǒng)內(nèi)核匯總液南,在任務(wù)重新調(diào)度執(zhí)行時(shí)候再次加載壳猜,保證原來任務(wù)的狀態(tài)不受影響勾徽,讓任務(wù)看起來是連續(xù)運(yùn)行的滑凉。
2.2.2 分類
問題:上下文切換無非是更新了CPU寄存器的值,為什么會影響系統(tǒng)的CPU性能呢喘帚?
回答這個(gè)問題之前畅姊,想一下操作系統(tǒng)的任務(wù)有哪些?進(jìn)程任務(wù)吹由、線程任務(wù)若未、中斷任務(wù)
上下文切換分類:進(jìn)程上下文切換、線程上下文切換倾鲫、中斷上下文切換
2.3 進(jìn)程上下文切換
2.3.1 系統(tǒng)調(diào)用 — 特權(quán)模式的切換(涉及上下文切換)
進(jìn)程運(yùn)行空間分級:R0粗合、R1、R2乌昔、R3
內(nèi)核空間(R0):具有最高的權(quán)限隙疚,進(jìn)程運(yùn)行在該空間才可以直接訪問所有資源;此時(shí)進(jìn)程處在內(nèi)核態(tài)磕道;
用戶空間(R3):只能訪問受限資源供屉,進(jìn)程運(yùn)行在該空間不能直接訪問內(nèi)存等硬件設(shè)備,必須經(jīng)過系統(tǒng)調(diào)用陷入到內(nèi)核中溺蕉,才能訪問這些特權(quán)資源伶丐;此時(shí)進(jìn)程處在用戶態(tài);
用戶態(tài)到內(nèi)核態(tài)的轉(zhuǎn)變疯特,需要通過【系統(tǒng)調(diào)用】哗魂;
系統(tǒng)調(diào)用過程中發(fā)生了2次上下文切換。
第一次:從用戶態(tài)切換到內(nèi)核態(tài)漓雅,具體工作:CPU寄存器存儲的用戶態(tài)指令位置被保存录别,CPU寄存器更新為內(nèi)核態(tài)指定的新位置羹与,進(jìn)程跳轉(zhuǎn)到內(nèi)核態(tài)中運(yùn)行內(nèi)核任務(wù);
第二次:從內(nèi)核態(tài)切換到用戶態(tài)庶灿,具體工作:CPU寄存器更新為原來保存的用戶態(tài)指定的位置纵搁,進(jìn)程跳轉(zhuǎn)到用戶態(tài)中繼續(xù)運(yùn)行任務(wù);
系統(tǒng)調(diào)用不涉及虛擬內(nèi)存等進(jìn)程用戶態(tài)資源往踢,也不回切換進(jìn)程腾誉,這跟我們平時(shí)說的進(jìn)程上下文切換是不一樣的。
進(jìn)程上下文切換峻呕,是指一個(gè)進(jìn)程切換到另一個(gè)進(jìn)程利职;
系統(tǒng)調(diào)用過程中一直是同一個(gè)進(jìn)程在運(yùn)行
2.3.2 進(jìn)程上下文切換和系統(tǒng)調(diào)用有什么區(qū)別?
首先瘦癌,進(jìn)程的切換是由內(nèi)核管理和調(diào)度的猪贪,進(jìn)程的切換只發(fā)生在內(nèi)核態(tài),所以讯私,進(jìn)程的上下文切換不僅包括虛擬內(nèi)存热押、棧、全局變量等用戶空間資源斤寇,還包括內(nèi)核堆棧桶癣、寄存器等內(nèi)核空間資源的狀態(tài);
因此娘锁,進(jìn)程的上下文切換比系統(tǒng)調(diào)用多了一步牙寞,在保存內(nèi)核態(tài)堆棧、寄存器之前莫秆,需要把進(jìn)程的虛擬內(nèi)存间雀、棧保存下來,加載下一進(jìn)程的內(nèi)核態(tài)后镊屎,還需要刷新虛擬內(nèi)存和用戶棧惹挟;
每次上下文切換都在幾十納米到幾微秒的CPU時(shí)間,這個(gè)時(shí)間還是相當(dāng)可觀的杯道,特別在上下文切換頻繁的情況匪煌,很容易導(dǎo)致CPU將大量的運(yùn)行時(shí)間花費(fèi)在寄存器、內(nèi)核棧党巾、虛擬內(nèi)存等資源的保存和恢復(fù)上萎庭,進(jìn)而大大縮短了CPU真正運(yùn)行的時(shí)間。
2.3.3 什么時(shí)候會發(fā)生進(jìn)程切換上下文
進(jìn)程需要調(diào)度的時(shí)候(PS齿拂,這不是廢話嘛)
Linux為每個(gè)CPU都維護(hù)了一個(gè)就緒隊(duì)列驳规,將活躍的進(jìn)程安裝優(yōu)先級和等待時(shí)間來排序,然后選擇最需要CPU的進(jìn)程署海,也就是優(yōu)先級最高和等待時(shí)間最長的進(jìn)程來運(yùn)行吗购。
那么進(jìn)程在什么時(shí)候才會被調(diào)度到CPU上運(yùn)行呢医男?
其一:為了保證所有進(jìn)程可以得到公平調(diào)度,CPU時(shí)間被劃分為一段段時(shí)間片捻勉,這些時(shí)間片再被輪流分配給各個(gè)進(jìn)程镀梭。
當(dāng)某個(gè)進(jìn)程的時(shí)間片耗盡了,就會被掛起踱启,切換到其他正在等待CPU的進(jìn)程運(yùn)行报账;
其二:進(jìn)程在系統(tǒng)資源不足時(shí)(內(nèi)存資源不足),要等到資源滿足后才可以運(yùn)行埠偿,這個(gè)時(shí)候進(jìn)程也會被掛起透罢,并由系統(tǒng)調(diào)度其他進(jìn)程運(yùn)行;
其三:進(jìn)程通過sleep這樣的方法主動將自己掛起冠蒋,也會重新調(diào)度羽圃;
其四:當(dāng)有更高優(yōu)先級進(jìn)程運(yùn)行時(shí),為了保證高優(yōu)先級進(jìn)程的運(yùn)行抖剿,當(dāng)前進(jìn)程會被掛起朽寞;
其五:發(fā)生硬件中斷時(shí),CPU上的進(jìn)程會被中斷掛起牙躺,轉(zhuǎn)而執(zhí)行內(nèi)核中的中斷服務(wù)程序愁憔。
這些場景很可能就是出現(xiàn)上下文切換性能問題的幕后元兇腕扶。
2.4線程上下文切換
線程和進(jìn)程最大的區(qū)別在于:線程是調(diào)度的基本單位孽拷,進(jìn)程是資源擁有的基本單位;
上面這句話還可以這么理解:
當(dāng)進(jìn)程只有一個(gè)線程時(shí)半抱,可以認(rèn)為進(jìn)程就等于線程脓恕;
當(dāng)進(jìn)程擁有多個(gè)線程時(shí),這些線程會共享相同的虛擬內(nèi)存和全局變量等資源窿侈,這些資源在上下文切換時(shí)是不需要修改的炼幔。
另外線程也有自己私有的數(shù)據(jù),比如線程棧和寄存器等史简,這些在上下文切換時(shí)候需要保存乃秀。
分兩種情況:
第一種:切換的兩個(gè)線程屬于不同進(jìn)程,此時(shí)因?yàn)橘Y源不共享圆兵,所以切換過程跟進(jìn)程上下文切換一樣跺讯。
第二種:前后兩個(gè)線程都屬于同一個(gè)進(jìn)程,此時(shí)虛擬內(nèi)存是共享的殉农,所以在切換時(shí)刀脏,只需切換線程私有數(shù)據(jù)、寄存器等不共享的數(shù)據(jù)超凳。
因此線程上下文切換要比進(jìn)程間的上下文切換消耗更少的資源愈污,這也是多線程代替多進(jìn)程的一個(gè)優(yōu)勢耀态。
2.5 中斷上下文切換
為了快速響應(yīng)硬件的事件,中斷處理會打斷進(jìn)程的正常調(diào)度和執(zhí)行暂雹,轉(zhuǎn)而調(diào)用中斷處理程序首装,響應(yīng)設(shè)備事件。在打斷進(jìn)程時(shí)杭跪,需要將當(dāng)前進(jìn)程的狀態(tài)保存簿盅,中斷上下文,其實(shí)只包括內(nèi)核態(tài)中斷服務(wù)程序執(zhí)行的必要狀態(tài)揍魂,包括CPU寄存器桨醋、內(nèi)核堆棧、硬件中斷參數(shù)等现斋。
中斷處理比進(jìn)程有更高的優(yōu)先級喜最。
中斷次數(shù)過多,也會影響系統(tǒng)性能庄蹋。