1、在沒有DMA技術之前的I/O過程是這樣的:
- CPU發出對應的指令給磁盤控制器,然后返回
- 磁盤控制器收到指令后,于是就開始準備數據,會把數據放入到磁盤控制器的內部緩沖區,然后產生中斷
- CPU收到中斷信號后,停下手頭工作,接著把磁盤控制器的緩沖區的數據一次一個字節地讀進自己的寄存器。然后再把寄存器里的數據寫入到內存,而在數據傳輸的期間CPU是無法執行其他任務 的。
整個傳輸過程中都要CPU親自參與搬運數據的過程,而且這個過程,CPU是不能做其他事情的。這會大大降低CPU的效率,并且如果使用千兆網卡或者硬盤傳輸大量數據時,都用CPU搬運的話,肯定忙不過來
2、DMA技術
直接內存訪問 (Direct Memory Access) ,簡單理解就是,在進行I/O設備和內存的數據傳輸的時候,數據搬運的工作全部交給DMA控制器,而CPU不再參與任何與數據搬運相關的事情,這樣CPU就可以去處理其他的事務。
與磁盤的交互(IO操作)都交給了DMA控制器去做,CPU得到解放
具體過程:
- ?戶進程調? read ?法,向操作系統發出 I/O 請求,請求讀取數據到??的內存緩沖區中,進程進?阻塞狀態;
- 操作系統收到請求后,進?步將 I/O 請求發送 DMA ,然后讓 CPU 執?其他任務;
- DMA 進?步將 I/O 請求發送給磁盤;
- 磁盤收到 DMA 的 I/O 請求,把數據從磁盤讀取到磁盤控制器的緩沖區 中,當磁盤控制器的緩沖區被讀滿后,向 DMA 發起中斷信號,告知??緩沖區已滿;
- DMA 收到磁盤的信號,將磁盤控制器緩沖區中的數據拷?到內核緩沖區中 ,此時不占? CPU,CPU 可以執?其他任務;
- 當 DMA 讀取了?夠多的數據,就會發送中斷信號給 CPU;
- CPU 收到 DMA 的信號,知道數據已經準備好,于是將數據從內核拷?到?戶空間 ,系統調?返回
早期DMA只存在于主板上,如今IO設備越來越多,數據傳輸的需求也不盡相同,所以每個I/O設備里面都有自己的DMA控制器
3、傳統的文件傳輸
如果服務端要提供文件傳輸的功能,我們能想到的最簡單的方式是:將磁盤上的文件讀取出來,然后通過網絡協議發送給客戶端。
而傳統的I/O的工作方式是,數據讀取和寫入是從用戶空間到內核空間來回復制,而內核空間的數據是通過操作系統的I/O接口從磁盤讀取或寫入。
read(file, tmp_buf, len);
write(socket, tmp_buf, len);
這兩行代碼干了非常多的事,如圖:
可以看到,這期間發生了4次用戶態與內核態的上下文切換 ,因為發生了兩次系統調用,一次是read(),一次是write()。每一次系統調用,都要從用戶態切換到內核態,等內核態完成任務后又要切換回用戶態。而上下文切換的成本也很大,尤其在高并發的場景下,這類時間容易被累積放大,從而影響系統的性能。
其次,還發生了4次數據拷貝 ,兩次是DMA拷貝,兩次是CPU拷貝。
- 第?次拷?,把磁盤上的數據拷?到操作系統內核的緩沖區?,這個拷?的過程是通過 DMA 搬運的。
- 第?次拷?,把內核緩沖區的數據拷?到?戶的緩沖區?,于是我們應?程序就可以使?這部分數據了,這個拷?到過程是由 CPU 完成的。
- 第三次拷?,把剛才拷?到?戶的緩沖區?的數據,再拷?到內核的 socket 的緩沖區?,這個過程依然還是由 CPU 搬運的。
- 第四次拷?,把內核的 socket 緩沖區?的數據,拷?到?卡的緩沖區?,這個過程?是由 DMA 搬運的
只是搬運一份數據,結果卻進行了四次數據拷貝,過多的數據拷貝會消耗CPU資源,大大降低系統性能。
這種傳統的文件傳輸存在冗余的上下文切換和拷貝次數!
優化文件傳輸
1、如何減少用戶態與內核態的上下文切換的次數 ?
讀取磁盤數據時,之所以要發生上下文切換,這是因為用戶空間沒有權限操作磁盤或網卡,內核的權限最高。所以一般要通過內核去完成某些任務的時候,就需要使用操作系統提供的系統調用函數。
而一次系統調用必然會發生2次上下文切換:從用戶態切換到內核態,內核態完成任務后再切換回用戶態
所以要減少上下文切換的次數就要減少系統調用的次數
2、如何減少數據拷貝的次數?
傳統的文件傳輸過程會經過四次數據拷貝,而這其中,從內核的讀緩沖區拷貝到用戶的緩沖區中,再從用戶的緩沖區拷貝到socket的緩沖區 ,這個過程是沒有必要的。因為文件傳輸的應用場景中,在用戶空間我們并不會對數據再加工 ,可以省去拷貝到數據緩沖區這一步。
零拷貝
1、實現零拷貝技術的方式通常有2種:
- mmap+write
- sendfile
它們是如何減少上下文切換和數據拷貝的次數?
2、mmap+write
read()系統調用的過程,會將內核緩沖區的數據拷貝到用戶的緩沖區,為了減少這一步開銷,我們可以用mmap()替換read()系統調用函數。
buf = mmap(file, len);
write(sockfd, buf, len);
mmap()系統調用函數會直接把內核緩沖區里的數據映射到用戶空間,這樣操作系統內核與用戶空間就不需要再進行任何的數據拷貝操作
這樣做會將內核的讀緩沖區拷貝到用戶緩沖區,再從用戶緩沖區拷貝到socket的緩沖區 這兩次拷貝變成內核緩沖區拷貝到socket緩沖區 這一次拷貝
即從原來的四次拷貝變為三次拷貝,減少了一次數據拷貝的過程。
但這并不是理想的零拷貝,因為仍然需要通過CPU把內核緩沖區的數據拷貝到socket緩沖區中,而且仍然需要4次上下文切換
3、sendfile
在Linux內核版本2.1中,提供了一個專門發送文件的系統調用函數sendfile()。
- ?先,它可以替代前?的 read() 和 write() 這兩個系統調?,這樣就可以減少?次系統調? ,也就減少了 2 次上下?切換的開銷。
- 其次,該系統調?,可以直接把內核緩沖區?的數據拷?到 socket 緩沖區? ,不再拷?到?戶態,這樣就只有 2 次上下?切換,和 3 次數據拷?。
從 Linux 內核 2.4 版本開始起,對于?持?卡?持 SG-DMA 技術的情況下, sendfile() 系統,調?的過程發?了點變化,具體過程如下:
- 第?步,通過 DMA 將磁盤上的數據拷?到內核緩沖區?;(DMA拷貝)
- 第?步,緩沖區描述符和數據?度傳到 socket 緩沖區,這樣?卡的 SG-DMA 控制器就可以直接將內核緩存中的數據拷?到?卡的緩沖區? ,此過程不需要將數據從操作系統內核緩沖區拷?到socket 緩沖區中,這樣就減少了?次數據拷?;(SG-DMA拷貝)
所以,這個過程之中,只進行了一次系統調用(sendfile(),進?了 2 次數據拷?(磁盤到內核,內核到網卡)
這就是所謂的零拷貝技術。因為我們沒有在內存層面去拷貝數據,全程沒有通過CPU來搬運數據
4、總結
實現零拷貝技術的文件傳輸方式相比傳統文件傳輸的方式,減少了2次上下文切換和數據拷貝次數。只需要進行兩次上下文切換和兩次數據拷貝就可以完成文件的傳輸。并且兩次數據拷貝都不要通過CPU完成,是由DMA來完成。總體來看,零拷貝技術可以把文件傳輸的性能提高至少一倍以上
總結
1、早期I/O操作,內存與磁盤的數據傳輸的工作都是由CPU完成,此時CPU不能進行其他任務,會特別浪費CPU資源
2、為了解決這一問題,出現了DMA技術。每個I/O設備都有自己的DMA控制器,通過這個DMA 控制器,CPU 只需要告訴 DMA 控制器,我們要傳輸什么數據,從哪?來,到哪?去,就可以放?離開了。后續的實際數據傳輸?作,都會由 DMA 控制器來完成,CPU 不需要參與數據傳輸的?作。
3、傳統的IO工作方式,從硬盤讀取數據,通過網卡向外發送 。需要進行4次用戶態與內核態之間的上下文切換,4次數據拷貝。其中2次數據拷貝發生在內核的緩沖區和對應的硬件設備(磁盤、網卡)之間,由DMA完成;2次數據拷貝發送在用戶態和內核態之間,由CPU完成。這種傳輸方式有冗余的上下文切換次數和數據拷貝次數!
4、對于文件傳輸的優化,實現零拷貝。通過一次系統調用(sendfile)合并了磁盤讀取(read)和網絡發送(write)兩個操作 ,降低了上下文切換次數;只進行了兩次數據拷貝,從磁盤文件到內核緩沖區,從內核緩沖區到網卡,都是由DMA搬運,降低了數據拷貝次數 !
5、零拷貝技術是基于 PageCache 的,PageCache 會緩存最近訪問的數據,提升了訪問緩存數據的性能,同時,為了解決機械硬盤尋址慢的問題,它還協助 I/O 調度算法實現了 IO 合并與預讀,這也是順序讀?隨機讀性能好的原因。這些優勢,進?步提升了零拷?的性能。
6、當傳輸大文件時,不能使用零拷貝,因為可能由于 PageCache 被??件占據,?導致「熱點」小文件無法利用到 PageCache,并且大文件的緩存命中率不?,這時就需要使用「異步 IO + 直接 IO 」的方式。