>>> 此貼的回復 >> 13.2 mmap設備操作
內存映射是現代 Unix 系統最有趣的特征之一。對於驅動程序來說,內存映射可以提供給用戶程序直接訪問設備內存的能力。
看一下X Window系統服務器的虛擬內存區域,就可以看到 mmap 用法的一個明顯的例子:
cat /proc/731/maps 08048000-08327000 r-xp 00000000 08:01 55505 /usr/X11R6/bin/XF86_SVGA 08327000-08369000 rw-p 002de000 08:01 55505 /usr/X11R6/bin/XF86_SVGA 40015000-40019000 rw-s fe2fc000 08:01 10778 /dev/mem 40131000-40141000 rw-s 000a0000 08:01 10778 /dev/mem 40141000-40941000 rw-s f4000000 08:01 10778 /dev/mem ...
X 服務器的VMA的整個列表是很長的,但是這裡我們對大部分的項都不感興趣。然而,確實可以看到 /dev/mem 的三個獨立的映射,它可以使我們對 X 服務器怎樣與顯示卡協同工作有一些了解。第一個映射顯示了映射到 fe2fc000 的一個 16KB 區域,這個地址遠遠高於系統上最高的 RAM 地址,它是 PCI 外圍設備(顯示卡)的一段內存區域,它是該卡的控制區域。中間的映射位於 a0000,它是在 640KB ISA 空洞中的標准位置。最後的 /dev/mem 映射是位於 f4000000 位置的一個相當大的區域,而且是顯示內存本身。這些區域也可以在 /proc/iomem 中看到:
000a0000-000bffff : Video RAM area f4000000-f4ffffff : Matrox Graphics, Inc. MGA G200 AGP fe2fc000-fe2fffff : Matrox Graphics, Inc. MGA G200 AGP
映射一個設備,意味著使用戶空間的一段地址關聯到設備內存上。無論何時,只要程序在分配的地址范圍內進行讀取或者寫入,實際上就是對設備的訪問。在 X 服務器的例子中,使用 mmap 可以既快速又簡單地訪問顯示卡的內存。對於象這樣的性能要求比較嚴格的應用來說,直接訪問能給我們提供很大不同。
正如讀者所懷疑的,並不是所有的設備都能進行 mmap 抽象;例如,象串口設備和其它面向流的設備,就無法實現這種抽象。mmap 的另一個限制是映射都是以 PAGE_SIZE 為單位的。內核只能在頁表一級上處理虛擬地址;因此,被映射的區域必須是 PAGE_SIZE 的整數倍,而且必須位於起始於 PAGE_SIZE 整數倍地址的物理內存內。如果區域的大小不是頁大小的整數倍,內核可通過生成一個稍微大一些的區域來調節頁面大小粒度。
這些限制對於驅動程序來說並不是很大的問題,因為程序訪問設備的動作總是依賴於設備的,它需要知道如何使得被映射的內存區域有意義,所以 PAGE_SIZE 對齊不是一個問題。在 ISA 設備用於某些非 x86 平台時存在一個比較大的限制,因為它們對於 ISA 硬件的開發並不一樣。例如,某些 Alpha 計算機將 ISA內存看成是不能直接映射的、離散的8位、16位或者32位項的集合。這種情況下,根本不能使用 mmap。不能直接將ISA地址映射到 Alpha 地址的原因,是由於兩種系統間不兼容的數據傳輸規范導致的。早期的 Alpha 處理器只能進行 32 位和 64 位的內存訪問,而 ISA 只能進行 8 位和 16 位的數據傳輸,並且沒有透明地將一個協議映射到另一個之上的方法。
在能夠使用 mmap 的情況下,使用 mmap 還有另外一個優勢。例如,我們已經討論了 X 服務器,它可以與顯示內存進行大量的數據交換。相對於 lseek/write 實現來說,將圖形顯示映射到用戶空間可以顯著提高吞吐量,另一個典型的例子是受程序控制的 PCI 設備。大多數 PCI 外圍設備都將它們自己的控制寄存器映射到內存地址上,而苛刻的應用程序可能更喜歡直接訪問寄存器,而不是重復調用 ioctl 來完成它的工作。
mmap 方法是 file_operations 結構中的一員,並且在執行 mmap系統調用時就會調用該方法。在調用實際方法之前,內核會完成很多工作,而且該方法的原型與其系統調用的原型具有很大區別。這與其它系統調用如 ioctl 和 poll 不同,在調用它們之前內核不需要做太多的工作。