您的位置:首页 > 运维架构 > Linux

利用/dev/mem和mmap读写linux内存的通用C代码及原理

2014-02-10 16:58 1271 查看
#define MMAP_MEM_PAGEALIGN   (4*1024-1)
typedef struct
{
unsigned int memAddr;
unsigned int memSize;
unsigned int mmapMemAddr;
unsigned int mmapMemSize;
unsigned int memOffset;

int    memDevFd;
volatile unsigned int *pMemVirtAddr;

} MMapCtrl;
MMapCtrl gMMapCtrl;
int32 common_MMap(uint32_t physAddr, uint32_t memSize , uint32_t *pMemVirtAddr){
gMMapCtrl.memDevFd = open("/dev/mem",O_RDWR|O_SYNC);
if(gMMapCtrl.memDevFd < 0)
{
printf(" ERROR: /dev/mem open failed !!!\n");
return -1;
}

gMMapCtrl.memOffset   = physAddr & MMAP_MEM_PAGEALIGN;

gMMapCtrl.mmapMemAddr = physAddr - gMMapCtrl.memOffset;

gMMapCtrl.mmapMemSize = memSize + gMMapCtrl.memOffset;

gMMapCtrl.pMemVirtAddr = mmap(
(void	*)gMMapCtrl.mmapMemAddr,
gMMapCtrl.mmapMemSize,
PROT_READ|PROT_WRITE|PROT_EXEC,MAP_SHARED,
gMMapCtrl.memDevFd,
gMMapCtrl.mmapMemAddr
);

if (gMMapCtrl.pMemVirtAddr==NULL)
{
printf(" ERROR: mmap() failed !!!\n");
return -1;
}
*pMemVirtAddr = (UInt32)((UInt32)gMMapCtrl.pMemVirtAddr + gMMapCtrl.memOffset);

return 0;
}

Int32 common_unmapMem()
{
if(gMMapCtrl.pMemVirtAddr)
munmap((void*)gMMapCtrl.pMemVirtAddr, gMMapCtrl.mmapMemSize);

if(gMMapCtrl.memDevFd >= 0)
close(gMMapCtrl.memDevFd);

return 0;
}

    linux 内核为用户提供了一个/dev/mem的驱动程序,使用户直接访问系统物理内存成为可能,上面的片段代码就是利用mmap和/dev/mem建立起直接读写系统物理内存的渠道。搞过嵌入式开发的人应该熟悉上面的代码,利用/dev/mem和mmap导出系统物理地址,免去了用户虚拟地址到内核逻辑地址的繁琐拷贝,提升效率。

1、简单介绍下dev/mem
    /dev/mem是linux下的一个字符设备,源文件是kernel/drivers/char/mem.c,有兴趣的可以下载内核源码看看,这个设备文件是专门用来读写物理地址用的。里面的内容是所有物理内存的地址以及内容信息。通常只有root用户对其有读写权限。源引网络资源对/dev/mem是这么评价的“/dev/mem是个好玩的东西,你竟然可以直接访问物理内存,这在linux下简直太神奇了,就想一个小偷想偷银行,可是发现银行戒备森严,正在小偷苦无对策的时候,突然发现银行有个后门,而且这个后门直通银行的金库。”
2、mmap驱动实现时注意事项
    曾经在写某板卡的pcie驱动时,需要mmap出映射到pcie总线地址的对应的物理内存地址,代码就不贴了,说下出的问题。在驱动加载上后,调用mmap出现了下面的错误,具体什么原因还是没搞清楚,没时间仔细研究了,工作不等人啊。
dspc868x_pcie_ep 0000:04:00.0: vma->vm_start: 0x4024a000

dspc868x_pcie_ep 0000:04:00.0: vma->vm_end: 0x4024b000

dspc868x_pcie_ep 0000:04:00.0: vma->vm_pgoff: 0x20000

dspc868x_pcie_ep 0000:04:00.0: Mapping 0x1000 bytes from address 0x20000000 success
Unhandled fault: Precise External Abort on non-linefetch (0x1018) at 0x4024a000

参考了别人写的其他设备驱动程序,在我的驱动实现mmap代码中添加如下的选项,禁止高速缓存选项,上面的错误就解决了。有对这部分特别熟悉的欢迎与我交流,指导下,谢谢。

vma->vm_flags |= VM_WRITE;
vma->vm_flags |= VM_RESERVED;
vma->vm_page_prot = (__pgprot(pgprot_val(pgprot_noncached(vma->vm_page_prot)) | (L_PTE_WRITE|L_PTE_DIRTY)));


另外mmap的介绍请参考:http://blog.csdn.net/linux_xiaomugua/article/details/7008867

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c语言 内核 内存