Linux驅動修煉之道 記憶體對映

2021-06-09 02:31:43 字數 3881 閱讀 3619

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裝置模型 上 之底...