计算机组成原理:DMA
引入
過去幾年里,整個計算機產業,都在嘗試不停地提升IO設備的速度。但是,無論IO速度怎么提升,比如CPU,總還是太慢。SSD 硬盤的 IOPS 可以到 2 萬、4 萬,但是我們 CPU 的主頻有 2GHz 以上,也就意味著每秒會有 20 億次的操作。
如果我們對于IO的操作,都是由CPU發出對應的指令,然后等待IO設備完成操作之后返回,那CPU就會有大量的時間其實都是在等待IO設備完成操作。
但是,這個CPU的等待,在很多時候,其實并沒有太多的實際意義。我們對于IO設備的大量操作,其實都只是把內存里面的數據,傳輸到IO設備而已。在這種情況下,其實CPU只是在傻等而已。特別是當傳輸的數據量比較大的時候,比如進行大文件復制,如果所有數據都要經過CPU,實在是太浪費時間了。
因此,計算機工程師們,就發明了 DMA 技術,也就是直接內存訪問(Direct MemoryAccess)技術,來減少CPU等待的時間
DMA,一個協處理器
本質上,DMA技術就是我們在主板上放一塊獨立的芯片。在進行內存和IO設備的數據傳輸的時候,我們不再通過CPU來控制數據傳輸,而是直接通過DMA控制器(DMA Controller,簡稱 DMAC)。這塊芯片,我們可以認為它其實就是一個協處理器(Co-Processor)。
DMAC最有價值的地方體現在,當我們要傳輸的數據特別大、速度特別快,或者傳輸的數據特別小、速度特別慢的時候
比如說,我們用千兆網卡或者硬盤來傳輸大量數據時,如果都用CPU來搬運,可能忙不過來,所以可以選擇DMAC。而當數據傳輸很慢的時候,DMAC可以等數據到齊了,再發送信號,給到CPU去處理,而不會讓CPU在那里忙等待。
DMAC 是一塊“協處理器芯片”,這里的“協”,指的是"協助"CPU,完成對應的數據傳輸工作。在DMAC控制數據傳輸的過程中,我們還是需要CPU的。
除此之外,DMAC也是一個特殊的IO設備。它和CPU以及其他IO設備一樣,通過連接到總線來進行實際的數據傳輸。總線上的設備呢,其實有兩種類型。一種我們稱之為主設備(Master),另外一種,我們稱之為從設備(Slave)。
- 想要主動發起數據傳輸,必須要是一個主設備來可以,CPU就是主設備。而我們從設備(比如硬盤)只能接收數據傳輸。
- 所以,如果通過CPU來傳輸數據,要么是CPU從IO設備讀數據,要么是CPU向IO設備寫數據。
那我們的 I/O 設備不能向主設備發起請求么?可以是可以,不過這個發送的不是數據內容,而是控制信號。IO設備可以告訴CPU,我這里有數據要傳輸給你,但是實際數據是CPU從 I/O 設備拉走的,而不是 I/O 設備推給 CPU 的。
不過,DMAC 就很有意思了,它既是一個主設備,又是一個從設備。對于 CPU 來說,它是一個從設備;對于硬盤這樣的 IO 設備來說呢,它又變成了一個主設備。那使用 DMAC進行數據傳輸的過程究竟是什么樣的呢?下面我們來具體看看。
- 首先是源地址的初始值和傳輸時候的地址增減方式
- 所謂源地址,就是數據要從哪里傳輸過來。如果我們要從內存里面寫入數據到硬盤上,那么就是要讀取的數據在內存里面的地址。如果是從硬盤讀取數據到內存,那么就是硬盤的IO接口的地址
- IO的地址可以是一個內存地址,也可以是一個端口地址。而地址的增減就是說,數據時從大的地址向小的地址傳輸,還是從小的地址向大的地址傳輸
- 其次就是目標地址(數據傳輸的目的地)初始值和傳輸時候的地址增減方式。
- 第三個自然是要傳輸的數據長度,也就是我們一共要傳輸多少數據
所以,整個數據傳輸的過程中,我們不是通過CPU來搬運數據,而是由DMAC這個芯片來搬運數據。但是CPU在這個過程中也是必不可少的。因為傳輸什么數據,從哪里傳輸到哪里,其實還是由CPU來設置的。這也是為什么,DMAC叫做“協處理器”
最早,計算機里是沒有 DMAC 的,所有數據都是由 CPU 來搬運的。隨著對于數據傳輸的需求越來越多,先是出現了主板上獨立的 DMAC 控制器。到了今天,各種 I/O 設備越來越
多,數據傳輸的需求越來越復雜,使用的場景各不相同。加之顯示器、網卡、硬盤對于數據傳輸的需求都不一樣,所以各個設備里面都有自己的 DMAC 芯片了。
為什么那么快?一起來看 Kafka 的實現原理
了解了 DMAC 是怎么回事兒,那你可能要問了,這和我們實際進行程序開發有什么關系呢?有什么 API,我們直接調用一下,就能加速數據傳輸,減少 CPU 占用嗎?
過去幾年的大數據浪潮里面,還真有一個開源項目很好地利用了 DMA 的數據傳輸方式,通過 DMA 的方式實現了非常大的性能提升。這個項目就是Kafka。下面我們就一起來看看它究竟是怎么利用 DMA 的。
- Kafka 是目前實時數據傳輸管道的標準解決方案。
- Kafka是一個用來處理實時數據的管道,我們常常把它來做一個消息隊列,或者用來收集和落地海量的日志。作為一個處理實時數據和日志的管道,瓶頸自然也在IO層面
- Kafka里面常會有兩種常見的海量數據傳輸的情況,一種是從網絡中接收上游的數據,然后需要落地到本地磁盤上,確保數據不丟失,另一種情況是,從本地磁盤上讀取出來,通過網絡發送出去。
我們來看一看后一種情況,從磁盤讀數據發送到網絡上去。如果我們自己寫一個簡單的程序,最直觀的辦法,自然是用一個文件讀操作,從磁盤上把數據讀到內存里面來,然后再用一個 Socket,把這些數據發送到網絡上去。
File.read(fileDesc, buf, len); Socket.send(socket, buf, len);在這個過程中,數據一共發生了四次傳輸的過程,其中兩次是DMA的傳輸,另外兩次是通過CPU控制的傳輸:
- 第一次傳輸,是從硬盤上,讀到操作系統內核的緩沖區。這個傳輸是通過DMA搬運的
- 第二次傳輸,需要從內核緩沖區里面的數據,復制到我們應用分配的內存里面。這個傳輸是通過CPU搬運的
- 第三次傳輸,是要從應用的內存里面,再寫到操作系統的socket的緩沖區里面去。這個傳輸,還是由CPU搬運的
- 第四次傳輸,需要再從socket緩沖區里面,寫到網卡的緩沖區里面去,這個傳輸也是通過DMA搬運的。
這個時候,你可以回過頭看看這個過程。我們只是要“搬運”一份數據,結果卻整整搬運了四次。而且這里面,從內核的讀緩沖區傳輸到應用的內存里,再從應用的內存里傳輸到
Socket 的緩沖區里,其實都是把同一份數據在內存里面搬運來搬運去,特別沒有效率。
像 Kafka 這樣的應用場景,其實大部分最終利用到的硬件資源,其實又都是在干這個搬運數據的事兒。所以,我們就需要盡可能地減少數據搬運的需求。
事實上,Kafka 做的事情就是,把這個數據搬運的次數,從上面的四次,變成了兩次,并且只有 DMA 來進行數據搬運,而不需要 CPU。
@Override public long transferFrom(FileChannel fileChannel, long position, long count) throws IOExecptionreturn fileChannel.transferTo(position, count, socketChannel); }Kafka的代碼調用了Java NIO庫,具體是FileChannel里面的transsferTo方法。我們的數據并沒有讀到中間的應用內存里面,而是直接通過channel,寫入到對應的網絡設備里。并且,對于socket的操作,也不是寫到socket的buffer里面,而是直接根據描述符寫到網卡的緩沖區里面。于是,在這個過程之中,我們只進行了兩次數據傳輸。
第一次,是通過 DMA,從硬盤直接讀到操作系統內核的讀緩沖區里面。第二次,則是根據Socket 的描述符信息,直接從讀緩沖區里面,寫入到網卡的緩沖區里面。
這樣,我們同一份數據傳輸的次數從四次變成了兩次,并且沒有通過CPU來進行數據搬運,所有的數據都是通過DMA來進行傳輸的。
這這個方法里面,我們沒有在內存層面去“復制”數據,所以這個方法,也叫做“零拷貝”
在使用了這樣的零拷貝的方法之后呢,我們傳輸同樣數據的時間,可以縮減為原來的 1/3,相當于提升了 3 倍的吞吐率。
總結
以上是生活随笔為你收集整理的计算机组成原理:DMA的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 服务器带宽租用常见问题
- 下一篇: String , StringBuild