我們都知道線程切換的開銷比進程切換的開銷小,那么小在什么地方?切換的過程是怎樣的?
無論是在多核還是單核系統中,一個CPU看上去都像是在並發的執行多個進程,這是通過處理器在進程間切換來實現的。
- 操作系統實現這種交錯執行的機制稱為上下文切換。
- 操作系統保持跟蹤進程運行所需的所有狀態信息,這種狀態,也就是上下文,它包括許多信息,例如PC和寄存器文件的當前值,以及主存的內容。
在任何一個時刻,單處理器系統都只能執行一個進程的代碼。
當操作系統決定要把控制權從當前進程轉移到某個新進程時,就會進行上下文切換,即保存當前進程的上下文,恢復新進程的上下文,然后將控制權傳遞到新進程,新進程就會從上次停止的地方開始
深入計算機系統一書中對上下文切換的表達如下圖:
如果現在有兩個並發的進程:外殼進程和hello進程。
開始只有外殼進程在運行,即等待命令行上的輸入,當我們讓他運行hello程序時,外殼通過調用一個專門的函數,即系統調用,來執行我們的請求,系統調用會將控制權傳遞給操作系統。
操作系統保存外殼進程的上下文,創建一個新的hello進程及其上下文,然后將控制權傳遞給新的hello進程。
hello進程終止后,操作系統恢復外殼進程的上下文,並將控制權傳回給他,外殼進程將繼續等待下一個命令行輸入。
上下文切換
內核為每一個進程維持一個上下文。上下文就是內核重新啟動一個被搶占的進程所需的狀態。包括一下內容:
- 通用目的寄存器
- 浮點寄存器
- 程序計數器
- 用戶棧
- 狀態寄存器
- 內核棧
-
各種內核數據結構:比如描繪地址空間的頁表,包含有關當前進程信息的進程表,以及包含進程已打開文件的信息的文件表。
進程切換
系統中的每個程序都是運行在某個進程的上下文中的。
上下文是由程序正確運行所需的狀態組成的,這個狀態包括存放在存儲器中的程序的代碼和數據,他的棧,通用目的寄存器的內容,程序計數器,環境變量以及打開文件描述符的集合。
所以進程切換就是上下文切換。
那回到最開始的問題,進程切換和線程切換有什么區別?
當然這里的線程指的是同一個進程中的線程。要想正確回答這個問題,需要理解虛擬內存。
虛擬內存
虛擬內存是操作系統為每個進程提供的一種抽象,每個進程都有屬於自己的、私有的、地址連續的虛擬內存,當然我們知道最終進程的數據及代碼必然要放到物理內存上,那么必須有某種機制能記住虛擬地址空間中的某個數據被放到了哪個物理內存地址上,這就是所謂的地址空間映射,那么操作系統是如何記住這種映射關系的呢,答案就是頁表。
每個進程都有自己的虛擬地址空間,進程內的所有線程共享進程的虛擬地址空間。
現在我們就可以來回答這個面試題了。
進程切換和線程切換的區別
最主要的一個區別在於進程切換涉及虛擬地址空間的切換而線程不會。因為每個進程都有自己的虛擬地址空間,而線程是共享所在進程的虛擬地址空間的,因此同一個進程中的線程進行線程切換時不涉及虛擬地址空間的轉換。
有的同學可能還是不太明白,為什么虛擬地址空間切換會比較耗時呢?
現在我們已經知道了進程都有自己的虛擬地址空間,把虛擬地址轉換為物理地址需要查找頁表,頁表查找是一個很慢的過程,因此通常使用Cache來緩存常用的地址映射,這樣可以加速頁表查找,這個cache就是TLB(translation Lookaside Buffer,我們不需要關心這個名字只需要知道TLB本質上就是一個cache,是用來加速頁表查找的)。由於每個進程都有自己的虛擬地址空間,那么顯然每個進程都有自己的頁表,那么當進程切換后頁表也要進行切換,頁表切換后TLB就失效了,cache失效導致命中率降低,那么虛擬地址轉換為物理地址就會變慢,表現出來的就是程序運行會變慢,而線程切換則不會導致TLB失效,因為線程線程無需切換地址空間,因此我們通常說線程切換要比較進程切換塊,原因就在這里。
參考鏈接:
1. https://blog.csdn.net/github_37382319/article/details/97273713
2. https://blog.csdn.net/xiangwanpeng/article/details/78196539