1 / 23

Chapter 15

Chapter 15. MMAP 與 DMA. 15.1 Linux 的記憶體管理. 主要是描述用於控管記憶體的各種資料結構 , 相當冗長 . 有了必要的基礎知識後 , 我們就可以開始使用這些結構. 15.1.1 位址的分類 (1/4). 作業系統的分類上 ,Linux 是一種虛擬記憶系統 . 虛擬記憶系統將 邏輯世界 ( 軟體 ) 與 現實世界 ( 硬體 ) 分隔開來 , 最大的好處是軟體可配置的空間超過 RAM 的實際容量 . 另一項優點是核心可在執行期改變行程的部分記憶空間 .

fran
Télécharger la présentation

Chapter 15

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Chapter 15 MMAP與DMA

  2. 15.1 Linux的記憶體管理 • 主要是描述用於控管記憶體的各種資料結構,相當冗長.有了必要的基礎知識後,我們就可以開始使用這些結構.

  3. 15.1.1 位址的分類(1/4) • 作業系統的分類上,Linux是一種虛擬記憶系統. • 虛擬記憶系統將邏輯世界(軟體)與現實世界(硬體)分隔開來,最大的好處是軟體可配置的空間超過RAM的實際容量. • 另一項優點是核心可在執行期改變行程的部分記憶空間. • Linux系統上不只有兩種位址(虛擬、實體),而且每種位址都有其特殊用途. 但核心原始程式裡沒有明確定義何種位址適用何種情況,所以必須相當謹慎小心.

  4. 15.1.1 位址的分類(3/4) • 使用者虛擬位址(User Virtual Address) • 簡稱為虛擬位址,位址寬度隨CPU架構而定 • 實體位址(Physical Address) • CPU與記憶體間的位址,寬度依CPU而定,但不一定與CPU暫存器相符 • 匯流排位址(Bus Address) • 週邊匯流排與記憶體的位址,具有高度的平台相依性 • 核心邏輯位址(Kernel Logical Address) 構成核心的正常位址空間,他們對應到所有主記憶體,而且通常被當作實體位址來使用。邏輯位址與實體位址只差距一段固定偏移量,通常存放在unsigned long或void *型別變數上。kmalloc()所傳回的記憶體,就是以邏輯位址來定位。 • 核心虛擬位址(Kernel Virtual Address) • 核心虛擬位址跟邏輯位址不同之處,在於核心虛擬位址與實體位址不一定有直接對應關係,虛擬位址通常存放在指標變數中。vmalloc()配置而來的記憶體位址是以虛擬位址來表示。

  5. 15.1.1 位址的分類(4/4) • <asm/page.h>定義了兩個可換算位址的巨集. • 如果你有一個邏輯位址,__pa()巨集可換算出其對應的實體位址 • __va()可將實體位址換算回邏輯位址,但僅限於低記憶體的實體位址才有效,因為高記憶體沒有邏輯位址

  6. 15.1.2 高低記憶體 • 核心邏輯位址與核心虛擬位址之間的差異,再配備超大量記憶體的32-bits系統上才凸顯出來. • 低記憶體(Low memory) • 存在kernel-space裡,可用邏輯位址來定位的記憶體 • 高記憶體(High memory) • 沒有邏輯位址的記憶體,因為系統上安裝超過32-bits定址範圍的實體記憶體. • 高低記憶體之間的分界線 核心在開機期間依據BIOS提供的資訊來決定的.在i386系統,分界通常位於1GB以下.這是核心自己設下的限制,因為核心必須將32-bit位址空間劃分成kernel-space與user-space兩大部份.

  7. 15.1.3 記憶體對應表與structpage(1/2) • 由於高記憶體沒有邏輯位址,處理記憶體的核心函式,紛紛改用struct page來代替邏輯位址. • page結構紀錄了關於實體記憶頁的一切資訊.系統上的每一頁記憶體,都有一個專屬的struct page,幾個重要欄位如下. • atomic_t count; • 此記憶頁的用量計次.當count降為0時,記憶頁會被釋放回自由串列. • void *virtual; • 本記憶頁對應的核心虛擬位址;若無對應的虛擬位址則指向NULL. • unsigned long flags; • 一組描述記憶頁狀態的位元旗標.如PG_locked、PG_reserved.

  8. 15.1.3 記憶體對應表與structpage(2/2) • 為了方便在struct page指標與虛擬位址之間轉換,Linux定義了一組方便的函式與巨集: • struct page *virt_to_page(void *kaddr); • 將核心邏輯位址轉換成對應的struct page指標. • void *page_address(struct page *page); • 傳回指定的page的核心虛擬位址.位於記憶體的記憶頁除非已事先映射到虛擬位址空間,否則沒有虛擬位址. • #include <linux/highmem.h> • void *kmap(struct page *page); • kmap()可傳回系統上任何記憶頁的核心虛擬位址. • Page在低記憶體→傳回該記憶頁的邏輯位址 • Page在高記憶體→將他映射到特殊的虛擬空間 • void kunmap(struct page *page); • 將kmap()所建立的特殊對應解除

  9. 15.1.4 虛擬記憶區(Virtual Memory Areas)(1/6) • 核心需要一個較高層級的機制,才能處理行程所見到的記憶體佈局.在Linux,這機制稱為虛擬記憶區(virtual memory areas),通常簡稱為區域或VMA. • 用來管理使用者行程的虛擬位址空間的各個區域 • 行程空間的虛擬位址空間,由下列區域構成: • 一個存放程式碼(executable binary)的區域,通常稱為text • 多個存放資料的區域 • 每一個有效的記憶體對映(memory mapping),各有一個區域.

  10. 15.1.4 虛擬記憶區(Virtual Memory Areas)(2/6) • 例:以下是init行程VMA的分布情形。cat /proc/1/maps • 08048000-0804e000 r-xp 00000000 03:01 64652 /sbin/init text • 0804e000-0804f000 rw-p 00006000 03:01 64652 /sbin/init data • 0804f000-08053000 rwxp 00000000 00:00 0 zero-mapped BSS • 40000000-40015000 r-xp 00000000 03:01 96278 /lib/ld-2.3.2.so text • 40015000-40016000 rw-p 00014000 03:01 96278 /lib/ld-2.3.2.so data • 40016000-40017000 rw-p 00000000 00:00 0 BSS for ld.so • 42000000-4212e000 r-xp 00000000 03:01 80290 /lib/tls/libc-2.3.2.so text • 4212e000-42131000 rw-p 0012e000 03:01 80290 /lib/tls/libc-2.3.2.so data • 42131000-42133000 rw-p 00000000 00:00 0 BSS for libc • bffff000-c0000000 rwxp 00000000 00:00 0 Stack segment • ffffe000-fffff000 ---p 00000000 00:00 0 vsyscall page • 各欄位的格式如下: • start-end|perm|offset|major:minor|inode|imagename

  11. 15.1.4 虛擬記憶區(Virtual Memory Areas)(3/6) • 上面每一欄除了imagename之外,都分別對應到struct vm_area_struct裡的欄位,這些欄位意義如下: • start-end VMA前後邊界的虛擬位址 • perm VMA的存取位元遮罩(r、w、x、p/s) • offset 檔案從何處開始映射到此VMA的起點 • major:minor 持有映射檔的裝置的主次編號 • inode 被映射檔案的inode編號 • imagename 被映射檔案(通常是可執行檔)的名稱 • 要實作mmap作業方法的驅動程式,必須填寫一個VMA結構,放在要求映射裝置的行程的位址空間裡

  12. 15.1.4 虛擬記憶區(Virtual Memory Areas)(4/6) • 我們看看struct vm_area_struct(定義在<linux/mm.h>)裡幾個最重要的欄位(很相似/proc/*/maps),因為驅動程式的mmap作業方法可能會需要用到這些欄位. • 驅動程式不能任意建立新的VMA,否則會破壞整個組織(串列與樹狀). • unsigned long vm_start; • unsigned long vm_end; • 此VMA涵蓋的虛擬位址範圍. • struct file *vm_file; • 如果VMA的映射對象是檔案,則vm_file指向該檔案的struct file結構.

  13. 14.1.4 虛擬記憶區(Virtual Memory Areas)(5/6) • unsigned long vm_pgoff; • 此區域在檔案的相對位置(以page為單位). • unsigned long vm_flags; • 一組描述VMA屬性的旗標.VM_IO表示此VMA映射到I/O region,VM_RESERVED要求記憶體管理系統不要將此VMA交換到磁碟上. • struct vm_operations_struct *vm_ops; • 一組可供核心用來操作VMA的函式. • void *vm_private_data; • 供驅動程式用於儲存私有資訊的欄位.

  14. 15.1.4 虛擬記憶區(Virtual Memory Areas)(6/6) • vm_operations_struct它紀錄了處理行程記憶體所需的三項作業方法:open、close與nopage如下所述. • void (*open)(struct vm_area_struct *area); • 核心會呼叫open作業方法,讓實作VMA的子系統有機會初始VMA 調整用量計次...等. • void (*close)(struct vm_area_struct *area); • 當VMA被摧毀,核心會呼叫它的close作業方法. • struct page *(*nopage)(struct vm_area_struct *vma,unsigned long address,int type); • 行程試圖讀取某個VMA記憶頁,但記憶頁不在主記憶體裡,則VMA的nopage作業方法會被呼叫。nopage從磁碟上的交換區讀回記憶頁內容,然後傳回一個指向實體記憶頁的struct page指標.若VMA沒定義自己的nopage方法,則核心會配置一個空的記憶頁

  15. 15.2 mmap作業方法(1/2) • 就驅動程式的觀點而言,記憶映射可用來提供直接存取裝置記憶體的能力給user-space應用程式. • 觀察X Window System server的VMA如何映射到/dev/mem,有助於理解mmap()系統呼叫的典型用法. • cat /proc/731/maps • 000a0000-000c0000 rwxs 000a0000 03:01 282652 /dev/mem • 000f0000-00100000 r-xs 000f0000 03:01 282652 /dev/mem • 00400000-005c0000 r-xp 00000000 03:01 1366927 /usr/X11R6/bin/Xorg • 006bf000-006f7000 rw-p 001bf000 03:01 1366927 /usr/X11R6/bin/Xorg • 2a95828000-2a958a8000 rw-s fcc00000 03:01 282652 /dev/mem • 2a958a8000-2a9d8a8000 rw-s e8000000 03:01 282652 /dev/mem • a0000:VGA卡的視訊記憶體的標準位置 • e8000000:位於系統記憶體的頂端,直接對應到顯卡上的視訊記憶體 • 從/proc/iomem也可看出顯示卡上的各段I/O記憶體

  16. 15.2 mmap作業方法(2/2) • 由於X server時常需要傳輸大量資料到視訊記憶體,如果將視訊記憶體直接映射到user-space,則應用程式可以直接填寫視訊記憶體,所以傳輸效率得以大幅提升. • mmap作業方法屬於file_operations結構的一部分,由mmap()系統呼叫觸發. • void *mmap(void *start,size_t length,int port,int flags,int fd,off_t offset); • int (*mmap)(struct filp *filp,struct vm_area_struct *vma); • 要支援mmap()系統呼叫的驅動程式,只需對該段虛擬位址範圍建立適當的分頁表 • 製作分頁表:全部交給remap_page_ranfe()函式一次搞定.

  17. 15.2.1 使用remap_pfn_range() • 要將某段虛擬位址映射到某段實體位址,必須另外產生新的分頁表,這個任務就交給它來完成. • int remap_pfn_range(struct vm_area_struct *vma, unsigned long virt_addr,unsigned long pfn, unsigned long size, pgprot_t); • 映射成功傳回0,失敗傳回錯誤碼 • vma 記憶頁所要映射到的VMA • virt_addr 要被重新映射的虛擬位址 • size 映射區規模(byte為單位) • prot 新VMA的保護方式.驅程能使用 vma->vm_page_port找到的值.

  18. 15.2.2 簡單的mmap實作 簡單線性的映射作法,讓應用程式可透過user-space的某段虛擬位址來存取裝置記憶體 • static int simple_remap_mmap(struct file *filp, struct vm_area_struct *vma) • { • if (remap_pfn_range(vma, vma->vm_start, vm->vm_pgoff, • vma->vm_end - vma->vm_start, • vma->vm_page_prot)) • return -EAGAIN; • vma->vm_ops = &simple_remap_vm_ops; • simple_vma_open(vma); • return 0; • }

  19. 15.2.3 增添新的VMA作業方法 • void simple_vma_open(struct vm_area_struct *vma) • { • printk(KERN_NOTICE "Simple VMA open, virt %lx, phys %lx\n", • vma->vm_start, vma->vm_pgoff << PAGE_SHIFT); • } • void simple_vma_close(struct vm_area_struct *vma) • { • printk(KERN_NOTICE "Simple VMA close.\n"); • } • static struct vm_operations_struct simple_remap_vm_ops = { • .open = simple_vma_open, • .close = simple_vma_close, • };

  20. 15.3直接I/O • 大部分的I/O作業,透過kernel-space的緩衝區做緩衝,隔離了user-space與實際裝置。好處:讓程式比較好寫,提升效能。 • 但在傳輸大量資料時,讓user-space直接與裝置I/O做溝通反而會比較好。

  21. 15.3.1實做直接I/O的關鍵函式 • 宣告於<linux/mm.h> • int get_user_pages(struct task_struct *tsk,struct mm_struct *mm, • unsigned long start,int len,int write,int force,struct page **pages, • struct vm_area_struct **vmas); • struct task_struct *tsk :指向要執行I/O的task_struct, • struct mm_struct *mm:指向的mm_struct即是形成的虛擬位址空間的所有VMA • unsigned long start:user-space緩衝區的起始位址 • int len:該緩衝區的長度 • int write:非零值,映射的記憶頁是供write存取用 • int force:不理會指定記憶頁的保護旗標,對驅動程式應設為零 • struct page **pages:指向user-space緩衝區的struct page串列 • 此函式的回傳值:實際映射的記憶頁數量。

  22. 15.3.2釋放記憶頁 • 完成直接I/O作業→釋放user-space記憶頁 • 1.檢查記憶頁是否為保留頁:PageReserved(); • 非絕對必要:user-space記憶體都不是保留頁 • 2.若改變了記憶頁內容→使用SetPageDirty(struct page *page)將記憶頁做標記,否則核心會直接釋放該記憶頁,而不寫回儲存裝置 • 3.將page從page cache釋放→page_cache_release(struct page *page); • 無論是否有改變記憶頁內容,最後都應做釋放的動作!

  23. 15.3.3非同步I/O • 非同步I/O讓user-space可不必等到I/O作業完成,就發起其他I/O作業。

More Related