Linux裝置驅動工程師之路——Linux記憶體管理機制及mmap方法
K-Style
轉載請註明來自于衡陽師範學院08電2 Y-Kee http://blog.csdn.net/ayangke,QQ:843308498
一、重點知識
1. Linux記憶體管理機制
Linux 2.6.29核心為每種CPU提供統一的介面,採用四級頁面管理構架。來相容二級、三級、四級管理架構的CPU。通過頁式管理機制完成虛擬位址(線性地址)到物理地址的映射。一般每個頁大小為4K。cr3寄存器中儲存了建立進程時分配的值。
Linux作業系統採用虛擬記憶體管理技術,使得每個進程都有獨立的進程地址空間,該空間大小是3G,使用者看到和接觸的都是虛擬位址,無法看到實際的物理地址。利用這種虛擬位址不但能起到保護作業系統的作用,而且更重要的是使用者程式可使用比實際實體記憶體更大的地址空間。
Linux將4G的虛擬位址空間劃分為兩個部分——使用者空間與核心空間。使用者空間從0到0xbfffffff,核心空間從3G到4G。使用者進程通常情況下只能訪問使用者空間的虛擬位址,不能訪問核心空間,例外是使用者進程通過系統調用訪問核心空間。
每個使用者空間是完全獨立的,互不想乾的。使用者空間對應進程,所有每當進程切換,使用者空間就會跟著變化。
實際的實體記憶體只有當進程真的去訪問新擷取的虛擬位址時,才會由“請頁機制”產生“缺頁異常”,從而進入分配實際頁框程式。
核心空間是由核心負責映射,它並不會跟著進程改變,是固定的。
實體記憶體896MB以上的部分稱之為高端記憶體。
2. mmap方法
實現mmap方法,驅動程式只需要為該位址範圍建立合適的頁表,並將vma->ops替換成一系列的新操作就可以了。
void *mmap(void *addr, size_t len, intprot, int flags, int fd, off_t offset)
記憶體映射函數,負責把檔案記憶體映射到虛擬記憶體空間,返回映射地址空間地址。
參數說明:
addr:指定映射的起始地址,通常設為NULL,由系統指定。
length:映射到記憶體的檔案長度
prot:映射區的保護模式,可以是PROT_EXEC(可執行)、PROT_READ(可讀)、PROT_WRITE(可寫)。
flag:映射區的特性,可以是
MAP_SHARED:寫入映射區的資料會複製迴文件,且允許其他映射該檔案的進程共用。
MAP_PRIVATE:對映射區的寫入操作會產生一個映射區的複製,對此地區的修改不會寫回原檔案。
fd:要映射的檔案描述符
offet:以檔案開始出的位移量,通常為0,從檔案頭開始映射。
int munmap(void *start, size_t length)
解除映射。
struct vm_area_struct
核心用來描述虛擬記憶體地區的結構。
int remap_pfn_range(structvm_area_struct *vma, unsigned long virt_add, unsigned long pfn, unsigned longpfn, unsigned long size, pgprot_t prot)
int io_remap_page_range(structvm_area_struct *vma, unsignde long virt_add, unsigned long phys_add, unsignedlong size, pgprot_t prot)
mmap的核心函數。他們映射了物理地址中從pfn表示的頁號開始的size個位元組到虛擬位址virt_add上。相關虛擬位址的保護位在port中指定。如果目標地址在I/O地址空間的話,使用io_remap_page_range函數。
二、驅動代碼
staticint mem_mmap(struct file *filp, struct vm_area_struct *vma){ intret = 0; structmem_dev *dev; dev= filp->private_data; ret = remap_pfn_range(vma,vma->vm_start, virt_to_phys(dev->data)>>PAGE_SHIFT, vma->vm_end- vma->vm_start, vma->vm_page_prot); //建立頁表 if(ret) return-EAGAIN; returnret;} static const struct file_operationsmem_fops = { .owner= THIS_MODULE, .open= mem_open, .write= mem_write, .read= mem_read, .release= mem_release, .llseek= mem_llseek, .ioctl= mem_ioctl, .poll= mem_poll, .mmap= mem_mmap,};