自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Shopee 送命題:進(jìn)程切換為什么比線程切換慢

開發(fā) 前端
注意這里問(wèn)的是為什么進(jìn)程切換比線程慢,而不是問(wèn)為什么進(jìn)程比線程慢。當(dāng)然這里的線程肯定指的是同一個(gè)進(jìn)程中的線程。

[[437022]]

 這個(gè)問(wèn)題挺有區(qū)分度的,我也是昨天整理面經(jīng)才看見的這道題。

注意這里問(wèn)的是為什么進(jìn)程切換比線程慢,而不是問(wèn)為什么進(jìn)程比線程慢。當(dāng)然這里的線程肯定指的是同一個(gè)進(jìn)程中的線程。

引子

在進(jìn)入文題之前,我想有必要解釋下虛擬地址(邏輯地址)和物理地址的區(qū)別

下面這段 C 代碼摘錄自《操作系統(tǒng)導(dǎo)論 - [美] 雷姆茲·H.阿帕希杜塞爾》,依次打印出 main 函數(shù)的地址,由 malloc(類似于 Java 中的 new 操作)返回的堆空間分配的值,以及棧上一個(gè)整數(shù)的地址:

得到以下輸出:

我們需要知道的是,所有這些打印出來(lái)的地址都是虛擬的,在物理內(nèi)存中這些地址并不真實(shí)存在,它們最終都將由操作系統(tǒng)和 CPU 硬件翻譯成真正的物理地址,然后才能從真實(shí)的物理位置獲取該地址的值。

OK,上述就當(dāng)作一個(gè)引子,讓各位對(duì)物理地址和虛擬地址有個(gè)直觀的理解,下面正文開始。

物理尋址 Physical Addressing

物理地址的概念很好理解,你可以把它稱為真正的地址?!渡钊肜斫庥?jì)算機(jī)系統(tǒng) - 第 3 版》中給出的物理地址(physical address)的定義如下:

計(jì)算機(jī)系統(tǒng)的主存被組織成一個(gè)由 M 個(gè)連續(xù)的字節(jié)大小的單元組成的數(shù)組。每字節(jié)都有一個(gè)唯一的物理地址。

比如說(shuō),第一個(gè)字節(jié)的物理地址是 0,接下來(lái)的字節(jié)地址是 1,再下一個(gè)是 2,以此類推,給定這種簡(jiǎn)單的結(jié)構(gòu),CPU 訪問(wèn)內(nèi)存的最自然的方式就是使用這樣的物理地址。我們把這種方式稱為物理尋址(physical addressing)。

舉個(gè)例子,比如說(shuō)當(dāng)程序執(zhí)行了一條加載指令,指令內(nèi)容是從物理地址 4 中讀取 4 字節(jié)字傳送到某個(gè)寄存器中。

物理尋址過(guò)程如下:當(dāng) CPU 執(zhí)行到這條指令時(shí),會(huì)生成物理地址 4,然后通過(guò)內(nèi)存主線,把它傳遞給內(nèi)存,內(nèi)存取出從物理地址 4 處開始的 4 字節(jié)字,并將它返回給 CPU,CPU 會(huì)將它存放到指定的寄存器中??聪聢D:

其實(shí)不難發(fā)現(xiàn),物理尋址這種方式,每一個(gè)程序都直接訪問(wèn)物理內(nèi)存,其實(shí)是存在重大缺陷的:

1)首先,用戶程序可以尋址內(nèi)存的任意一個(gè)字節(jié),它們就可以很容易地破壞操作系統(tǒng),從而使系統(tǒng)慢慢地停止運(yùn)行。

2)再次,這種尋址方式使得操作系統(tǒng)中同時(shí)運(yùn)行兩個(gè)或以上的程序幾乎是不可能的。

舉個(gè)例子,我們打開了三個(gè)相同的程序(計(jì)算器),都執(zhí)行到某一步。比方說(shuō),用戶在這三個(gè)計(jì)算器程序的界面上分別輸入了 10、100、1000,其對(duì)應(yīng)的指令就是把用戶輸入的數(shù)字保存在內(nèi)存中的某個(gè)地址中。如果這個(gè)位置只能保存一個(gè)數(shù),那應(yīng)該保存哪個(gè)呢?這不就沖突了嗎?

簡(jiǎn)單來(lái)說(shuō),第一個(gè)計(jì)算器程序給物理內(nèi)存地址賦值 10,第二個(gè)計(jì)算器程序也同樣給這個(gè)地址賦值為 100,那么第二個(gè)程序的賦值會(huì)覆蓋掉第一個(gè)程序所賦的值,這會(huì)造成兩個(gè)程序同時(shí)崩潰。

當(dāng)然了,我們也說(shuō)了是幾乎不可能,不是完全不可能,還是有一些方法可以在物理尋址這種方式下實(shí)現(xiàn)多個(gè)程序并發(fā)運(yùn)行的。

最簡(jiǎn)單的方法就是:首先,將空閑的進(jìn)程存儲(chǔ)在磁盤上,這樣當(dāng)它們不運(yùn)行時(shí)就不會(huì)占用內(nèi)存,然后,讓一個(gè)程序(或者說(shuō)進(jìn)程)單獨(dú)占用全部?jī)?nèi)存運(yùn)行一小段時(shí)間,當(dāng)發(fā)生上下文切換的時(shí)候,就停止這個(gè)進(jìn)程,并將它所有的狀態(tài)信息保存在磁盤上,再加載其他進(jìn)程的狀態(tài)信息,然后運(yùn)行一段時(shí)間...... 只要在某一個(gè)時(shí)間內(nèi)存中只有一個(gè)程序,那么就不會(huì)發(fā)生上述所說(shuō)的地址沖突。這就實(shí)現(xiàn)了一種比較粗糙的并發(fā)。

為什么說(shuō)他是粗糙的呢,因?yàn)檫@種方法有一個(gè)問(wèn)題:將全部的內(nèi)存信息保存到磁盤太慢了!特別是當(dāng)內(nèi)存增長(zhǎng)的時(shí)候。

因此,我們考慮把進(jìn)程對(duì)應(yīng)的內(nèi)存一直留在物理內(nèi)存中,給每個(gè)進(jìn)程分別劃分各自的區(qū)域,在發(fā)生上下文切換的時(shí)候就切換到特定的區(qū)域。

如下圖所示,有 3 個(gè)進(jìn)程(A、B、C),每個(gè)進(jìn)程擁有從 512KB 物理內(nèi)存中切出來(lái)給它們的一小部分內(nèi)存,可以理解為這 3 個(gè)進(jìn)程共享物理內(nèi)存:

顯然,這種方式是存在一定安全隱患的。畢竟如果各個(gè)進(jìn)程之間可以隨意讀取、寫入內(nèi)容的話那就亂套了。

那么如何對(duì)每個(gè)進(jìn)程使用的地址進(jìn)行保護(hù)(protection)呢?繼續(xù)使用物理內(nèi)存模型肯定是不行了,因此操作系統(tǒng)創(chuàng)造了一個(gè)新的內(nèi)存抽象,引入了一個(gè)新的內(nèi)存模型,那就是虛擬地址空間,很多書中都會(huì)直接稱呼為 “地址空間(Address Space)”。

虛擬尋址 Virtual Addressing

上面提到,對(duì)于物理內(nèi)存模型來(lái)說(shuō),如果各個(gè)進(jìn)程之間可以隨意讀取、寫入內(nèi)容的話那就亂套了。

所以,每個(gè)進(jìn)程的棧啊、堆啊、代碼段啊等等它們的實(shí)際物理內(nèi)存地址對(duì)于這個(gè)進(jìn)程來(lái)說(shuō)應(yīng)該是不可見的,這樣誰(shuí)也不能直接訪問(wèn)這個(gè)物理地址。

那問(wèn)題就來(lái)了,物理地址被隱藏起來(lái)了,我們?cè)撛趺慈ピL問(wèn)這個(gè)進(jìn)程呢?

操作系統(tǒng)會(huì)給每個(gè)進(jìn)程分配一個(gè)虛擬地址空間(vitural address),每個(gè)進(jìn)程包含的棧、堆、代碼段這些都會(huì)從這個(gè)地址空間中被分配一個(gè)地址,這個(gè)地址就被稱為虛擬地址。底層指令寫入的地址也是虛擬地址。

每個(gè)進(jìn)程都擁有一個(gè)自己的虛擬地址空間,并且獨(dú)立于其他進(jìn)程的地址空間。(注意這句話非常重要!!!兄弟姐妹們背起來(lái))

也就是說(shuō)一個(gè)進(jìn)程中的虛擬地址 28 所對(duì)應(yīng)的物理地址與另一個(gè)進(jìn)程中的虛擬地址 28 所對(duì)應(yīng)的物理地址是不同的,這樣就不會(huì)發(fā)生沖突了。

可以這么理解,物理地址就是一個(gè)倉(cāng)庫(kù),虛擬地址就是一個(gè)門牌,比方說(shuō)一共有三十個(gè)門牌,那么所有的進(jìn)程都能看見這三十個(gè)門牌,但是他們看見的某個(gè)相同門牌,指向的并不是同一個(gè)倉(cāng)庫(kù)。

有了虛擬地址空間后,CPU 就可以通過(guò)生成一個(gè)虛擬地址來(lái)訪問(wèn)主存,這個(gè)虛擬地址在被送到內(nèi)存之前會(huì)先被轉(zhuǎn)換成合適的物理地址,這個(gè)虛擬地址到物理地址的轉(zhuǎn)換過(guò)程稱為 地址翻譯/地址轉(zhuǎn)換(address translation)。

地址翻譯需要 CPU 硬件和操作系統(tǒng)的密切合作:CPU 上的內(nèi)存管理單元(Memory Management Unit,MMU)就是專門用來(lái)進(jìn)行虛擬地址到物理地址的轉(zhuǎn)換的,不過(guò) MMU 需要借助存放在內(nèi)存中的頁(yè)表,而這張表的內(nèi)容正是由操作系統(tǒng)進(jìn)行管理的。

頁(yè)表是一個(gè)十分重要的數(shù)據(jù)結(jié)構(gòu)!

操作系統(tǒng)為每個(gè)進(jìn)程建立了一張頁(yè)表。一個(gè)進(jìn)程對(duì)應(yīng)一張頁(yè)表,進(jìn)程的每個(gè)頁(yè)面對(duì)應(yīng)一個(gè)頁(yè)表項(xiàng),每個(gè)頁(yè)表項(xiàng)由頁(yè)號(hào)和塊號(hào)(頁(yè)框號(hào))組成,記錄著進(jìn)程頁(yè)面和實(shí)際存放的內(nèi)存塊之間的映射關(guān)系。

從數(shù)學(xué)角度來(lái)說(shuō),頁(yè)表是一個(gè)函數(shù),它的參數(shù)是虛擬頁(yè)號(hào),結(jié)果是物理頁(yè)框號(hào)。

至此,上述這一套 CPU 生成虛擬地址并進(jìn)行地址翻譯的流程就是虛擬尋址(virtual addressing):

進(jìn)程切換為什么比線程切換慢?

呼,講了一大堆,其實(shí)最重要的就是這句話:

每個(gè)進(jìn)程都擁有一個(gè)自己的虛擬地址空間,并且獨(dú)立于其他進(jìn)程的地址空間

So,Tell me,進(jìn)程切換會(huì)涉及什么的切換?

是的,進(jìn)程切換會(huì)涉及到虛擬地址空間的切換,而這正是導(dǎo)致進(jìn)程切換比線程切換慢的原因所在!

很多小伙伴可能都云里霧里,啊,是這樣嗎,怎么回事

想一下,上面是不是說(shuō)過(guò),虛擬地址轉(zhuǎn)換為物理地址需要兩個(gè)東西:CPU 上的 MMU 和內(nèi)存中的頁(yè)表

每次訪問(wèn)內(nèi)存,都需要進(jìn)行虛擬地址到物理地址的轉(zhuǎn)換,對(duì)吧,因此,每條指令進(jìn)行一兩次或更多地去訪問(wèn)頁(yè)表是必要的,而頁(yè)表又是存在于內(nèi)存中的。

顯然,訪問(wèn)頁(yè)表(內(nèi)存)次數(shù)太多導(dǎo)致其成為了操作系統(tǒng)地一個(gè)性能瓶頸,我們得想個(gè)法子解決它

于是,轉(zhuǎn)換檢測(cè)緩沖區(qū)(Translation Lookaside Buffer,TLB)應(yīng)運(yùn)而生,也稱為快表

為啥說(shuō)他快呢?因?yàn)?TLB 通常內(nèi)置在 CPU 的 MMU 中,這訪問(wèn)速度跟內(nèi)存不是一個(gè)檔次的。內(nèi)存中的頁(yè)表一般被稱為慢表。

事實(shí)上,TLB 的出現(xiàn)是基于這樣一種現(xiàn)象的:大多數(shù)程序總是對(duì)少量的頁(yè)面進(jìn)行多次的訪問(wèn)。因此,只有很少的頁(yè)表項(xiàng)會(huì)被反復(fù)讀取,而其他的頁(yè)表項(xiàng)很少被訪問(wèn)。

TLB 中存放的就是那些會(huì)被反復(fù)讀取的頁(yè)表項(xiàng)。換句話說(shuō),TLB 中存放的就是頁(yè)表中的一部分副本。

若 TLB 命中,就不需要再訪問(wèn)內(nèi)存了;若 TLB 中沒有目標(biāo)頁(yè)表項(xiàng),則還需要去查詢內(nèi)存中的頁(yè)表(慢表),從頁(yè)表中得到物理頁(yè)框地址,同時(shí)將頁(yè)表中的該表項(xiàng)添加到 TLB 中。

簡(jiǎn)單理解,TLB 就相當(dāng)于一個(gè)緩存

現(xiàn)在再回到問(wèn)題,不知道各位小伙伴有沒有一點(diǎn)思路了。

由于進(jìn)程切換會(huì)涉及到虛擬地址空間的切換,這就導(dǎo)致內(nèi)存中的頁(yè)表也需要進(jìn)行切換,一個(gè)進(jìn)程對(duì)應(yīng)一個(gè)頁(yè)表是不假,但是 CPU 中的 TLB 只有一個(gè)啊,這就尷尬了,頁(yè)表切換后這個(gè) TLB 就失效了。這樣,TLB 在一段時(shí)間內(nèi)肯定是無(wú)法被命中的,操作系統(tǒng)就必須去訪問(wèn)內(nèi)存,那么虛擬地址轉(zhuǎn)換為物理地址就會(huì)變慢,表現(xiàn)出來(lái)的就是程序運(yùn)行會(huì)變慢。

而線程切換呢,由于不涉及虛擬地址空間的切換,也就不存在這個(gè)問(wèn)題了。

最后放上這道題的背誦版:

面試官:進(jìn)程切換為什么比線程切換要慢呢?

小牛肉:額,關(guān)于這個(gè)問(wèn)題,需要從虛擬地址和物理地址說(shuō)起

物理地址就是真實(shí)的地址嘛,這種尋址方式很容易破壞操作系統(tǒng),而且使得操作系統(tǒng)中同時(shí)運(yùn)行兩個(gè)或以上的程序幾乎是不可能的(此處可以舉個(gè)例子,第一個(gè)程序給物理內(nèi)存地址賦值 10,第二個(gè)程序也同樣給這個(gè)地址賦值為 100,那么第二個(gè)程序的賦值會(huì)覆蓋掉第一個(gè)程序所賦的值,這會(huì)造成兩個(gè)程序同時(shí)崩潰)。

當(dāng)然,也不是完全不可能,有一種方式可以實(shí)現(xiàn)比較粗糙的并發(fā)

就是說(shuō),我們將空閑的進(jìn)程存儲(chǔ)在磁盤上,這樣當(dāng)它們不運(yùn)行時(shí)就不會(huì)占用內(nèi)存,當(dāng)進(jìn)程需要運(yùn)行的時(shí)候再?gòu)拇疟P上轉(zhuǎn)到內(nèi)存上來(lái),不過(guò)很顯然這種方式比較浪費(fèi)時(shí)間。

于是,我們考慮,把所有進(jìn)程對(duì)應(yīng)的內(nèi)存一直留在物理內(nèi)存中,給每個(gè)進(jìn)程分別劃分各自的區(qū)域,這樣,發(fā)生上下文切換的時(shí)候就切換到特定的區(qū)域

那問(wèn)題還是很明顯的,就是仍然沒法避免破壞操作系統(tǒng),因?yàn)楦鱾€(gè)進(jìn)程之間可以隨意讀取、寫入內(nèi)容。

所以,我們需要一種機(jī)制對(duì)每個(gè)進(jìn)程使用的地址進(jìn)行保護(hù),因此操作系統(tǒng)創(chuàng)造了一個(gè)新的內(nèi)存模型,那就是虛擬地址空間

就是說(shuō),每個(gè)進(jìn)程都擁有一個(gè)自己的虛擬地址空間,并且獨(dú)立于其他進(jìn)程的地址空間,然后每個(gè)進(jìn)程包含的棧、堆、代碼段這些都會(huì)從這個(gè)地址空間中被分配一個(gè)地址,這個(gè)地址就被稱為虛擬地址。底層指令寫入的地址也是虛擬地址。

有了虛擬地址空間后,CPU 就可以通過(guò)虛擬地址轉(zhuǎn)換成物理地址這樣一個(gè)過(guò)程,來(lái)間接訪問(wèn)物理內(nèi)存了。

地址轉(zhuǎn)換需要兩個(gè)東西,一個(gè)是 CPU 上的內(nèi)存管理單元 MMU,另一個(gè)是內(nèi)存中的頁(yè)表,頁(yè)表中存的虛擬地址到物理地址的映射

但是呢,每次訪問(wèn)內(nèi)存,都需要進(jìn)行虛擬地址到物理地址的轉(zhuǎn)換,對(duì)吧,這樣的話,頁(yè)表就會(huì)被頻繁地訪問(wèn),而頁(yè)表又是存在于內(nèi)存中的。所以說(shuō),訪問(wèn)頁(yè)表(內(nèi)存)次數(shù)太多導(dǎo)致其成為了操作系統(tǒng)地一個(gè)性能瓶頸。

于是,引入了轉(zhuǎn)換檢測(cè)緩沖區(qū) TLB,也就是快表,其實(shí)就是一個(gè)緩存,把經(jīng)常訪問(wèn)到的內(nèi)存地址映射存在 TLB 中,因?yàn)?TLB 是在 CPU 的 MMU 中的嘛,所以訪問(wèn)起來(lái)非常快。

然后,正是因?yàn)?TLB 這個(gè)東西,導(dǎo)致了進(jìn)程切換比線程切換慢。

由于進(jìn)程切換會(huì)涉及到虛擬地址空間的切換,這就導(dǎo)致內(nèi)存中的頁(yè)表也需要進(jìn)行切換,一個(gè)進(jìn)程對(duì)應(yīng)一個(gè)頁(yè)表是不假,但是 CPU 中的 TLB 只有一個(gè),頁(yè)表切換后這個(gè) TLB 就失效了。這樣,TLB 在一段時(shí)間內(nèi)肯定是無(wú)法被命中的,操作系統(tǒng)就必須去訪問(wèn)內(nèi)存,那么虛擬地址轉(zhuǎn)換為物理地址就會(huì)變慢,表現(xiàn)出來(lái)的就是程序運(yùn)行會(huì)變慢。

而線程切換呢,由于不涉及虛擬地址空間的切換,所以也就不存在這個(gè)問(wèn)題了。

 

責(zé)任編輯:武曉燕 來(lái)源: 飛天小牛肉
相關(guān)推薦

2024-12-13 08:24:23

2020-03-18 14:08:48

Windows操作系統(tǒng)功能

2018-10-31 10:37:29

云計(jì)算遷移云平臺(tái)

2020-08-26 09:56:30

WindowsLinuxUbuntu

2020-12-21 14:28:01

語(yǔ)言JavaC ++

2023-03-03 00:03:07

Linux進(jìn)程管理

2022-04-28 08:12:29

函數(shù)調(diào)用進(jìn)程切換代碼

2023-07-30 23:44:49

Go協(xié)程進(jìn)程

2025-03-05 10:34:56

2018-08-16 08:03:21

Python語(yǔ)言解釋器

2010-05-24 09:41:31

2017-10-25 09:50:51

Linux

2018-04-02 09:21:24

主備機(jī)房切換上云

2024-04-15 04:00:00

C#反射代碼

2015-07-08 14:47:56

JSPBeetl

2021-01-13 10:51:08

PromissetTimeout(函數(shù)

2022-11-10 15:32:29

2021-07-28 07:53:20

CPU 線程切換

2023-09-01 13:49:00

內(nèi)存進(jìn)程線程

2015-11-24 10:46:06

LinuxNetworkManasystemd-net
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)