日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

03 | 基础篇:经常说的 CPU 上下文切换是什么意思?(上)

發(fā)布時間:2024/9/3 编程问答 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 03 | 基础篇:经常说的 CPU 上下文切换是什么意思?(上) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

上一節(jié),我給你講了要怎么理解平均負載( Load Average),并用三個案例展示了不同場景下平均負載升高的分析方法。這其中,多個進程競爭 CPU 就是一個經(jīng)常被我們忽視的問題。

我想你一定很好奇,進程在競爭 CPU 的時候并沒有真正運行,為什么還會導致系統(tǒng)的負載升高呢?看到今天的主題,你應該已經(jīng)猜到了,CPU 上下文切換就是罪魁禍首。

我們都知道,Linux 是一個多任務操作系統(tǒng),它支持遠大于 CPU 數(shù)量的任務同時運行。當然,這些任務實際上并不是真的在同時運行,而是因為系統(tǒng)在很短的時間內,將 CPU 輪流分配給它們,造成多任務同時運行的錯覺。

CPU 寄存器

而在每個任務運行前,CPU 都需要知道任務從哪里加載、又從哪里開始運行,也就是說,需要系統(tǒng)事先幫它設置好?CPU 寄存器和程序計數(shù)器(Program Counter,PC)。

CPU 寄存器,是 CPU 內置的容量小、但速度極快的內存。而程序計數(shù)器,則是用來存儲 CPU 正在執(zhí)行的指令位置、或者即將執(zhí)行的下一條指令位置。它們都是 CPU 在運行任何任務前,必須的依賴環(huán)境,因此也被叫做?CPU 上下文

CPU 上下文切換

知道了什么是 CPU 上下文,我想你也很容易理解?CPU 上下文切換。CPU 上下文切換,就是先把前一個任務的 CPU 上下文(也就是 CPU 寄存器和程序計數(shù)器)保存起來,然后加載新任務的上下文到這些寄存器和程序計數(shù)器,最后再跳轉到程序計數(shù)器所指的新位置,運行新任務。

而這些保存下來的上下文,會存儲在系統(tǒng)內核中,并在任務重新調度執(zhí)行時再次加載進來。這樣就能保證任務原來的狀態(tài)不受影響,讓任務看起來還是連續(xù)運行。

我猜肯定會有人說,CPU 上下文切換無非就是更新了 CPU 寄存器的值嘛,但這些寄存器,本身就是為了快速運行任務而設計的,為什么會影響系統(tǒng)的 CPU 性能呢?

在回答這個問題前,不知道你有沒有想過,操作系統(tǒng)管理的這些“任務”到底是什么呢?

也許你會說,任務就是進程,或者說任務就是線程。是的,進程和線程正是最常見的任務。但是除此之外,還有沒有其他的任務呢?

不要忘了,硬件通過觸發(fā)信號,會導致中斷處理程序的調用,也是一種常見的任務。

所以,根據(jù)任務的不同,CPU 的上下文切換就可以分為幾個不同的場景,也就是進程上下文切換線程上下文切換以及中斷上下文切換

這節(jié)課我就帶你來看看,怎么理解這幾個不同的上下文切換,以及它們?yōu)槭裁磿l(fā) CPU 性能相關問題。

進程上下文切換

Linux 按照特權等級,把進程的運行空間分為內核空間和用戶空間,分別對應著下圖中, CPU 特權等級的 Ring 0 和 Ring 3。

  • 內核空間(Ring 0)具有最高權限,可以直接訪問所有資源;
  • 用戶空間(Ring 3)只能訪問受限資源,不能直接訪問內存等硬件設備,必須通過系統(tǒng)調用陷入到內核中,才能訪問這些特權資源。

換個角度看,也就是說,進程既可以在用戶空間運行,又可以在內核空間中運行。進程在用戶空間運行時,被稱為進程的用戶態(tài),而陷入內核空間的時候,被稱為進程的內核態(tài)。

從用戶態(tài)到內核態(tài)的轉變,需要通過系統(tǒng)調用來完成。比如,當我們查看文件內容時,就需要多次系統(tǒng)調用來完成:首先調用 open() 打開文件,然后調用 read() 讀取文件內容,并調用 write() 將內容寫到標準輸出,最后再調用 close() 關閉文件。

那么,系統(tǒng)調用的過程有沒有發(fā)生 CPU 上下文的切換呢?答案自然是肯定的。

CPU 寄存器里原來用戶態(tài)的指令位置,需要先保存起來。接著,為了執(zhí)行內核態(tài)代碼,CPU 寄存器需要更新為內核態(tài)指令的新位置。最后才是跳轉到內核態(tài)運行內核任務。

而系統(tǒng)調用結束后,CPU 寄存器需要恢復原來保存的用戶態(tài),然后再切換到用戶空間,繼續(xù)運行進程。所以,一次系統(tǒng)調用的過程,其實是發(fā)生了兩次 CPU 上下文切換。

不過,需要注意的是,系統(tǒng)調用過程中,并不會涉及到虛擬內存等進程用戶態(tài)的資源,也不會切換進程。這跟我們通常所說的進程上下文切換是不一樣的:

  • 進程上下文切換,是指從一個進程切換到另一個進程運行。
  • 而系統(tǒng)調用過程中一直是同一個進程在運行。

所以,系統(tǒng)調用過程通常稱為特權模式切換,而不是上下文切換。但實際上,系統(tǒng)調用過程中,CPU 的上下文切換還是無法避免的。

那么,進程上下文切換跟系統(tǒng)調用又有什么區(qū)別呢?

首先,你需要知道,進程是由內核來管理和調度的,進程的切換只能發(fā)生在內核態(tài)。所以,進程的上下文不僅包括了虛擬內存、棧、全局變量等用戶空間的資源,還包括了內核堆棧、寄存器等內核空間的狀態(tài)。

因此,進程的上下文切換就比系統(tǒng)調用時多了一步:在保存當前進程的內核狀態(tài)和 CPU 寄存器之前,需要先把該進程的虛擬內存、棧等保存下來;而加載了下一進程的內核態(tài)后,還需要刷新進程的虛擬內存和用戶棧。

如下圖所示,保存上下文和恢復上下文的過程并不是“免費”的,需要內核在 CPU 上運行才能完成。

根據(jù)?Tsuna?的測試報告,每次上下文切換都需要幾十納秒到數(shù)微秒的 CPU 時間。這個時間還是相當可觀的,特別是在進程上下文切換次數(shù)較多的情況下,很容易導致 CPU 將大量時間耗費在寄存器、內核棧以及虛擬內存等資源的保存和恢復上,進而大大縮短了真正運行進程的時間。這也正是上一節(jié)中我們所講的,導致平均負載升高的一個重要因素。

另外,我們知道, Linux 通過 TLB(Translation Lookaside Buffer)來管理虛擬內存到物理內存的映射關系。當虛擬內存更新后,TLB 也需要刷新,內存的訪問也會隨之變慢。特別是在多處理器系統(tǒng)上,緩存是被多個處理器共享的,刷新緩存不僅會影響當前處理器的進程,還會影響共享緩存的其他處理器的進程。

知道了進程上下文切換潛在的性能問題后,我們再來看,究竟什么時候會切換進程上下文。

顯然,進程切換時才需要切換上下文,換句話說,只有在進程調度的時候,才需要切換上下文。Linux 為每個 CPU 都維護了一個就緒隊列,將活躍進程(即正在運行和正在等待 CPU 的進程)按照優(yōu)先級和等待 CPU 的時間排序,然后選擇最需要 CPU 的進程,也就是優(yōu)先級最高和等待 CPU 時間最長的進程來運行。

那么,進程在什么時候才會被調度到 CPU 上運行呢?

最容易想到的一個時機,就是進程執(zhí)行完終止了,它之前使用的 CPU 會釋放出來,這個時候再從就緒隊列里,拿一個新的進程過來運行。其實還有很多其他場景,也會觸發(fā)進程調度,在這里我給你逐個梳理下

其一,為了保證所有進程可以得到公平調度,CPU 時間被劃分為一段段的時間片,這些時間片再被輪流分配給各個進程。這樣,當某個進程的時間片耗盡了,就會被系統(tǒng)掛起,切換到其它正在等待 CPU 的進程運行。

其二,進程在系統(tǒng)資源不足(比如內存不足)時,要等到資源滿足后才可以運行,這個時候進程也會被掛起,并由系統(tǒng)調度其他進程運行。

其三,當進程通過睡眠函數(shù) sleep 這樣的方法將自己主動掛起時,自然也會重新調度。

其四,當有優(yōu)先級更高的進程運行時,為了保證高優(yōu)先級進程的運行,當前進程會被掛起,由高優(yōu)先級進程來運行。

最后一個,發(fā)生硬件中斷時,CPU 上的進程會被中斷掛起,轉而執(zhí)行內核中的中斷服務程序。

了解這幾個場景是非常有必要的,因為一旦出現(xiàn)上下文切換的性能問題,它們就是幕后兇手。

線程上下文切換

說完了進程的上下文切換,我們再來看看線程相關的問題。

線程與進程最大的區(qū)別在于,線程是調度的基本單位,而進程則是資源擁有的基本單位。說白了,所謂內核中的任務調度,實際上的調度對象是線程;而進程只是給線程提供了虛擬內存、全局變量等資源。所以,對于線程和進程,我們可以這么理解:

  • 當進程只有一個線程時,可以認為進程就等于線程。
  • 當進程擁有多個線程時,這些線程會共享相同的虛擬內存和全局變量等資源。這些資源在上下文切換時是不需要修改的。
  • 另外,線程也有自己的私有數(shù)據(jù),比如棧和寄存器等,這些在上下文切換時也是需要保存的。

這么一來,線程的上下文切換其實就可以分為兩種情況:

第一種, 前后兩個線程屬于不同進程。此時,因為資源不共享,所以切換過程就跟進程上下文切換是一樣。

第二種,前后兩個線程屬于同一個進程。此時,因為虛擬內存是共享的,所以在切換時,虛擬內存這些資源就保持不動,只需要切換線程的私有數(shù)據(jù)、寄存器等不共享的數(shù)據(jù)。

到這里你應該也發(fā)現(xiàn)了,雖然同為上下文切換,但同進程內的線程切換,要比多進程間的切換消耗更少的資源,而這,也正是多線程代替多進程的一個優(yōu)勢。

中斷上下文切換

除了前面兩種上下文切換,還有一個場景也會切換 CPU 上下文,那就是中斷。

為了快速響應硬件的事件,中斷處理會打斷進程的正常調度和執(zhí)行,轉而調用中斷處理程序,響應設備事件。而在打斷其他進程時,就需要將進程當前的狀態(tài)保存下來,這樣在中斷結束后,進程仍然可以從原來的狀態(tài)恢復運行。

跟進程上下文不同,中斷上下文切換并不涉及到進程的用戶態(tài)。所以,即便中斷過程打斷了一個正處在用戶態(tài)的進程,也不需要保存和恢復這個進程的虛擬內存、全局變量等用戶態(tài)資源。中斷上下文,其實只包括內核態(tài)中斷服務程序執(zhí)行所必需的狀態(tài),包括 CPU 寄存器、內核堆棧、硬件中斷參數(shù)等。

對同一個 CPU 來說,中斷處理比進程擁有更高的優(yōu)先級,所以中斷上下文切換并不會與進程上下文切換同時發(fā)生。同樣道理,由于中斷會打斷正常進程的調度和執(zhí)行,所以大部分中斷處理程序都短小精悍,以便盡可能快的執(zhí)行結束。

另外,跟進程上下文切換一樣,中斷上下文切換也需要消耗 CPU,切換次數(shù)過多也會耗費大量的 CPU,甚至嚴重降低系統(tǒng)的整體性能。所以,當你發(fā)現(xiàn)中斷次數(shù)過多時,就需要注意去排查它是否會給你的系統(tǒng)帶來嚴重的性能問題。

小結

總結一下,不管是哪種場景導致的上下文切換,你都應該知道:

  • CPU 上下文切換,是保證 Linux 系統(tǒng)正常工作的核心功能之一,一般情況下不需要我們特別關注。
  • 但過多的上下文切換,會把 CPU 時間消耗在寄存器、內核棧以及虛擬內存等數(shù)據(jù)的保存和恢復上,從而縮短進程真正運行的時間,導致系統(tǒng)的整體性能大幅下降。
  • 今天主要為你介紹這幾種上下文切換的工作原理,下一節(jié),我將繼續(xù)案例實戰(zhàn),說說上下文切換問題的分析方法。

    思考

    最后,我想邀請你一起來聊聊,你所理解的 CPU 上下文切換。你可以結合今天的內容,總結自己的思路和看法,寫下你的學習心得。

    總結

    以上是生活随笔為你收集整理的03 | 基础篇:经常说的 CPU 上下文切换是什么意思?(上)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。