linuxDMA物理地址虚拟地址的映射⽅法(mmap)
linux DMA 物理地址虚拟地址的映射⽅法(mmap)
最近在研究linux DMA的使⽤,做了很多的测试验证,也踩了很多坑,因为⽇常⼯作原因 ,我对linux kernel 的研究确实不是很多,也是⼯作原因,最近花时间在研究linux DMA,说起来蛮惭愧的,下⾯把我踩得坑做⼀下间的描述
1. DMA传输的所需的地址是物理地址⽽⾮虚拟地址
2. ⽤户层如果想要做DMA传输,需要将物理地址和虚拟地址进⾏映射(mmap⽅法)
3. malloc 返回的地址是虚拟地址,不能⽤于DMA的传输,地址映射完后,可以⽤memset 进⾏初值赋值。
函数原型
void*mmap(void*start, size_t length,int prot,int flags,int fd, off_t offset);
返回值
1 EACCES:访问出错
2 EAGAIN:⽂件已被锁定,或者太多的内存已被锁定
3 EBADF:fd不是有效的⽂件描述词
4 EINVAL:⼀个或者多个参数⽆效
5 ENFILE:已达到系统对打开⽂件的限制
6 ENODEV:指定⽂件所在的⽂件系统不⽀持内存映射
mmap格式怎么打开7 ENOMEM:内存不⾜,或者进程已超出最⼤内存映射数量
8 EPERM:权能不⾜,操作不允许
9 ETXTBSY:已写的⽅式打开⽂件,同时指定MAP_DENYWRITE标志
10 SIGSEGV:试着向只读区写⼊
11 SIGBUS:试着访问不属于进程的内存区
参数
start:映射区的开始地址
length:映射区的长度
prot:期望的内存保护标志,不能与⽂件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在⼀起
1 PROT_EXEC :页内容可以被执⾏
2 PROT_READ :页内容可以被读取
3 PROT_WRITE :页可以被写⼊
4 PROT_NONE :页不可访问
flags:指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是⼀个或者多个以下位的组合体
1 MAP_FIXED //使⽤指定的映射起始地址,如果由start和len参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。如果指定的起始地址不可⽤,操作将会失败。并且起始地址必须落在页的边界上。
2 MAP_SHARED //与其它所有映射这个对象的进程共享映射空间。对共享区的写⼊,相当于输出到⽂件。直到msync()或者munmap()被调⽤,⽂件实际上不会被更新。
3 MAP_PRIVATE //建⽴⼀个写⼊时拷贝的私有映射。内存区域的写⼊不会影响到原⽂件。这个标志和以上标志是互斥的,只能使⽤其中⼀个。
4 MAP_DENYWRITE //这个标志被忽略。
5 MAP_EXECUTABLE //同上
6 MAP_NORESERVE //不要为这个映射保留交换空间。当交换空间被保留,对映射区修改的可能会得到保证。当交换空间不被保留,同时内存不⾜,对映射区的修改会引起段违例信号。
7 MAP_LOCKED //锁定映射区的页⾯,从⽽防⽌页⾯被交换出内存。
8 MAP_GROWSDOWN //⽤于堆栈,告诉内核VM系统,映射区可以向下扩展。
9 MAP_ANONYMOUS //匿名映射,映射区不与任何⽂件关联。
10 MAP_ANON //MAP_ANONYMOUS的别称,不再被使⽤。
11 MAP_FILE //兼容标志,被忽略。
12 MAP_32BIT //将映射区放在进程地址空间的低2GB,MAP_FIXED指定时会被忽略。当前这个标志只在x86-64平台上得到⽀持。
13 MAP_POPULATE //为⽂件映射通过预读的⽅式准备好页表。随后对映射区的访问不会被页违例阻塞。
14 MAP_NONBLOCK //仅和MAP_POPULATE⼀起使⽤时才有意义。不执⾏预读,只为已存在于内存中的页⾯建⽴页表⼊⼝。
fd:有效的⽂件描述词。如果MAP_ANONYMOUS被设定,为了兼容问题,其值应为-1 offset:被映射对象内容的起点
下⾯展⽰⼀段⽤于物理地址和虚拟地址进⾏映射的代码,有需要的朋友可以参考:
地址映射
int dma_mmap(unsigned long addr_p,unsigned int len,unsigned char**addr_v)
{
int fd;
unsigned char*mem;
unsigned int pagesize =getpagesize();
unsigned long base_addr = addr_p &~(pagesize -1);
unsigned int offset = addr_p - base_addr;
fd =open("/dev/mem", O_RDWR | O_SYNC);
if(fd <0){
printf("fail to open /dev/mem in mem_map\n");
return-1;
}
printf("base addr:0x%lx\n", base_addr);
mem =mmap(0, len + offset, PROT_READ | PROT_WRITE, MAP_SHARED,
fd, base_addr);
if(mem == MAP_FAILED){
close(fd);
fprintf(stderr,"fail to mmap /dev/mem in 0x%lx - %d\n",
addr_p, len);
return-1;
}
close(fd);
*addr_v = mem + offset;
return0;
}
addr_p: 物理地址
len:需要申请的地址内存长度
addr_v : 虚拟地址
解除地址映射
unsigned int dma_munmap(unsigned char*addr_v,unsigned long addr_p,unsigned int len)
{
int ret;
unsigned int pagesize =getpagesize();
unsigned long base_addr = addr_p &~(pagesize -1);
unsigned int offset = addr_p - base_addr;
addr_v = addr_v - offset;
ret =munmap(addr_v, len + offset);
if(ret <0){
fprintf(stderr,"fail to munmap 0x%lx - %d\n", addr_p, len);
return-1;
}
return0;
}
代码呢就不做细致的讲解的,主要的知识点就是:什么是 /dev/mem?
什么是 mmap()?
建议⾃⾏百度学习解决。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。