get-vm linux,linux2.4.19下__ioremap和get_vm_area的粗略理解

linux2.4.19下__ioremap和get_vm_area的粗略理解

文章来源:http://gliethttp.cublog.cn

对于提供了MMU(存储管理器,辅助操作系统进行内存管理,提供虚实地址转换等硬件支持)的处理器而言,Linux提供了复杂的存储管理系统,使得进程所能访问的内存达到4GB.进程的4GB内存空间被人为的分为两个部分--用户空间与内核空间.用户空间地址分布从0到3GB(PAGE_OFFSET,在at91rm9200中它等于0xC0000000),3GB到4GB为内核空间.

内核空间中,从3G到VMALLOC_START这段地址是物理内存映射区域(该区域中包含了内核镜像、物理页框表mem_map等等),比如我们使用的vmware虚拟系统内存是160M,那么3G~3G+160M这片内存就应该映射物理内存.在物理内存映射区之后,就是vmalloc区域.对于160M的系统而言,VMALLOC_START位置应在3G+160M附近(在物理内存映射区与VMALLOC_START期间还存在一个8M的hole来防止跃界),VMALLOC_END的位置接近4G(最后位置系统会保留AT91C_IO_VIRT_BASE~0xFFFFFFFF作为at91rm9200的IO寄存器操作专用,实际过程是将0xFFFA0000 .. 0xFFFFFFFF寄存器物理地址,线性映射到0xFEFA0000 .. 0xFF000000虚拟地址,所以AT91C_IO_VIRT_BASE=0xFEFA0000,以后直接使用#define AT91_IO_P2V(x)    ((x) - AT91C_IO_PHYS_BASE + AT91C_IO_VIRT_BASE);就可以将物理地址直接线性的转换为虚拟地址).

kmalloc和get_free_page申请的内存位于物理内存映射区域,而且在物理上也是连续的,它们与真实的物理地址只有一个固定的偏移,因此存在较简单的转换关系,virt_to_phys()可以实现内核虚拟地址转化为物理地址,转换过程只是将虚拟地址减去3G,与之对应的函数为phys_to_virt(),将内核物理地址转化为虚拟地址.

而vmalloc申请的内存则位于VMALLOC_START~VMALLOC_END之间,与物理地址没有简单的转换关系,虽然在逻辑上它们也是连续的,但是在物理上它们不要求连续

几乎每一种外设都是通过读写设备上的寄存器来进行的,通常包括控制寄存器、状态寄存器和数据寄存器三大类,外设的寄存器通常被连续地编址.根据CPU体系结构的不同,CPU对IO端口的编址方式有两种:

(1)I/O映射方式(I/O-mapped)

典型地,如X86处理器为外设专门实现了一个单独的地址空间,称为"I/O地址空间"或者"I/O端口空间",CPU通过专门的I/O指令(如X86的IN和OUT指令)来访问这一空间中的地址单元.

(2)内存映射方式(Memory-mapped)

RISC指令系统的CPU(如ARM、PowerPC等)通常只实现一个物理地址空间,外设I/O端口成为内存的一部分.此时,CPU可以象访问一个内存单元那样访问外设I/O端口,而不需要设立专门的外设I/O指令.

但是,这两者在硬件实现上的差异对于软件来说是完全透明的,驱动程序开发人员可以将内存映射方式的I/O端口和外设内存统一看作是"I/O内存"资源.

一般来说,在系统运行时,外设的I/O内存资源的物理地址是已知的,由硬件的设计决定.但是CPU通常并没有为这些已知的外设I/O内存资源的物理地址预定义虚拟地址范围,驱动程序并不能直接通过物理地址访问I/O内存资源,而必须将它们映射到核心虚地址空间内(通过页表),然后才能根据映射所得到的核心虚地址范围,通过访内指令访问这些I/O内存资源.Linux在io.h头文件中声明了函数ioremap(),用来将I/O内存资源的物理地址映射到核心虚地址空间(3GB-4GB)中.

1.结构体

1.1>struct vm_struct {

unsigned long flags;

void * addr;

unsigned long size;

struct vm_struct * next;

};

2.驱动程序引用

fb_info.RegAddr = (unsigned char*) ioremap_nocache(S1D_PHYSICAL_REG_ADDR,S1D_PHYSICAL_REG_SIZE);

#define ioremap_nocache(off,sz)        __arch_ioremap((off),(sz),1)

#define __arch_ioremap(off,sz,nocache)                \

({                                \

unsigned long _off = (off), _size = (sz);        \

void *_ret = (void *)0;                    \

if (iomem_valid_addr(_off, _size))            \

_ret = __ioremap(iomem_to_phys(_off),_size,0);    \

_ret;                            \

})

#define iomem_valid_addr(iomem,size) (1)

#define iomem_to_phys(iomem)         (iomem)

所以ioremap_nocache函数最后还是等效于 直接调用 __ioremap函数

3.__ioremap函数分析[/arch/arm/mm/Ioremap.c]

void * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags)

{

void * addr;

struct vm_struct * area;

unsigned long offset, last_addr;

//size=0或者32位数据wraparound了将返回null    last_addr = phys_addr + size - 1;

if (!size || last_addr < phys_addr)

return NULL;

//#define PAGE_ALIGN(addr)      (((addr)+PAGE_SIZE-1)&PAGE_MASK)

//页面对齐,最后经过页规整之后,size的值将是页大小的整倍数,即:size % PAGE_SIZE将等于0

offset = phys_addr & ~PAGE_MASK;

phys_addr &= PAGE_MASK;

size = PAGE_ALIGN(last_addr) - phys_addr;

area = get_vm_area(size, VM_IOREMAP);

if (!area)

return NULL;

addr = area->addr;

if (remap_area_pages(VMALLOC_VMADDR(addr), phys_addr >> PAGE_SHIFT, size, flags)) {//remap_area_pages待读

vfree(addr);

return NULL;

}

return (void *) (offset + (char *)addr);

}

//#define __va(x)               ((void *)__phys_to_virt((unsigned long)(x)))

//#define TASK_SIZE             (0xc0000000)        3GB//#define AT91_SDRAM_BASE       (0x20000000)        sdram起始地址

//#define PAGE_OFFSET           TASK_SIZE//#define PHYS_OFFSET           (AT91_SDRAM_BASE)//#define __virt_to_phys(vpage) ((vpage) - PAGE_OFFSET + PHYS_OFFSET)//用来线性转换3GB~VMALLOC_START-8M虚拟内存地址到物理内存SDRAM地址

//#define __phys_to_virt(ppage) ((ppage) + PAGE_OFFSET - PHYS_OFFSET)//将物理内存AT91_SDRAM_BASE~meminfo.end地址线性转换成3GB~VMALLOC_START-8M的虚拟地址

//在arch/arm/mm/Init.c的void __init mem_init(void)中有high_memory = (void *)__va(meminfo.end);这么一句,

//在arch/arm/kernel/Setup.c的void __init setup_arch(char **cmdline_p)命令行解释函数中,调用函数:

//parse_cmdline(struct meminfo *mi, char **cmdline_p, char *from);实现了meminfo的填充,其中如果用户通过uboot之类

//的参数mem=31M方式传递了内存大小,那么由linux上电默认的内存大小32M将会被mem=31M代替,这样最后的1M物理内存就被闲置了,可以//作DMA专用,也可以通过参数@0x20000000指定外部sdram的起始地址,一般不需要,linux上电默认的地址就是sdram的起始地址了.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值