2011-09-16 96 views
23

我被要求描述兩個不同進程之間的上下文切換(1)以及(2)同一進程中兩個不同線程之間所涉及的步驟。上下文切換的步驟

  1. 在上下文切換期間,內核會將舊進程的上下文保存在PCB中,然後加載預定運行的新進程的保存上下文。
  2. 操作系統可以調度同一進程中兩個不同線程之間的上下文切換,以便它們似乎並行執行,因此通常比兩個不同進程之間的上下文切換快。

這是太籠統了,還是你會添加什麼來解釋清楚的過程?

回答

43

由於過程切換總是涉及線程切換,因此以相反的順序解釋它們要容易得多。

單核CPU上的一個典型線程上下文切換髮生這樣的:

  1. 所有上下文切換由一個「中斷」啓動。這可能是運行驅動程序的實際硬件中斷(例如,來自網卡,鍵盤,內存管理或計時器硬件)或軟件調用(系統調用),它執行類似硬件中斷的調用序列進入操作系統。在驅動程序中斷的情況下,操作系統提供了一個驅動程序可以調用的入口點,而不是執行「正常」直接中斷返回&,因此如果驅動程序需要OS設置線程,則允許驅動程序通過OS調度程序退出準備好了,(例如它已經發出信號量的信號)。

  2. 非平凡系統必須啓動硬件保護級別更改才能進入內核狀態,以便可以訪問內核代碼/數據等。

  3. 被中斷的線程的核心狀態必須被保存。在一個簡單的嵌入式系統中,這可能只是將所有寄存器推送到線程堆棧並將堆棧指針保存在其線程控制塊(TCB)中。

  4. 許多系統在此階段切換到OS專用堆棧,以便大量的OS內部堆棧要求不會在每個線程的堆棧中產生。

  5. 可能需要標記出現中斷狀態更改的線程堆棧位置,以允許嵌套中斷。

  6. 驅動程序/系統調用運行,並可以通過從不同線程優先級的內部隊列中添加/刪除TCB來更改就緒線程集。網卡驅動程序可能已經設置了一個事件,或者發出了另一個線程正在等待的信號量,以便線程將被添加到就緒集中,或者正在運行的線程可能已經調用了sleep(),因此被選擇將其自身從ready套件中移除。

  7. 運行OS調度程序算法以決定下一個要運行的線程,通常是優先級最高的就緒隊列的前端隊列。如果下一個運行的線程屬於與先前運行的線程不同的進程,那麼這裏需要一些額外的東西(見後面的內容)。

  8. 從TCB爲該線程保存的堆棧指針被檢索並加載到硬件堆棧指針中。

  9. 恢復所選線程的核心狀態。在我的簡單系統上,寄存器將從所選線程的堆棧中彈出。更復雜的系統必須處理恢復用戶級保護。

  10. 執行中斷返回,將執行轉移到選定的線程。

在多核CPU的情況下,事情更復雜。調度程序可能會決定當前在另一個核心上運行的線程可能需要停止並由剛準備好的線程替換。它可以通過使用其處理器間驅動程序來硬件中斷運行必須停止的線程的內核。在這種操作的複雜性,對所有其他的東西上面,是一個很好的理由,以避免編寫操作系統內核:)

一個典型的進程上下文切換髮生這樣的事情:

  1. 進程上下文切換啓動通過線程上下文切換,所有上述1-9都將需要發生。

  2. 在上面的步驟5中,調度程序決定運行屬於與擁有先前運行的線程的進程不同的進程的線程。

  3. 內存管理硬件必須加載新進程的地址空間,即任何允許新進程的線程訪問其內存的選擇器/段/標記/任何內容。

  4. 任何FPU硬件的上下文需要從PCB保存/恢復。

  5. 可能還有其他需要保存/恢復的進程專用硬件。

在任何實際系統中,機構是體系結構相關和上面是一個粗略的和不完全的導要麼上下文切換的影響。還有一些流程開關生成的其他開銷並不完全是開關的一部分 - 在進程切換後可能會有額外的緩存刷新和頁面錯誤,因爲它的某些內存可能已被分頁出來,到擁有之前運行的線程的進程。

+2

能解釋單芯上下文切換的步驟4中更詳細?爲什麼嵌套中斷需要「標記」?另外,寄存器的確切位置在哪裏? (假設Linux) –

7

我希望我能提供更詳細/清晰的圖片。

首先,OS調度線程,而不是進程,因爲線程是系統中唯一的可執行單元。進程切換隻是一個線程切換,其中線程屬於不同的進程。由於這種情況在一般的開關程序中很常見。

  1. 調度程序應該被調用。有三種基本情況:

    • 不自主開關。發生線程事件的一些外部事件影響了計劃。例如,定時器環喚醒了一個具有高優先級的線程,HDD控制器報告請求的文件部分已經被讀入內存,並且等待它的線程可以繼續執行,系統定時器已經告訴內核你的線程有跑出其時間量等
    • 自願。線程通過系統調用明確請求重新調度。例如請求產量,或請求睡眠,或請求等待,直到互斥體將被釋放。
    • 半自願。線程隱式觸發重新調度執行一些不相關的系統調用。例如,它要求系統讀取文件。 OS已將此請求路由到磁盤控制器,並且不會浪費時間忙於等待結果決定切換到另一個線程。
  2. 在所有情況下,爲了能夠執行線程切換,應該將控制權交給內核。在無意識開關的情況下,這種控制過程通過中斷執行,在自願(和半自願)情況下,通過系統調用傳遞控制。

  3. 在這兩種情況下,進入內核都是由CPU輔助的。處理器執行權限檢查,記住線程被搶佔的位置(以便能夠在將來恢復),從線程堆棧的用戶部分切換到其內核對象,並將控制權傳遞給預定義的知名點內核代碼。

  4. 內核執行的第一個動作是保存CPU寄存器的內容,內核將爲其自己的任務重用。通常,內核只使用通用CPU寄存器,並通過推入堆棧來保存它們。
  5. 然後內核處理主要請求 - 處理中斷,或準備文件讀取請求或執行計時器設置。
  6. 在請求處理的某個時刻,內核會執行一個動作,它會影響當前線程的狀態(決定在此線程中沒有任何待辦事項,我們應該等待)或影響另一個線程的狀態(由於收到中斷或由於釋放的互斥鎖等原因,新線程可以運行)。
  7. 內核調用調度程序。調度員必須做出兩個決定。首先是關於如何處理當前線程。它應該被封鎖嗎?如果是這樣,進入等待隊列應該放置什麼?如果線程被強制切換,它將被放置到runqueue的末尾,如果線程被阻塞,因爲它等待某些事件,它將被放入一個等待隊列中。第二個決定是關於下一個要運行的線程。
  8. 一旦做出兩個決定,scheduller執行上下文swicth傳遞給它兩個參數 - 當前線程和下一個線程的線程控制塊。
  9. 上下文切換本身由三個主要步驟組成。首先,內核會計算CPU寄存器線程實際使用的內容,並將其內容保存在堆棧或outgoint線程的TCB中。如果線程不使用FPU和SSE寄存器(例如IA-32平臺),則其內容不會被保存。
  10. 第二步是上下文切換的核心。內核將當前指令指針推送到堆棧,堆棧指針的值保存在傳出線程的TCB中。然後它從傳入線程的TCB加載到CPU新的堆棧指​​針並從其頂部彈出指令指針。那!新的活動堆棧意味着新的活動線程。從這一點開始,系統的其餘部分會認爲它在傳入線程的上下文中起作用。
  11. 在第三步,內核計算出傳入線程實際使用的寄存器並將之前保存的內容(請參閱步驟1)加載回CPU。
  12. 然後內核檢查兩個線程(傳入和傳出)是否屬於相同的進程。如果它們屬於不同的進程(人們稱之爲進程切換的情況),則內核重置當前地址空間,將MMU指向新的一組虛擬到物理地址轉換表。作爲此進程的一部分,CPU會刷新將虛擬緩存到物理地址轉換規則的轉換後備緩衝區(TLB)。新的虛擬地址空間意味着先前緩存的規則現在不正確。請注意,這是關注進程的整個上下文切換操作集中的唯一步驟!
  13. 內核爲傳入線程準備線程本地存儲。例如,將相應的內存頁映射到指定的地址,或者例如在IA-32上通用的方法是加載指向傳入線程的TLS數據的新段。
  14. 最後,內核加載到傳入線程的內核部分的CPU地址中。在此之後,每個新的內核調用將使用傳入線程堆棧的內核部分,並且不會損壞傳出線程堆棧中存儲的數據。
  15. 內核可以執行的另一個步驟是對系統定時器進行重新編程。做這個內核要求定時器響一聲,並在一段時間後將控制權交給內核。這段時間被稱爲線程的時間量。
  16. 最後,內核喜歡在上下文切換期間收集統計信息,包括soch信息,線程消耗多少CPU時間,系統中上下文切換如何以實時爲單位發生,線程被調用了多少次,它們有多少次釋放的CPU是自願的和非自願的,他們有多少次用完了它們的量子。部分統計數據由scheduller使用,試圖做出更優化的決策。統計數據的另一個目的是傳遞給系統管理員和用戶,以向他們展示系統背後發生的事情。
  17. 線程切換可以認爲是在此時完成的,並且內核繼續以前中斷的系統操作。例如,正在等待文件讀取的線程檢查內存中的讀取結果並對其進行處理。或者在某個大型系統活動的中間被阻塞在互斥體上的線程繼續該活動。
  18. 最後,稍後或更早的線程完成其系統活動並希望返回到用戶模式以繼續它的初始任務。此時,內核從通用寄存器的內核堆棧內容中彈出,這些內容在進入內核期間先前保存並要求CPU執行返回到用戶模式。
  19. CPU捕獲指令指針和堆棧指針的值,它們在進入內核模式期間先前保存並恢復它們。這樣做也會將線程從其堆棧的內核部分切換回堆棧的用戶部分。最後,CPU將將要執行的代碼權限重置爲一個更有限的集合(例如,禁止使用特殊的系統指令,或禁止訪問kerel代碼和數據)。最後,CPU將控制權交給線程最初被搶佔的點。在系統調用線程將在系統調用被調用的地方進行,通過捕獲和處理其結果。在通過中斷搶佔的情況下,線程將在中斷髮生時的同一點繼續執行。在這種情況下,它甚至會完全不知道它被打斷了。

總結的一些注意事項:

  1. 內核調度和執行只有線程,而不是進程。由於這種情況,swicth在線程之間發生。
  2. 屬於不同進程的腳本之間的上下文切換過程本質上與屬於同一進程的線程之間的過程相同。第一種情況只有一個額外的步驟 - 加載新的虛擬地址空間(導致TLB刷新)。
  3. 線程的上下文存儲在線程堆棧的內核部分或線程TCB(不在PCB!中)。
  4. 線程切換會導致性能損失。線程切換的直接成本很高。甚至由緩存污染和TLB刷新(如果在交換過程中重新加載虛擬地址空間)創建更大的不合理成本。
2
  1. 在交換機,當前正在執行的進程的狀態必須以某種方式保存起來,這樣,當它被重新調度,這種狀態可以被恢復。
  2. 進程狀態包括進程可能使用的所有寄存器,特別是程序計數器,以及可能需要的任何其他特定於操作系統的數據。這通常存儲在稱爲process control block(PCB)或開關框架的數據結構中。
  3. PCB可能存儲在內核內存的每個進程堆棧中(而不是用戶模式調用堆棧),或者可能存在某些特定的操作系統定義的數據結構以用於此信息。 PCB的句柄被添加到準備運行的進程隊列中,通常稱爲就緒隊列。
  4. 由於操作系統已經有效地暫停了一個進程的執行,因此它可以通過從就緒隊列中選擇進程並恢復其PCB來切換上下文。在這樣做時,來自PCB的程序計數器被加載,並且因此執行可以在選擇的過程中繼續。進程和線程優先級可以影響從就緒隊列中選擇哪個進程(即,它可以是優先級隊列)。

Context Switch

(來源:Context switch