07. 就該這么學(xué)并發(fā) - 上下文切換

前言

前面的幾章, 我們有提到個名詞“上下文切換

這是個什么東西呢?

其實很好理解, 我們看一本書籍時,往往間隔的看(很少有人一天看完的吧?!), 那么如何保證自己下次是從上一次斷點處看呢? 顯而易見, 我們用書簽!

“上下文切換”就是計算機的“書簽”.

本章,我們來學(xué)習(xí)“上下文切換”原理以及如何減少“上下文切換”.

在此之前, 還得先復(fù)習(xí)下前面提到的進程和線程, 因為上下文切換的對象就是進程和線程.

線程與進程

進程是操作系統(tǒng)的管理單位,也是系統(tǒng)分配資源的基本單位;

線程則是進程的管理單位, 也是是CPU調(diào)度的基本單位;

線程依托于進程, 一個進程至少包含一個線程;

線程在Linux系統(tǒng)中就是能并行運行并且與他們的父進程(創(chuàng)建他們的進程)共享同一地址空間(一段內(nèi)存區(qū)域)和其他資源的輕量級進程

一個線程指的是進程中一個單一順序的控制流

不管是在單線程還是多線程中, 每個線程都有

  • 一個程序計數(shù)器

記錄要執(zhí)行的下一條指令;

程序計數(shù)器是一個專用的寄存器,用于表明指令序列中 CPU 正在執(zhí)行的位置,存的值為正在執(zhí)行的指令的位置或者下一個將要被執(zhí)行的指令的位置,具體依賴于特定的系統(tǒng).

  • 一組寄存器

保存當(dāng)前線程的工作變量

寄存器是CPU 內(nèi)部數(shù)量較少但是速度很快的內(nèi)存(與之對應(yīng)的是 CPU 外部相對較慢的 RAM 主內(nèi)存).寄存器通過對常用值(通常是運算的中間值)的快速訪問來提高計算機程序運行的速度.

  • 堆棧

記錄執(zhí)行歷史,其中每一幀保存了一個已經(jīng)調(diào)用但未返回的過程

進程&線程表項

上下文切換

上下文切換針對的是多任務(wù)處理的系統(tǒng),

多任務(wù)處理系統(tǒng)指的是同時運行兩個或多個程序的系統(tǒng).

在多任務(wù)處理系統(tǒng)中, CPU需要處理所有程序的操作, 當(dāng)用戶來回切換它們時, 需要記錄這些程序執(zhí)行到哪里.

上下文切換就是這樣一個過程:

允許CPU記錄并恢復(fù)各種正在運行程序的狀態(tài),使它能夠完成切換操作

多任務(wù)系統(tǒng)往往需要同時執(zhí)行多道作業(yè), 作業(yè)數(shù)往往大于機器的CPU數(shù).

然而, 一顆CPU同時只能執(zhí)行一項任務(wù), 如何讓用戶感覺這些任務(wù)正在同時進行呢?

操作系統(tǒng)的設(shè)計者 巧妙地利用了時間片輪轉(zhuǎn)的方式:

CPU給每個任務(wù)都運行一定的時間, 然后把當(dāng)前任務(wù)的狀態(tài)保存下來,
再加載下一任務(wù)的狀態(tài)后, 繼續(xù)服務(wù)下一任務(wù).

任務(wù)的狀態(tài)保存及再加載,這段過程就叫做上下文切換.

時間片輪轉(zhuǎn)的方式使多個任務(wù)在同一顆CPU上執(zhí)行變成了可能.

任務(wù)的狀態(tài)保存及再加載, 這段過程就叫做上下文切換

上下文切換(有時也稱做進程切換或任務(wù)切換)是指:

CPU從一個進程或線程切換到另一個進程或線程.

上下文切換可以認為是內(nèi)核(操作系統(tǒng)的核心)在 CPU 上對于進程(或線程)進行以下的活動:

  • 掛起一個進程

將這個進程在 CPU 中的狀態(tài)(上下文)存儲于內(nèi)存中的某處

  • 恢復(fù)一個進程

在內(nèi)存中檢索下一個進程的上下文并將其在 CPU 的寄存器中恢復(fù)

  • 跳轉(zhuǎn)到程序計數(shù)器所指向的位置

即跳轉(zhuǎn)到進程被中斷時的代碼行, 以恢復(fù)該進程

切換種類

上下文切換在不同的場合有不同的含義

上下文切換種類 描述
線程切換 同一進程中的兩個線程之間的切換
進程切換 兩個進程之間的切換
模式切換 在給定線程中,用戶模式和內(nèi)核模式的切換
地址空間切換 將虛擬內(nèi)存切換到物理內(nèi)存

切換步驟

在上下文切換過程中, CPU會停止處理當(dāng)前運行的程序, 并保存當(dāng)前程序運行的具體位置以便之后繼續(xù)運行.

從這個角度來看, 上下文切換有點像我們同時閱讀幾本書,來回切換書本的同時, 我們需要記住每本書當(dāng)前讀到的頁碼.

在程序中, 上下文切換過程中的“頁碼”信息是保存在進程控制塊(PCB, process control block)中的, PCB還經(jīng)常被稱作“切換楨”(switchframe).

“頁碼”信息會一直保存到CPU的內(nèi)存中,直到他們被再次使用.

PCB通常是系統(tǒng)內(nèi)存占用區(qū)中的一個連續(xù)存區(qū),
它存放著操作系統(tǒng)用于描述進程情況及控制進程運行所需的全部信息.

它的作用主要是“使一個在多道程序環(huán)境下不能獨立進行的程序(含數(shù)據(jù)), 成為一個能獨立運行的基本單位, 一個能與其他進程并發(fā)執(zhí)行的進程.

或者說, 操作系統(tǒng)是根據(jù)PCB來對并發(fā)執(zhí)行的進程進行控制和管理.

舉個例子, 兩個進程A和B, 系統(tǒng)需要從A切換到B:

  1. 保存進程A的狀態(tài)(寄存器和操作系統(tǒng)數(shù)據(jù))燕锥;
  2. 更新PCB中的信息,對進程A的“運行態(tài)”做出相應(yīng)更改溺健;
  3. 將進程A的PCB放入相關(guān)狀態(tài)的隊列;
  4. 將進程B的PCB信息改為“運行態(tài)”,并執(zhí)行進程B蜘醋;
  5. B執(zhí)行完后,從隊列中取出進程A的PCB,恢復(fù)進程A被切換時的上下文,繼續(xù)執(zhí)行A邮府;

進程/線程上下文切換差異

線程切換和進程切換的步驟是有差異的

主要體現(xiàn)在性能和地址空間上.

  • 性能上

進程的上下文切換需要兩步

  1. 切換頁目錄以使用新的地址空間;
  2. 切換內(nèi)核棧和硬件上下文溉奕;

線程的上下文切換只需一步

  1. 切換內(nèi)核棧和硬件上下文褂傀;

進程上下文切換比線程上下文切換多了步驟1, 所以明顯是進程切換代價大.

  • 地址空間

對于Linux來說, 線程和進程的最大區(qū)別就在于地址空間.
線程的切換虛擬內(nèi)存空間依然是相同的,
而進程切換是不同的.

這兩種上下文切換的處理都是通過操作系統(tǒng)內(nèi)核來完成的.

內(nèi)核的這種切換過程最顯著的性能損耗是將寄存器中的內(nèi)容切換出.

一個正在執(zhí)行的進程包括程序計數(shù)器、寄存器加勤、變量的當(dāng)前值等,

這些數(shù)據(jù)都是保存在CPU的寄存器中的,并且這些寄存器只能是正在使用CPU的進程才能使用.

在進程切換時,

首先得保存上一個進程的這些數(shù)據(jù)

主要為了下次獲得CPU的使用權(quán)時,從上次的中斷處開始繼續(xù)順序執(zhí)行,
而不是返回到進程開始,否則每次進程重新獲得CPU時所處理的任務(wù)都是上一次的重復(fù),永遠也到不了進程的結(jié)束,
因為一個進程幾乎不可能執(zhí)行完所有任務(wù)后才釋放CPU

然后將本次獲得CPU的進程的這些數(shù)據(jù)裝入CPU的寄存器, 從上次斷點處繼續(xù)執(zhí)行剩下的任務(wù).

操作系統(tǒng)為了便于管理系統(tǒng)內(nèi)部進程,為每個進程創(chuàng)建了一張進程表項:

進程表項

切換查看

在Linux系統(tǒng)下可以使用vmstat命令來查看上下文切換的次數(shù)

image.png

vmstat 1指每秒統(tǒng)計一次, 其中cs列就是指上下文切換的數(shù)目.

切換原因

引起線程上下文切換的原因,主要存在三種情況如下:

  • 中斷處理

在中斷處理中, 其他程序”打斷”了當(dāng)前正在運行的程序.
當(dāng)CPU接收到中斷請求時, 會在正在運行的程序和發(fā)起中斷請求的程序之間進行一次上下文切換.
中斷分為硬件中斷和軟件中斷,
軟件中斷包括因為IO阻塞仙辟、未搶到資源或者用戶代碼等原因,線程被掛起.

  • 多任務(wù)處理

在多任務(wù)處理中,CPU會在不同程序之間來回切換,每個程序都有相應(yīng)的處理時間片,CPU在兩個時間片的間隔中進行上下文切換.

  • 用戶態(tài)切換

對于一些操作系統(tǒng),當(dāng)進行用戶態(tài)切換時也會進行一次上下文切換,雖然這不是必須的.

對于我們經(jīng)常使用的搶占式操作系統(tǒng)而言,引起線程上下文切換的原因大概有以下幾種:

  • 當(dāng)前執(zhí)行任務(wù)的時間片用完之后,系統(tǒng)CPU正常調(diào)度下一個任務(wù)

  • 當(dāng)前執(zhí)行任務(wù)碰到IO阻塞,調(diào)度器將此任務(wù)掛起,繼續(xù)下一任務(wù)

  • 多個任務(wù)搶占鎖資源,當(dāng)前任務(wù)沒有搶到鎖資源,被調(diào)度器掛起,繼續(xù)下一任務(wù)

  • 用戶代碼掛起當(dāng)前任務(wù),讓出CPU時間

  • 硬件中斷

切換損耗

上下文切換會帶來直接和間接兩種因素影響程序性能的消耗.

  • 直接消耗
  • CPU寄存器需要保存和加載
  • 系統(tǒng)調(diào)度器的代碼需要執(zhí)行
  • TLB實例需要重新加載
  • CPU 的pipeline需要刷掉
  • 間接消耗
  • 多核的cache之間得共享數(shù)據(jù)
    間接消耗對于程序的影響要看線程工作區(qū)操作數(shù)據(jù)的大小鳄梅;

如何減少切換

上下文切換會導(dǎo)致額外的開銷, 因此減少上下文切換次數(shù)便可以提高多線程程序的運行效率.

但上下文切換又分為2種:

  • 讓步式上下文切換

即執(zhí)行線程主動釋放CPU,與鎖競爭嚴重程度成正比;
可通過減少鎖競爭來避免叠国;

  • 搶占式上下文切換

指線程因分配的時間片用盡, 而被迫放棄CPU或者被其他優(yōu)先級更高的線程所搶占;
一般由于線程數(shù)大于CPU可用核心數(shù)引起;
可通過調(diào)整線程數(shù),適當(dāng)減少線程數(shù)來避免.

減少上下文切換的方法如下

  • 無鎖并發(fā)

多線程競爭時,會引起上下文切換;
所以多線程處理數(shù)據(jù)時,可以用一些辦法來避免使用鎖;
如將數(shù)據(jù)的ID按照Hash取模分段,不同的線程處理不同段的數(shù)據(jù);

  • CAS算法

Java的Atomic包使用CAS算法來更新數(shù)據(jù),而不需要加鎖戴尸;

  • 最少線程

避免創(chuàng)建不需要的線程;
比如任務(wù)很少,但是創(chuàng)建了很多線程來處理,這樣會造成大量線程都處于等待狀態(tài)粟焊;

  • 使用協(xié)程

在單線程里實現(xiàn)多任務(wù)的調(diào)度,并在單線程里維持多個任務(wù)間的切換;

合理控制線程數(shù)目

合理設(shè)置線程數(shù)目,關(guān)鍵點是

  • 盡量減少線程切換和管理的開支

要求線程數(shù)盡量少,這樣可以減少線程切換和管理的開支孙蒙;

  • 最大化利用CPU

要求盡量多的線程,以保證CPU資源最大化的利用项棠;

針對不同的實際情況,我們需要采取合理的措施:

  • 對于任務(wù)耗時短的情況:

要求線程盡量少,如果線程太多,有可能出現(xiàn)線程切換和管理的時間,大于任務(wù)執(zhí)行的時間, 那效率就低了;

  • 對于耗時長的任務(wù):

要分是CPU任務(wù),還是IO等類型的任務(wù).
如果是CPU類型的任務(wù),線程數(shù)不宜太多挎峦;
如果是IO類型的任務(wù),線程多一些更好,可以更充分利用CPU.

  • 對于高并發(fā),低耗時的情況:

建議少線程, 只要滿足并發(fā)即可;
因為上下文切換本來就多,并且高并發(fā)就意味著CPU是處于繁忙狀態(tài)的,
增加更多地線程也不會讓線程得到執(zhí)行時間片,反而會增加線程切換的開銷香追;
例如并發(fā)100,線程池可能設(shè)置為10就可以.

  • 對于低并發(fā),高耗時的情況:

建議多線程, 保證有空閑線程, 接受新的任務(wù).
例如并發(fā)10,線程池可能就要設(shè)置為20;

  • 對于高并發(fā)高耗時:
  • 要分析任務(wù)類型坦胶;
  • 增加排隊透典;
  • 加大線程數(shù);

請關(guān)注我的訂閱號

訂閱號.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末顿苇,一起剝皮案震驚了整個濱河市峭咒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌岖圈,老刑警劉巖讹语,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蜂科,居然都是意外死亡顽决,警方通過查閱死者的電腦和手機短条,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來才菠,“玉大人茸时,你說我怎么就攤上這事「撤茫” “怎么了可都?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蚓耽。 經(jīng)常有香客問我渠牲,道長,這世上最難降的妖魔是什么步悠? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任签杈,我火速辦了婚禮,結(jié)果婚禮上鼎兽,老公的妹妹穿的比我還像新娘答姥。我一直安慰自己,他們只是感情好谚咬,可當(dāng)我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布鹦付。 她就那樣靜靜地躺著,像睡著了一般择卦。 火紅的嫁衣襯著肌膚如雪敲长。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天互捌,我揣著相機與錄音潘明,去河邊找鬼。 笑死秕噪,一個胖子當(dāng)著我的面吹牛钳降,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播腌巾,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼遂填,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了澈蝙?” 一聲冷哼從身側(cè)響起吓坚,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎灯荧,沒想到半個月后礁击,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年哆窿,在試婚紗的時候發(fā)現(xiàn)自己被綠了链烈。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡挚躯,死狀恐怖强衡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情码荔,我是刑警寧澤漩勤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站缩搅,受9級特大地震影響越败,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜硼瓣,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一眉尸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧巨双,春花似錦、人聲如沸霉祸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽丝蹭。三九已至慢宗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奔穿,已是汗流浹背镜沽。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贱田,地道東北人缅茉。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像男摧,于是被迫代替她去往敵國和親蔬墩。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,486評論 2 348