計算機組成原理筆記(四)


我的博客:https://www.luozhiyun.com/

內存

內存是五大組成部分里面的存儲器,我們的指令和數據,都需要先加載到內存里面,才會被CPU拿去執行。

我們的內存需要被分成固定大小的頁(Page),然后再通過虛擬內存地址(Virtual Address)到物理內存地址(Physical Address)的地址轉換(Address Translation),才能到達實際存放數據的物理內存位置。而我們的程序看到的內存地址,都是虛擬內存地址。

頁表

想要把虛擬內存地址,映射到物理內存地址,最直觀的辦法,就是來建一張映射表。虛擬內存里面的頁,到物理內存里面的頁的一一映射。這個映射表,在計算機里面,就叫作頁表(Page Table)。

頁表這個地址轉換的辦法,會把一個內存地址分成頁號(Directory)和偏移量(Offset)兩個部分。

對於一個內存地址轉換,其實就是這樣三個步驟:

  1. 把虛擬內存地址,切分成頁號和偏移量的組合;
  2. 從頁表里面,查詢出虛擬頁號,對應的物理頁號;
  3. 直接拿物理頁號,加上前面的偏移量,就得到了物理內存地址;

多級頁表(Multi-Level Page Table)

大部分進程所占用的內存是有限的,需要的頁也自然是很有限的。我們只需要去存那些用到的頁之間的映射關系就好了。

在整個進程的內存地址空間,通常是“兩頭實、中間空”。在程序運行的時候,內存地址從頂部往下,不斷分配占用的棧的空間。而堆的空間,內存地址則是從底部往上,是不斷分配占用的。

所以,在一個實際的程序進程里面,虛擬內存占用的地址空間,通常是兩段連續的空間。

我們以一個4級的多級頁表為例,來看一下。

對應的,一個進程會有一個4級頁表。我們先通過4級頁表索引,找到4級頁表里面對應的條目(Entry)。這個條目里存放的是一張3級頁表所在的位置。4級頁面里面的每一個條目,都對應着一張3級頁表,所以我們可能有多張3級頁表。

找到對應這張3級頁表之后,我們用3級索引去找到對應的3級索引的條目。3級索引的條目再會指向一個2級頁表。同樣的,2級頁表里我們可以用2級索引指向一個1級頁表。

而最后一層的1級頁表里面的條目,對應的數據內容就是物理頁號了。在拿到了物理頁號之后,我們同樣可以用“頁號+偏移量”的方式,來獲取最終的物理內存地址。

TLB加速地址轉換

程序里面的每一個進程,都有一個屬於自己的虛擬內存地址空間。我們可以通過地址轉換來獲得最終的實際物理地址。我們每一個指令都存放在內存里面,每一條數據都存放在內存里面。

“地址轉換”是一個非常高頻的動作,“地址轉換”的性能就變得至關重要了。

多級頁表讓原本只需要訪問一次內存的操作,變成了需要訪問4次內存,才能找到物理頁號。

程序所需要使用的指令,都順序存放在虛擬內存里面。我們執行的指令,也是一條條順序執行下去的。

於是,計算機工程師們專門在CPU里放了一塊緩存芯片。這塊緩存芯片我們稱之為TLB,全稱是地址變換高速緩沖(Translation-Lookaside Buffer)。這塊緩存存放了之前已經進行過地址轉換的查詢結果。這樣,當同樣的虛擬地址需要進行地址轉換的時候,我們可以直接在TLB里面查詢結果,而不需要多次訪問內存來完成一次轉換。

TLB和我們前面講的CPU的高速緩存類似,可以分成指令的TLB和數據的TLB,也就是ITLB和DTLB。

為了性能,我們整個內存轉換過程也要由硬件來執行。在CPU芯片里面,我們封裝了內存管理單元(MMU,Memory Management Unit)芯片,用來完成地址轉換。和TLB的訪問和交互,都是由這個MMU控制的。

I/O

我們先來看一個固態硬盤的Benchmark圖:

“4K”的指標就是我們的程序,去隨機讀取磁盤上某一個4KB大小的數據,一秒之內可以讀取到多少數據。

我們拿這個40MB/s和一次讀取4KB的數據算一下。 40MB / 4KB = 10,000 也就是說,一秒之內,這塊SSD硬盤可以隨機讀取1萬次的4KB的數據。如果是寫入的話呢,會更多一些,90MB /4KB 差不多是2萬多次。

這個每秒讀寫的次數,我們稱之為IOPS,也就是每秒輸入輸出操作的次數。
DTR(Data Transfer Rate,數據傳輸率)

我們在實際的應用開發當中,對於數據的訪問,更多的是隨機讀寫,而不是順序讀寫。

診斷 I/O瓶頸

首先看一下CPU有沒有在等待io操作。

# top

top - 06:26:30 up 4 days, 53 min,  1 user,  load average: 0.79, 0.69, 0.65
Tasks: 204 total,   1 running, 203 sleeping,   0 stopped,   0 zombie
%Cpu(s): 20.0 us,  1.7 sy,  0.0 ni, 77.7 id,  0.0 wa,  0.0 hi,  0.7 si,  0.0 st
KiB Mem:   7679792 total,  6646248 used,  1033544 free,   251688 buffers
KiB Swap:        0 total,        0 used,        0 free.  4115536 cached Mem

wa的指標,這個指標就代表着iowait,也就是CPU等待IO完成操作花費的時間占CPU的百分比。

如果iowait很大,那么就可以去看看實際的I/O操作情況是什么樣的。使用iostat,就能夠看到實際的硬盤讀寫情況。

$ iostat

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          17.02    0.01    2.18    0.04    0.00   80.76
Device:            tps    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn
sda               1.81         2.02        30.87     706768   10777408

tps指標,其實就對應着我們上面所說的硬盤的IOPS性能。而kB_read/s和kB_wrtn/s指標,就對應着我們的數據傳輸率的指標。

使用iotop找出到底是哪一個進程是這些I/O讀寫的來源。

$ iotop

Total DISK READ :       0.00 B/s | Total DISK WRITE :      15.75 K/s
Actual DISK READ:       0.00 B/s | Actual DISK WRITE:      35.44 K/s
  TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO>    COMMAND                                             
  104 be/3 root        0.00 B/s    7.88 K/s  0.00 %  0.18 % [jbd2/sda1-8]
  383 be/4 root        0.00 B/s    3.94 K/s  0.00 %  0.00 % rsyslogd -n [rs:main Q:Reg]
 1514 be/4 www-data    0.00 B/s    3.94 K/s  0.00 %  0.00 % nginx: worker process

硬盤

機械硬盤

一塊機械硬盤是由盤面、磁頭和懸臂三個部件組成的。

首先,自然是盤面(Disk Platter)。盤面其實就是我們實際存儲數據的盤片。
我們的硬盤有5400轉的、7200轉的,乃至10000轉的。這個多少多少轉,指的就是盤面中間電機控制的轉軸的旋轉速度,英文單位叫RPM,也就是每分鍾的旋轉圈數(Rotations Per Minute)。

磁頭:數據並不能直接從盤面傳輸到總線上,而是通過磁頭,從盤面上讀取到,然后再通過電路信號傳輸給控制電路、接口,再到總線上的。通常,我們的一個盤面上會有兩個磁頭,分別在盤面的正反面。

懸臂鏈接在磁頭上,並且在一定范圍內會去把磁頭定位到盤面的某個特定的磁道(Track)上。

一個盤面通常是圓形的,由很多個同心圓組成,每一個同心圓都是一個磁道。每個磁道都有自己的一個編號。

一個磁道,會分成一個一個扇區(Sector)。上下平行的一個一個盤面的相同扇區呢,我們叫作一個柱面(Cylinder)。

讀取數據,其實就是兩個步驟。

  1. 把盤面旋轉到某一個位置。在這個位置上,我們的懸臂可以定位到整個盤面的某一個子區間。
  2. 把我們的懸臂移動到特定磁道的特定扇區,也就在這個“幾何扇區”里面,找到我們實際的扇區。找到之后,我們的磁頭會落下,就可以讀取到正對着扇區的數據。

進行一次硬盤上的隨機訪問,需要的時間由兩個部分組成。

第一個部分,叫作平均延時(Average Latency)。這個時間,其實就是把我們的盤面旋轉,把幾何扇區對准懸臂位置的時間。這個時間很容易計算,它其實就和我們機械硬盤的轉速相關。

隨機情況下,平均找到一個幾何扇區,我們需要旋轉半圈盤面。上面7200轉的硬盤,那么一秒里面,就可以旋轉240個半圈。那么,這個平均延時就是:1s / 240 = 4.17ms

第二個部分,叫作平均尋道時間(Average Seek Time),也就是在盤面選轉之后,我們的懸臂定位到扇區的的時間。我們現在用的HDD硬盤的平均尋道時間一般在4-10ms。

SSD硬盤

現在新的大容量SSD硬盤是由很多個裸片(Die)疊在一起的,就好像我們的機械硬盤把很多個盤面(Platter)疊放再一起一樣,這樣可以在同樣的空間下放下更多的容量。

一張裸片上可以放多個平面(Plane),一般一個平面上的存儲容量大概在GB級別。一個平面上面,會划分成很多個塊(Block),一般一個塊(Block)的存儲大小, 通常幾百KB到幾MB大小。一個塊里面,還會區分很多個頁(Page),就和我們內存里面的頁一樣,一個頁的大小通常是4KB。

對於SSD硬盤來說,數據的寫入叫作Program。寫入不能像機械硬盤一樣,通過覆寫(Overwrite)來進行的,而是要先去擦除(Erase),然后再寫入。

SSD的讀取和寫入的基本單位,不是一個比特(bit)或者一個字節(byte),而是一個(Page)。SSD的擦除單位必須按照來擦除。

SSD的使用壽命,其實是每一個塊(Block)的擦除的次數。

SLC的芯片,可以擦除的次數大概在10萬次,MLC就在1萬次左右,而TLC和QLC就只在幾千次了。

SSD讀寫的生命周期

白色代表這個頁從來沒有寫入過數據,綠色代表里面寫入的是有效的數據,紅色代表里面的數據,在我們的操作系統看來已經是刪除的了。

一開始,所有塊的每一個頁都是白色的。隨着我們開始往里面寫數據,里面的有些頁就變成了綠色。

然后,因為我們刪除了硬盤上的一些文件,所以有些頁變成了紅色。但是這些紅色的頁,並不能再次寫入數據。因為SSD硬盤不能單獨擦除一個頁,必須一次性擦除整個塊,所以新的數據,我們只能往后面的白色的頁里面寫。這些散落在各個綠色空間里面的紅色空洞,就好像硬盤碎片。

如果有哪一個塊的數據一次性全部被標紅了,那我們就可以把整個塊進行擦除。它就又會變成白色,可以重新一頁一頁往里面寫數據。

在快要沒有白色的空頁去寫入數據的時候,SSD會做一次類似於Windows里面“磁盤碎片整理”或者Java里面的“內存垃圾回收”工作。找一個紅色空洞最多的塊,把里面的綠色數據,挪到另一個塊里面去,然后把整個塊擦除,變成白色,可以重新寫入數據。

DMA

為什么要發明DMA技術?

就目前而言I/O速度如何提升,比起CPU,總還是太慢。如果我們對於I/O的操作,都是由CPU發出對應的指令,然后等待I/O設備完成操作之后返回,那CPU有大量的時間其實都是在等待I/O設備完成操作。

但是,這個CPU的等待,在很多時候,其實並沒有太多的實際意義。我們對於I/O設備的大量操作,其實都只是把內存里面的數據,傳輸到I/O設備而已。

因此,計算機工程師們,就發明了DMA技術,也就是直接內存訪問(Direct Memory Access)技術,來減少CPU等待的時間。

DMA有什么用?

本質上,DMA技術就是我們在主板上放一塊獨立的芯片。在進行內存和I/O設備的數據傳輸的時候,我們不再通過CPU來控制數據傳輸,而直接通過DMA控制器(DMA Controller,簡稱DMAC)。

當傳輸大量數據的時候,DMAC可以等數據到齊了,再發送信號,給到CPU去處理,而不是讓CPU在那里忙等待。

DMAC是怎么控制數據傳輸的?

DMAC其實也是一個特殊的I/O設備,它和CPU以及其他I/O設備一樣,通過連接到總線來進行實際的數據傳輸。總線上的設備呢,其實有兩種類型。一種我們稱之為主設備(Master),另外一種,我們稱之為從設備(Slave)。

想要主動發起數據傳輸,必須要是一個主設備才可以,CPU就是主設備。而我們從設備(比如硬盤)只能接受數據傳輸。

DMAC它既是一個主設備,又是一個從設備。對於CPU來說,它是一個從設備;對於硬盤這樣的IO設備來說呢,它又變成了一個主設備。

我們下面看一張圖:

  1. 首先,CPU還是作為一個主設備,向DMAC設備發起請求。這個請求,其實就是在DMAC里面修改配置寄存器。
  2. CPU修改DMAC的配置的時候,會告訴DMAC這樣幾個信息:
    • 首先是源地址的初始值以及傳輸時候的地址增減方式

所謂源地址,就是數據要從哪里傳輸過來。如果我們要從內存里面寫入數據到硬盤上,那么就是要讀取的數據在內存里面的地址。
* 其次是目標地址初始值和傳輸時候的地址增減方式
* 第三個是要傳輸的數據長度

  1. 設置完這些信息之后,DMAC就會變成一個空閑的狀態(Idle)。
  2. 如果我們要從硬盤上往內存里面加載數據,這個時候,硬盤就會向DMAC發起一個數據傳輸請求。這個請求並不是通過總線,而是通過一個額外的連線。
  3. 然后,我們的DMAC需要再通過一個額外的連線響應這個申請。
  4. 於是,DMAC這個芯片,就向硬盤的接口發起要總線讀的傳輸請求。數據就從硬盤里面,讀到了DMAC的控制器里面。
  5. 然后,DMAC再向我們的內存發起總線寫的數據傳輸請求,把數據寫入到內存里面。
  6. DMAC會反復進行上面第6、7步的操作,直到DMAC的寄存器里面設置的數據長度傳輸完成。
  7. 數據傳輸完成之后,DMAC重新回到第3步的空閑狀態。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM