下面所讨论的都是没有经过MMU的物理地址
1.物理实体:I/O端口(I/OPorts)(少数外设板卡含有IO Mem)+物理内存条(即主存)————
2.对外设的物理地址编址方式:I/O端口方式(独立编址)&I/O内存方式(统一编址)————I/O端口的本质一般是外设寄存器。从访问外设寄存器的角度来看,前者也称为I/O映射方式”(I/O-mapped),后者也称为内存映射方式(Memory-mapped IO, MMIO)
2.CPU的物理地址:IO空间 (独立编址独有)+内存空间————只有一个内存空间(统一编址32位的4G空间),英文为IO space 和 Memory Space。CPU通过设立专门的I/O指令(如X86的IN和OUT指令)来访问IO空间中的地址单元。
3. IO端口 vs IO内存: 当寄存器或内存位于IO空间时,称为IO端口(这里的IO端口是一个概念,而前面的I/O端口是寄存器物理实体,前者范围小)。当寄存器或内存位于内存空间时,称为IO内存。
-------------------------------------------------------------------------------------------------------------------
Linux 将基于“I/O映射方式(独立编址)”的或“内存映射方式(统一编址)”,的I/O端口(物理实体)通称为“I/O区域”(I/O region),这里的I/O region是一种外设物理实体资源,下面讨论访问这部分资源。
5.1 (当物理外设挂在独立编址CPU的IO空间物理地址中时)Linux访问独立编址特有的 IO空间中的IO端口2种方式:
5.1(a) 专有指令访问:I/O-mapped,直接使用 intb()/outb()之类的函数(对应专有指令)来读写IO端口 ;
5.1(b)偏移映射访问, Memory-mapped, 先把IO端口使用ioport_map()“映射(本质是偏移到)”到IO内存(即“内存空间” ,把IO空间的东西(物理实体),映射到内存空间了,平移了64k,正好移出独立编址的那64k空间)再使用访问IO内存的函数ioread/iowirte来访问 IO端口;
5. 2(当物理外设挂在独立编址CPU内存空间物理地址(RAM)中(这种情况存在吗?),或物理外设挂在统一编址CPU中内存空间物理地址)(注:后者内存空间包含memory地址段和外设地址段,但目前我还不知道外设实体是否可以挂在RAM地址段?)Linux访问两类IO内存:
通过ioremap()函数映射到内核空间的虚拟地址(3G-4G)然后应用程序再通过系统调用去访问内核虚拟地址。
--------------------------------------------------------------------------------------------------
6.1 ioport_map()和ioremap()的区别:
前者将64k物理地址空间,做个偏移制造一个“假象”的虚拟地址64k~128k之间,仅仅让工程师可使用统一的虚拟内存访问接口ioread8/iowrite8()访问物理实体寄存器。
后者ioremap()功能:将一个I/O物理地址空间映射到内核的虚拟地址空间3G-4G.然后再使用read/write接口访问。
void * ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags);
其中三个参数的含义为:
phys_addr:与requset_mem_region函数中参数start相同的I/O物理地址;
size:要映射的空间的大小;
flags:要映射的IO空间的和权限有关的标志;
6.2 memory和mmio区别: memory指主存(内存条的地址),MMIO特指统一编址留给外设的那部分“内存映射IO”地址,如下图
6.3 mmap()函数(手工虚拟内存),不经系统调用,将用户空间虚拟地址直接映射为物理地址。malloc用于DDR(内核动态分配虚拟内存) ,都是在内核代码(3G-4G)中提供给其他子系统用来分配内存的函数。vmalloc用于地址较大的高端内存,地址完全虚拟,需要对页表进行设置,效率低,分配的逻辑地址是连续的,但物理地址一般是不连续的,适用于分配大量内存的情况。。。kmalloc()分配的物理地址与虚拟地址只有一个PAGE—OFFSET偏移,不需要修改页表。
ioremap()用于外设:将一个外设物理地址空间(上文5.2处)映射到内核的虚拟地址空间3G-4G中的高端内存.然后再使用read/write系统调用接口访问。进程通过ioremap()访问物理地址需要先经过系统调用从操作系统用户态进入到内核态。
6.4 物理地址physical address、总线地址busaddress区别?总线地址是外设看到的电路地址,物理地址是MMU出来的地址,有时候相同,有时候需要片选基址+偏移用来转换
----------------------------------------------------------------------------------------
6.5.上面说的基本都是真实物理地址,程序和指令中操作的是虚拟地址,虚拟地址和物理地址转换如下:
6.6 访问IO Ports 和IO Mem(挂在外设上的存储器)的区别,访问外部IO MEM必须通过remap映射到内核的MEM空间后才能访问。
在驱动程序编写过程中,很少会注意到IO Port和IO Mem的区别。虽然使用一些不符合规范的代码可以达到最终目的,这是极其不推荐使用的。
结合下图,我们彻底讲述IO端口和IO内存以及内存之间的关系。主存16M字节的SDRAM,外设是个视频采集卡,上面有16M字节的SDRAM作为缓冲区。
1. CPU是i386架构的情况
在i386系列的处理中,内存和外部IO是独立编址,也是独立寻址的。MEM的内存空间是32位可以寻址到4G,IO空间是16位可以寻址到64K。
在Linux内核中,访问外设上的IO Port必须通过IO Port的寻址方式。而访问IO Mem就比较罗嗦,外部MEM不能和主存一样访问,虽然大小上不相上下,可是外部MEM是没有在系统中注册的。访问外部IO MEM必须通过remap映射到内核的MEM空间后才能访问。
为了达到接口的同一性,内核提供了IO Port到IO Mem的映射函数。映射后IO Port就可以看作是IO Mem,按照IO Mem的访问方式(即remap)即可。
2. CPU是ARM 或PPC架构的情况
在这一类的嵌入式处理器中,IO Port的寻址方式是采用内存映射,也就是IO bus就是Mem bus。系统的寻址能力如果是32位,IO Port+Mem(包括IO Mem)可以达到4G。
访问这类IO Port时,我们也可以用IO Port专用寻址方式。至于在对IO Port寻址时,内核是具体如何完成的,这个在内核移植时就已经完成。在这种架构的处理器中,仍然保持对IO Port的支持,完全是i386架构遗留下来的问题,在此不多讨论。而访问IO Mem的方式和i386一致。
----------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------