gdb+gdbserver調(diào)試arm-linux程序
關(guān)于linux中的mmap調(diào)用如下,最好的辦法查看命令,man mmap:
必要的頭文件
#include
函數(shù)聲明
void * mmap(void *addr,size_t length,int prot,
int flags,int fd,off_t offset);
關(guān)于各個(gè)參數(shù)的意義如下:
1、返回值是一個(gè)通用型指針,這樣就保證了各種類型的申請(qǐng)方式。
2、void *addr 是程序員所希望的虛擬地址作為起始映射地址,通常為NULL,內(nèi)核自動(dòng)分配。
3、size_t length當(dāng)然是指需要映射的區(qū)域大小。
4、int flags是指對(duì)這段區(qū)域的保護(hù)方式。具體的可以參看內(nèi)核源碼的linux/mm.h。常用的是PROT_EXEC,PROT_READ,PROT_WRITE,PROT_NONE。
5、int flags主要是指對(duì)這段區(qū)域的映射方式,主要分為兩種方式MAP_SHARE,MAP_PRIVATE.其中的MAP_SHARE是指對(duì)映射區(qū)域的寫(xiě)操作會(huì)更新到文件中,這樣就相當(dāng)于直接操作文件。而MAP_PRIVATE通常采用一種稱為"寫(xiě)時(shí)保護(hù)的機(jī)制"實(shí)現(xiàn)映射,對(duì)映射區(qū)的寫(xiě)操作不會(huì)更新到文件中,實(shí)現(xiàn)方法是將需要被寫(xiě)操作的頁(yè)復(fù)制到重新分配的新頁(yè)中,然后再對(duì)新的頁(yè)進(jìn)行寫(xiě)操作。原來(lái)的映射并沒(méi)有改變,但是讀操作并不會(huì)重新分配物理內(nèi)存空間。具體的參考深入理解計(jì)算機(jī)系統(tǒng)。
6、int fd是指將被映射的文件描述符,映射需要保證文件描述符的正確性。
7、off_t offset是指從文件的具體位置開(kāi)始映射,通常情況下可以設(shè)置為0,即從開(kāi)頭映射。
基本的映射關(guān)系如下圖:
設(shè)備驅(qū)動(dòng)的mmap實(shí)現(xiàn)主要是將一個(gè)物理設(shè)備的可操作區(qū)域(設(shè)備空間)映射到一個(gè)進(jìn)程的虛擬地址空間。這樣就可以直接采用指針的方式像訪問(wèn)內(nèi)存的方式訪問(wèn)設(shè)備。在驅(qū)動(dòng)中的mmap實(shí)現(xiàn)主要是完成一件事,就是實(shí)際物理設(shè)備的操作區(qū)域到進(jìn)程虛擬空間地址的映射過(guò)程。同時(shí)也需要保證這段映射的虛擬存儲(chǔ)器區(qū)域不會(huì)被進(jìn)程當(dāng)做一般的空間使用,因此需要添加一系列的保護(hù)方式。
具體的實(shí)現(xiàn)過(guò)程如下:
/*主要是建立虛擬地址到物理地址的頁(yè)表關(guān)系,其他的過(guò)程又內(nèi)核自己完成*/
static int mem_mmap(struct file* filp,struct vm_area_struct *vma)
{
/*間接的控制設(shè)備*/
struct mem_dev *dev = filp->private_data;
/*標(biāo)記這段虛擬內(nèi)存映射為IO區(qū)域,并阻止系統(tǒng)將該區(qū)域包含在進(jìn)程的存放轉(zhuǎn)存中*/
vma->vm_flags |= VM_IO;
/*標(biāo)記這段區(qū)域不能被換出*/
vma->vm_flags |= VM_RESERVED;
/**/
if(remap_pfn_range(vma,/*虛擬內(nèi)存區(qū)域*/
vma->vm_start, /*虛擬地址的起始地址*/
virt_to_phys(dev->data)>>PAGE_SHIFT, /*物理存儲(chǔ)區(qū)的物理頁(yè)號(hào)*/
dev->size, /*映射區(qū)域大小*/
vma->vm_page_prot /*虛擬區(qū)域保護(hù)屬性*/
))
return -EAGAIN;
return 0;
}
具體的實(shí)現(xiàn)分析如下:
vma->vm_flags |= VM_IO;
vma->vm_flags |= VM_RESERVED;
上面的兩個(gè)保護(hù)機(jī)制就說(shuō)明了被映射的這段區(qū)域具有映射IO的相似性,同時(shí)保證這段區(qū)域不能隨便的換出。就是建立一個(gè)物理頁(yè)與虛擬頁(yè)之間的關(guān)聯(lián)性。具體原理是虛擬頁(yè)和物理頁(yè)之間是以頁(yè)表的方式關(guān)聯(lián)起來(lái),虛擬內(nèi)存通常大于物理內(nèi)存,在使用過(guò)程中虛擬頁(yè)通過(guò)頁(yè)表關(guān)聯(lián)一切對(duì)應(yīng)的物理頁(yè),當(dāng)物理頁(yè)不夠時(shí),會(huì)選擇性的犧牲一些頁(yè),也就是將物理頁(yè)與虛擬頁(yè)之間切斷,重現(xiàn)關(guān)聯(lián)其他的虛擬頁(yè),保證物理內(nèi)存夠用。在設(shè)備驅(qū)動(dòng)中應(yīng)該具體的虛擬頁(yè)和物理頁(yè)之間的關(guān)系應(yīng)該是長(zhǎng)期的,應(yīng)該保護(hù)起來(lái),不能隨便被別的虛擬頁(yè)所替換。具體也可參看關(guān)于虛擬存儲(chǔ)器的文章。
接下來(lái)就是建立物理頁(yè)與虛擬頁(yè)之間的關(guān)系,即采用函數(shù)remap_pfn_range(),具體的參數(shù)如下:
int remap_pfn_range(structvm_area_struct *vma, unsigned long addr,unsigned long pfn, unsigned long size, pgprot_t prot)
1、struct vm_area_struct是一個(gè)虛擬內(nèi)存區(qū)域結(jié)構(gòu)體,表示虛擬存儲(chǔ)器中的一個(gè)內(nèi)存區(qū)域。其中的元素vm_start是指虛擬存儲(chǔ)器中的起始地址。
2、addr也就是虛擬存儲(chǔ)器中的起始地址,通常可以選擇addr = vma->vm_start。
3、pfn是指物理存儲(chǔ)器的具體頁(yè)號(hào),通常通過(guò)物理地址得到對(duì)應(yīng)的物理頁(yè)號(hào),具體采用virt_to_phys(dev->data)>>PAGE_SHIFT.首先將虛擬內(nèi)存轉(zhuǎn)換到物理內(nèi)存,然后得到頁(yè)號(hào)。>>PAGE_SHIFT通常為12,這是因?yàn)槊恳豁?yè)的大小剛好是4K,這樣右移12相當(dāng)于除以4096,得到頁(yè)號(hào)。
4、size區(qū)域大小
5、區(qū)域保護(hù)機(jī)制。
返回值,如果成功返回0,否則正數(shù)。
測(cè)試代碼可以直接通過(guò)對(duì)虛擬內(nèi)存區(qū)域操作,實(shí)現(xiàn)不同的操作,如下:
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
int fd;
char *start;
char buf[2048];
strcpy(buf,"This is a test!!!!");
fd = open("/dev/memdev0",O_RDWR);
if(fd == -1)
{
printf("Error!!");
exit(-1);
}
/*創(chuàng)建映射*/
start = mmap(NULL,2048,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
/*必須檢測(cè)是否成功*/
if(start == -1)
{
printf("mmap error!!!");
exit(-1);
}
strcpy(start,buf);
printf("start = %s,buf = %s",start,buf);
strcpy(start,"Test is Test!!!");
printf("start = %s,buf = %s",start,buf);
/**/
strcpy(buf,start);
printf("start = %s,buf=%s",start,buf);
/*取消映射關(guān)系*/
munmap(start,2048);
/*關(guān)閉文件*/
close(fd);
exit(0);
}
經(jīng)過(guò)測(cè)試,成功得到了驅(qū)動(dòng)。
評(píng)論