c-sharp**
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
記憶體對映函式mmap負責把檔案內容對映到程序的虛擬記憶體空間,通過對這段記憶體的讀取和修改,來實現對檔案的讀取和修改,而不需要再呼叫read,write等操作。
addr:指定對映的起始位址,通常設為null,由系統指定。
length:對映到記憶體的檔案長度。
prot:對映的保護方式,可以是:
prot_exec:對映區可被執行
prot_read:對映區可被讀取
prot_write:對映區可被寫入
prot_none:對映區不能訪問
flags:對映區的特性,可以是:
map_shared:
寫入對映區的資料會複製回檔案,且允許其他對映該檔案的程序共享。
map_private:
對對映區的寫入操作會產生乙個對映區的複製(copy_on_write),對此區域所做的修改不會寫回原檔案。
fd:由open返回的檔案描述符,代表要對映的檔案。
offset:以檔案開始處的偏移量,必須是分頁大小的整數倍,通常為0,表示從檔案頭開始對映。
解除對映:
c-sharp**
int munmap(void *start, size_t length);
功能:取消引數start所指向的對映記憶體,引數length表示欲取消的記憶體大小。
返回值:解除成功返回0,否則返回-1,錯誤原因存在於errno中。
虛擬位址區域:vm_area_struct
linux核心使用結構vm_area_struct()描述虛擬記憶體區域,其中幾個主要成員如下:
unsigned long vm_start 虛擬記憶體區域起始位址
unsigned long vm_end 虛擬記憶體區域結束位址
unsigned long vm_flags 該區域的標誌
如:vm_io和vm_reserved。vm_io將該vma標記為記憶體對映的io區域,vm_io會阻止系統將該區域包含在程序的存放轉存(core dump)中,vm_reserved標誌記憶體區域不能被換出。
mmap裝置操作
對映乙個裝置是指把使用者空間的一段位址關聯到裝置記憶體上,當程式讀寫這段使用者空間的位址時,它實際上是在訪問裝置。這裡需要做的兩個操作:
1.找到可以用來關聯的虛擬位址區間
2.關聯
其中找到可以用來關聯的虛擬位址區間是由核心完成的,mmap只要關聯這個操作。
mmap方法是file_operations結構的成員,在mmap系統呼叫發出時被呼叫。在此之前,核心已經完成了很多任務作。mmap裝置方法所需要做的就是建立虛擬位址到實體地址的頁表。
c-sharp**
void (*mmap)(struct file*, struct vm_area_struct *);
其中第二個引數struct vm_area_struct *相當於核心找到的,可以拿來用的虛擬記憶體區間。
mmap完成頁表的建立:
方法有二:
1.使用remap_pfn_range一次建立所有頁表;
2.使用nopage vma方法每次建立乙個頁表;
構造頁表的工作可由remap_pgn_range函式完成,原型如下:
c-sharp**
int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn, unsigned long size, pgprot_t prot);
vma是核心為我們找到的虛擬位址空間,addr要關聯的是虛擬位址,pfn是要關聯的實體地址,size是關聯的長度是多少。
mmap裝置操作例項:
c-sharp**
int memdev_map(struct file *filp, struct vm_area_struct *vma)
先說一下對於arm而言虛擬位址與實體地址的關係:
在arch/arm/include/asm/memory.h中:
c-sharp**
#define __virt_to_phys(x) ((x) - page_offset + phys_offset)
#define __phys_to_virt(x) ((x) - phys_offset + page_offset)
static inline unsigned long virt_to_phys(void *x)
static inline void *phys_to_virt(unsigned long x)
上面轉換過程的page_offset通常為3g,而phys_offset則定於為系統dram記憶體的基位址。因此,對於我們的開發板,並不是將0位址對映到3g,而是將外接的sdram的首位址對映到3g。注意:這裡的virt_to_phys和phys_to_virt方法僅適用於896mb以下的低端記憶體,高階記憶體的虛擬位址與實體地址之間不存在如此簡單的換算關係。下邊是fbmem.c中的mmap操作,示意圖如下:
c-sharp**
static
intfb_mmap(struct file *file, struct vm_area_struct * vma)
__acquires(&info->lock)
__releases(&info->lock)
mutex_lock(&info->lock);
/*視訊記憶體的實體地址*/
start = info->fix.smem_start;
/*求出幀緩衝的長度*/
len = page_align((start & ~page_mask) + info->fix.smem_len);
if (off >= len)
/*記憶體對映i/o的開始位置*/
start = info->fix.mmio_start;
/*記憶體對映i/o的長度*/
len = page_align((start & ~page_mask) + info->fix.mmio_len);
}
mutex_unlock(&info->lock);
/*把起始位址頁對齊*/
start &= page_mask;
/*如果區域大於總長度,報錯*/
if ((vma->vm_end - vma->vm_start + off) > len)
return -einval;
/*得到在檔案中的偏移*/
off += start;
/*把以頁為單位轉為以位元組為單位*/
vma->vm_pgoff = off >> page_shift;
/* this is an io map - tell maydump to skip this vma */
/*設定vma標誌,將vma設定成乙個記憶體對映的io區域,vm_reserved告訴記憶體管理系統不要將vma交換出去*/
vma->vm_flags |= vm_io | vm_reserved;
fb_pgprotect(file, vma, off);
/*真正建立對映的部分,為實體地址和虛擬位址建立頁表*/
if (io_remap_pfn_range(vma, vma->vm_start, off >> page_shift,
vma->vm_end - vma->vm_start, vma->vm_page_prot))
return -eagain;
return 0;
}
Linux驅動修煉之道
一些學習linux驅動的筆記整理在這裡與大家分享,如果那裡有錯誤也請高手指出。若干年後能進入intel開源中心或ibm搞linux kernel是我目前的目標。君子藏器於身,待時而動。文章 不斷更新中。linux驅動修煉之道 流水燈 linux驅動修煉之道 按鍵 linux驅動修煉之道 lcd背光與...
Linux驅動修煉之道
linux驅動修煉之道 流水燈 linux驅動修煉之道 按鍵 linux驅動修煉之道 lcd背光與gpio控制 linux核心訪問外設i o資源的方式 linux裝置管理檔案系統,mdev,熱插拔 linux驅動修煉之道 混雜裝置 linux驅動修煉之道 clock框架 linux裝置模型 上 之底...
Linux驅動修煉之道
linux驅動修煉之道 流水燈 linux驅動修煉之道 按鍵 linux驅動修煉之道 lcd背光與gpio控制 linux核心訪問外設i o資源的方式 linux裝置管理檔案系統,mdev,熱插拔 linux驅動修煉之道 混雜裝置 linux驅動修煉之道 clock框架 linux裝置模型 上 之底...