Cpu可以像一个访问一个内存单元那样访问外设的I/O端口,而不需要设立专门的IO指令
内存管理单元MMU
作用是提供虚拟地址到物理地址的映射,内存访问权限保护和Cache缓存控制等硬件支持。
有几个概念需要明确。
TLB translation lookaside buffer 缓存少量的虚拟地址和物理地址的转换关系,是转换表的cache
TTW translation table walk 当TLB中没有对应的地址转换关系时,通过对内存中转换表的访问来获取对应关系。TTW成功后,结果写入TLB
Linux 2.6 支持不带MMU的处理器。
Linux操作系统单独为系统的每个用户进程分配独立的内存空间并保证用户空间不能直接访问内核空间的地址(因为MMU提供了内存访问权限保护)。
用户空间(0-3GB)与内核空间(3-4GB)
内核空间的物理内存映射区域为0-896M
用户空间内存动态申请:
Malloc和free应该成对出现。
static void *malloc(int size)
{
void *p;
if (size < 0)
error("Malloc error");
if (!malloc_ptr)
malloc_ptr = free_mem_ptr;
malloc_ptr = (malloc_ptr + 3) & ~3;/* Align */
p = (void *)malloc_ptr;
malloc_ptr += size;
if (free_mem_end_ptr && malloc_ptr >= free_mem_end_ptr)
error("Out of memory");
malloc_count++;
return p;
}
static void free(void *where)
{
malloc_count--;
if (!malloc_count)
malloc_ptr = free_mem_ptr;
}
内核空间内存动态申请:
Kmalloc(size_t size, int flags)在内核中分配
__get_free_pages() 返回一个指向新页的指针
Vmalloc() 开销大,需要建立新的页表
虚拟地址与物理地址的关系
Virt_to_phys()虚拟地址转换为物理地址
Phys_to_virt()物理地址转换为虚拟地址
/* bank page offsets */
#define PAGE_OFFSET1(PAGE_OFFSET + 0x10000000)
#define PAGE_OFFSET2(PAGE_OFFSET + 0x30000000)
#define __phys_to_virt(phys)\
((phys) >= 0x80000000 ?(phys) - 0x80000000 + PAGE_OFFSET2 :\
(phys) >= 0x20000000 ?(phys) - 0x20000000 + PAGE_OFFSET1 :\
(phys) + PAGE_OFFSET)
#define __virt_to_phys(virt)\
((virt) >= PAGE_OFFSET2 ? (virt) - PAGE_OFFSET2 + 0x80000000 :\
(virt) >= PAGE_OFFSET1 ? (virt) - PAGE_OFFSET1 + 0x20000000 :\
(virt) - PAGE_OFFSET)
ARM只有内存空间没有IO空间。设备的寄存器被称为IO内存。
Void *ioremap(unsigned long offset, unsigned long size)//建立新的页表,返回一个特殊的虚拟地址,该虚拟地址可以用来存取特定的物理地址范围。
Iounmap(void *addr);//用来释放获得的虚拟地址。
void __iomem *
__arm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype)
{
unsigned long last_addr;
unsigned long offset = phys_addr & ~PAGE_MASK;
unsigned long pfn = __phys_to_pfn(phys_addr);
/*
* Don't allow wraparound or zero size
*/
last_addr = phys_addr + size - 1;
if (!size || last_addr < phys_addr)
return NULL;
return __arm_ioremap_pfn(pfn, offset, size, mtype);
}
将设备物理地址映射到虚拟地址。这样可以直接通过指针访问这些地址。或是使用linux内核的IO内存读写函数来完成对设备内存的虚拟地址的读写
申请和释放IO内存
Request_mem_region(unsigned long start, unsigned long len, char * name);
不是必须的,但是建议使用。任务是检查申请的资源是否可用。
将设备地址映射到用户空间,不再需要用户空间到内核空间的复制过程。对这段地址的访问转换为对设备地址的访问。
void *mmap(void *start, size_t length, int prot, int flags,
int fd, off_t offset);