天天看點

作業系統 | 程序/線程切換問題一. 底層邏輯:二. 虛拟記憶體知識複習三. 程序與線程 四. 程序切換在切換什麼五. 使用者級線程如何切換?六. 程序切換和線程切換七. 為什麼虛拟位址切換很慢

一. 底層邏輯:

1. 一個晶片上通常會運作多個獨立的邏輯流,于是就有了并發。

2. 為了解決并發所帶來的上下文切換問題,是以引入了程序。

3. 程序就這樣抽象出一個概念,搭配虛拟記憶體、程序表之類的東西,用來管理獨立的程式運作、切換。

4. 程式的運作涉及大量的計算機資源配置,出于安全性考慮,這些資源的配置設定,需要陷入核心,切換到作業系統,由作業系統來進行資源的配置。

5. 由于程序的切換需要反複進入核心,置換掉一大堆的狀态,程序數一高,大部分的系統資源都被程序切換吃掉了。

6. 為了解決這個問題,搞出線程,這個地方阻塞了,但還有其它地方的邏輯流可以計算,這些邏輯流是共享一個位址空間的,不需要特别麻煩的切換頁表、重新整理TLB,隻需要将寄存器重新整理一遍就行,比程序切換開銷少。

7. 如果連時鐘阻塞、 線程切換這些功能我們都不需要了,自己在程序裡面寫一個邏輯流排程的東西。那麼我們即可以利用到并發優勢,又可以避免反複系統調用,還有程序切換造成的開銷,分分鐘給你上幾千個邏輯流不費力。這就是使用者态線程。

8. 實作一個使用者态線程有兩個必須要處理的問題:一是碰着阻塞式I\O會導緻整個程序被挂起;二是由于缺乏時鐘阻塞,程序需要自己擁有排程線程的能力。如果一種實作使得每個線程需要自己通過調用某個方法,主動交出控制權。那麼我們就稱這種使用者态線程是協作式的,即是協程。

9. 本質上,協程就是使用者空間下的線程。協程的優點是,讓原來要使用異步+回掉的方式寫的代碼,可以用看似同步的方式寫出來。

總結:

1. 由于并發的關系引入了程序,讓不同的邏輯流可以并行執行。但是程序的切換需要陷入核心态,置換狀态資訊,造成極大的系統開銷。

2. 為了減少這種系統開銷,進而引入線程,線程可以解決并發的問題,同時也減少了系統的開銷資源,由于線程間可以共享位址空間,是以線上程切換的時候,可以不用切換頁表全局目錄。

3. 自己在程序裡面寫一個邏輯流排程的東西。那麼我們即可以利用到并發優勢,又可以避免反複系統調用,還有程序切換造成的開銷,分分鐘給你上幾千個邏輯流不費力。這就是使用者态線程。(協程)

二. 虛拟記憶體知識複習

虛拟記憶體是什麼?解決什麼問題?如何映射?請看這篇部落格:

作業系統面試題:虛拟記憶體是什麼,解決了什麼問題,如何映射?_我是方小磊的部落格-CSDN部落格_虛拟記憶體面試題

虛拟記憶體是作業系統為每個程序提供的一種抽象,每個程序都有屬于自己的、私有的、位址連續的虛拟記憶體,當然我們知道最終程序的資料及代碼必然要放到實體記憶體上,那麼必須有某種機制能記住虛拟位址空間中的某個資料被放到了哪個實體記憶體位址上,這就是所謂的位址空間映射,也就是虛拟記憶體位址與實體記憶體位址的映射關系,那麼作業系統是如何記住這種映射關系的呢,答案就是頁表,頁表中記錄了虛拟記憶體位址到實體記憶體位址的映射關系。有了頁表就可以将虛拟位址轉換為實體記憶體位址了,這種機制就是虛拟記憶體。

每個程序都有自己的虛拟位址空間,程序内的所有線程共享程序的虛拟位址空間。

三. 程序與線程 

線程

  • 多線程有多個控制序列,單線程隻有一個控制序列
  • 在一個程式裡的一個執行路線就叫做線程(thread)。

    更準确的定義是:線程是一個程序内部的控制序列(指令序列)

  • 一切程序至少都有一個執行線程

程序

程序的相關概念如下:

  • (1)程式: 完成特定功能的一系列有序指令的集合,通過編譯連結成可執行檔案:
  • (2)可執行檔案: 稱之為程式,代碼段(指令)+資料段(指令操作的程式),儲存在磁盤上
  • (3)程序: 程式的一次動态執行過程,代碼段+資料段+堆棧段+PCB(程序控制塊:程序運作狀态(包括:就緒狀态,運作狀态,等待狀态),程序上下文,程序執行的CPU狀态,目前運作到哪個位址:IP指令指針+SP堆棧指針+寄存器狀态)

程序與程式的對比如下:

  • 程序 程式
    動态的(在不斷的推進的過程中,會更改資料段,産生一些臨時的資料儲存在堆棧段當中,且PCB的的狀态也是不斷發生改變的) 靜态的(儲存在磁盤上,程式檔案資訊是不會改變的)
    短暫的(隻是程式的一次動态執行過程) 永久的
    代碼段+資料段+堆棧段+PCB 代碼段+資料段
  • 一個程序隻能對應一個程式(一個程序是一個程式的動态執行執行個體,一個程式可以運作多個執行個體,一個程式可以對應多個程序)
  • 程序資料結構:多個線程共享下面的資訊

        (1)程序ID,uid,gid,有效uid,有效gid,cwd目前工作狀态,

        (2)位址空間Memory Map每個程序都有2^32=4GB 的位址空間(對于32位的機器而言),

        (3)信号分發表Signal Dispatch Table處理信号,

        (4)檔案描述表File Descriptor(維護目前打開的檔案),

  • 程序資料結構:不共享的資訊如下:

        (1)CPU state(單線程隻有一個,多線程有多個,儲存執行程式所必要的資訊):優先級Poriority,

        (2)信号屏蔽字Signal Mask,

        (3)寄存器Registers,

        (4)核心堆棧Kernel Stack

  • 程序是資源競争的基本機關
  • 線程是程式執行的最小機關
  • 線程共享程序資料,但也擁有自己的一部分資料

        (1)線程ID

        (2)一組寄存器:IP指令指針+SP堆棧指針+通用寄存器等

        (3)棧:線程的局部變量

        (4)errno:每個線程都有一個errno

        (5)信号狀态:每個線程都有一個對信号的處理狀态

        (6)優先級

  • fork和建立新線程的差別

        (1)當一個程序執行一個fork調用的時候,會建立出程序的一個新拷貝,新程序将擁有它自己的變量和它自己的PID。

        這個新程序的運作時間是獨立的,他在執行時,幾乎完全獨立于建立他的程序

        (2)在程序裡面建立一個新線程的時候,新的執行線程會擁有自己的堆棧(是以,也就擁有自己的局部變量),但要與他的建立者共享全局變量,檔案描述符,信号處理器和目前的工作目錄狀态

四. 程序切換在切換什麼

1. 切換核心态堆棧(由于程序切換需要陷入核心态,使用者态到核心态的切換,必然會涉及到核心态堆棧的切換)

2. PCB的切換(重新引起排程時,作業系統會找到新的程序的PCB,并完成該程序與新程序PCB的切換,寄存器的值,硬體上下文)

    —ip(instruction pointer):指向目前執行指令的下一條指令

    —bp(base pointer): 用于存放執行中的函數對應的棧幀的棧底位址

    —sp(stack poinger): 用于存放執行中的函數對應的棧幀的棧頂位址

    —cr3:頁目錄基址寄存器,儲存頁目錄表的實體位址

3. 切換頁表全局目錄(由于程序是獨享記憶體空間的,程序對記憶體的操作涉及到虛拟記憶體,頁表是實作方式,是以切程序記憶體,就是切換頁表)

4、重新整理TLB(TLB本質是一種cache,用于完成虛拟位址和實體位址間的轉換)

總結:

1.  程序切換必然陷入核心态,那就必然涉及到核心态堆棧切換。

2.  每一個程序都有一個PCB用于描述控制程序的運作。程序切換必然切換PCB。

3.  每個程序獨享記憶體空間,虛拟記憶體到實體記憶體的映射,通過頁表實作,是以要切換頁表。

4.  虛拟位址和實體位址間的轉換,還涉及到TLB(cache),是以需要刷TLB。

五. 使用者級線程如何切換?

作業系統 | 程式/線程切換問題一. 底層邏輯:二. 虛拟記憶體知識複習三. 程式與線程 四. 程式切換在切換什麼五. 使用者級線程如何切換?六. 程式切換和線程切換七. 為什麼虛拟位址切換很慢
作業系統 | 程式/線程切換問題一. 底層邏輯:二. 虛拟記憶體知識複習三. 程式與線程 四. 程式切換在切換什麼五. 使用者級線程如何切換?六. 程式切換和線程切換七. 為什麼虛拟位址切換很慢

 程序切換:包括指令切換,和記憶體的切換。(分治思想hhh 老師說的好好)

 [線程]作業系統線程是怎麼切換的(使用者态的内部切換)_pmdream的部落格-CSDN部落格_線程切換

六. 程序切換和線程切換

其實程序和線程之間本質的差別在于對記憶體資源的占有

—程序獨享資源,是以切程序需要切頁表(虛拟位址空間的切換)

—線程共享資源,是以不需要切頁表(虛拟位址空間的切換)

線程的切換本質上還是程序的切換,隻要程序切換,必然會進入核心态,隻有核心才有權力進行程序排程,而程序的排程也會涉及到一些資料結構,比如排程程序的紅黑樹,或者是阻塞程序的隊列集,也隻有核心才有資格通路。

程序切換與線程切換的一個最主要差別就在于程序切換涉及到虛拟位址空間的切換而線程切換則不會。因為每個程序都有自己的虛拟位址空間,而線程是共享所在程序的虛拟位址空間的,是以同一個程序中的線程進行線程切換時不涉及虛拟位址空間的轉換。

舉一個不太恰當的例子,線程切換就好比你從主卧走到次卧,反正主卧和次卧都在同一個房子中(虛拟位址空間),是以你無需換鞋子、換衣服等等。但是程序切換就不一樣了,程序切換就好比從你家到别人家,這是兩個不同的房子(不同的虛拟位址空間),出發時要換好衣服、鞋子等等,到别人家後還要再換鞋子等等。

是以我們可以形象的認為線程是處在同一個屋檐下的,這裡的屋檐就是虛拟位址空間,是以線程間切換無需虛拟位址空間的切換;而程序則不同,兩個不同程序位于不同的屋檐下,即程序位于不同的虛拟位址空間,是以程序切換涉及到虛拟位址空間的切換,這也是為什麼程序切換要比線程切換慢的原因。

當每個程序建立的時候,核心會為每個程序配置設定虛拟記憶體,這個時候資料和代碼還在磁盤上,當運作到對應的程式時,程序去尋找頁表,如果發現頁表中位址沒有存放在實體記憶體上,而是在磁盤上,于是發生缺頁異常,于是将磁盤上的資料拷貝到實體記憶體中并更新頁表,下次再通路該虛拟位址時就能命中了。

七. 為什麼虛拟位址切換很慢

現在我們已經知道了程序都有自己的虛拟位址空間,把虛拟位址轉化為實體位址需要查找頁表,頁表查找是一個很慢的過程,是以通常使用Cache來緩存常用的位址映射,這樣可以加速頁表查找,這個Cache就是TLB,Translation Lookaside Buffer,我們不需要關心這個名字,隻需要知道TLB本質上就是一個cache,是用來加速頁表查找的。由于每個程序都有自己的虛拟位址空間,那麼顯然每個程序都有自己的頁表,那麼當程序切換後頁表也要進行切換,頁表切換後TLB就失效了,cache失效導緻命中率降低,那麼虛拟位址轉換為實體位址就會變慢,表現出來的就是程式運作會變慢,而線程切換不會導緻TLB失效,因為線程無需切換位址空間,是以我們通常說線程切換比程序切換快,原因就在這裡。

作業系統面試題:程序切換與線程切換的差別_我是方小磊的部落格-CSDN部落格

程序/線程切換問題 - 簡書

繼續閱讀