/proc/iomem, ioremap(), mmap()
The kernel manages device resources like registers as physical addresses(物理地址). These are the addresses in /proc/iomem. The physical address is not directly useful to a driver; it must use ioremap() to map the space and produce a virtual address(需要用ioremap()转换到虚拟地址).
ioremap:完成物理地址到内核虚拟地址空间的转换,用于外部设备的物理寄存器,不能用于System RAM。 (几乎每一种外设都是通过读写设备上的相关寄存器来进行的,通常包括控制寄存器、状态寄存器和数据寄存器三大类,外设的寄存器通常被连续地编址)
mmap:完成物理地址到用户虚拟地址空间的转换。
Ioremap:为了处理I/O内存区域( ioremap 只处理 soc 的 io memory ,不处理 System RAM)驱动程序应该通过调用ioremap()来映射该区域。ioremap主要是检查传入地址的合法性,建立页表(包括访问权限),完成物理地址到内核虚拟地址的转换。
Mmap:映射一个设备意味着关联一些用户空间地址到设备内存. 无论何时程序在给定范围内读或写, 它实际上是在存取设备。 为实现 mmap, 驱动只要建立合适的页表给这个地址范围。通过调用 remap_pfn_range来建立页表。把进程的用户空间虚拟地址,找一段出来分配给设备内存/内核内存。
内存映射,简而言之就是将内核空间的一段内存区域映射到用户空间。映射成功后,用户对这段内存区域的修改可以直接反映到内核空间,相反,内核空间对这段区域的修改也直接反映用户空间。那么对于内核空间与用户空间两者之间需要大量数据传输等操作的话效率是非常高的。当然,也可以将内核空间的一段内存区域同时映射到多个进程,这样还可以实现进程间的共享内存通信。
系统调用mmap()就是用来实现上面说的内存映射。最长见的操作就是文件(在Linux下设备也被看做文件)的操作,可以将某文件映射至内存(进程空间),如此可以把对文件的操作转为对内存的操作,以此避免更多的lseek()与read()、write()操作,这点对于大文件或者频繁访问的文件而言尤其受益。
两个函数的区别:
1、两者都是内存映射的函数
2、ioreamap()使用在内核里,将设备所在的物理地址映射到内核虚拟地址上,以后访问那个物理地址直接操作映射的虚拟地址就好。
3、mmap()用在用户空间,将设备所在的物理地址映射到用户虚拟地址上,以后访问那个物理地址直接操作映射的虚拟地址就好。
4、如果要操作设备的物理地址,有两种办法,一种是使用ioreamap(),然后用户调用read,write,ioctrl系统调用去访问那块物理地址,这里涉及到了两次拷贝,一次是用户空间到内核空间的拷贝,一次是内核空间到物理地址的拷贝;第二种是使用mmap(),这样可以只用到一次拷贝,可以直接从用户空间拷贝数据至物理地址。
Linux /proc/iomem与/proc/ioports-CSDN博客
一文搞懂 mmap 涉及的所有内容 - 知乎
关于物理地址和虚拟地址之间的转换
用户空间 mmap()函数 void *mmap(void *start, size_t length, int prot, int flags,int fd, off_t offset),下面就其参数解释如下:
- start:用户进程中要映射的用户空间的起始地址,通常为NULL(由内核来指定)
- length:要映射的内存区域的大小
- prot:期望的内存保护标志
- flags:指定映射对象的类型
- fd:文件描述符(由open函数返回)
- offset:设置在内核空间中已经分配好的的内存区域中的偏移,例如文件的偏移量,大小为PAGE_SIZE的整数倍
- 返回值:mmap()返回被映射区的指针,该指针就是需要映射的内核空间在用户空间的虚拟地址(通过调用 remap_pfn_range来建立页表)