首页 > 编程知识 正文

iometer测试结果分析,figsize函数python

时间:2023-05-03 10:15:35 阅读:18526 作者:2941

在开始之前,我先谈谈ioremap的作用。 ioremap主要映射寄存器。

为什么要映射?

内核空间只能访问虚拟地址的3~4G地址空间。 通常,3到4g空间的一部分映射物理内存,默认情况下不映射寄存器。 如果尝试访问某个寄存器,则必须将此注册的虚拟地址映射到高端内存。 内核空间可以直接访问。

下一篇文章分析了3到4g的内核空间和io映射。 值得仔细看看。

3359 blog.csdn.net/god leading/article/details/18702029

打开源代码并搜索ioremap,可以看到有三个函数的原型

一个是在include/sam-generic/目录下,仔细查看其中的源代码,它没有进行任何形式的映射,只有在不加MMU的时候才能使用它

剩下的两个由以下函数定义

从名字可以看出,我们使用了ram关联。 此外,由于没有将其他与架构相关的源代码添加到source insght中,因此__arch_ioremap中也没有突出显示它

上面相关的函数很多,所以以最长的ioremap函数为例进行分析

#defineioremap(cookie,size ) _arm_ioremap ) cookie,size,MT_DEVICE )第一个参数是寄存器的开始物理地址,第二个参数是映射的寄存器范围

__arm_ioremap的第三个参数描述了上述ioremap的几种格式。 我们主要使用通常的形式。 缺省情况下,以下分析用输入参数0进行分析

# definemt _ device0# definemt _ device _ non shared1# definemt _ device _ cached2# definemt _ device _ wc3搜索,共两个

另一个是我们需要分析的内容,这里除了传递的三个参数之外,还调用了另一个函数作为入口参数。

void _ iomem * _ arm _ io remap (unsigned long phys _ addr,size_t size,unsigned int mtype ) return__arm_arm ) }_Builtin_return_address(0)是指获得当前函数的返回地址,即意味着该函数被另一个函数调用、然后在该函数被执行之后返回如下所示。

6000 0000: ldr r0,=0x6666000 0004: bl call_func /*函数调用*/6000 0008: str r0,=0x555 /*调用返回地址*/,所以这里

接下来看看这个函数做了什么,

#ifdef_assembly_/*x强制转换为XY,或x*/#define_AC(x,y ) X#define _AT(T ) t, x ) x )转换为else#define_AC )或x () t ) x ) ) #endif#define PAGE_SHIFT12/* 1和1UL对我们来说是相同的,这里是112比特,即确定为definepage_size ) ) page_shift(/*0x1000-1--0xfff后至0xfff在32位系统上为0xfff,f000。 这里使用的作用是* mmu的存储器管理最小为一页,即4096字节。 因此,*/# define page _ mask ((page _ size-1 ) ) define_phys_to_pfn () paddr ) PAGE_SHIFT size_t size,unsigned int mtype,void *caller ) { unsigned long ler } unsigned long offset=phys _ addr~page _ mask; /*确定物理地址在页面内的偏移的*/unsigned long pfn=_ _ phys _ to _ pfn (phys _ addr ); /*查找包含物理地址的页面地址*//* * don ' tallowwraparoundorzerosize *注释非常清楚。 不

允许映射的范围为0,也不允许结束地址小于起始地址(即phys_addr + size - 1 > 0xffff,ffff) */last_addr = phys_addr + size - 1;if (!size || last_addr < phys_addr)return NULL; /* 这里传入参数分别是 页位置 页内偏移 映射大小 类型(我们默认是0) caller(ioremap后面的指定的地址) */return __arm_ioremap_pfn_caller(pfn, offset, size, mtype,caller);}

 

/* 仔细追这里面每个宏的定义,可以发现是页表的一些定义Hardware page table definitions. */static struct mem_type mem_types[] = {[MT_DEVICE] = { /* Strongly ordered / ARMv6 shared device */.prot_pte= PROT_PTE_DEVICE | L_PTE_MT_DEV_SHARED | L_PTE_SHARED,.prot_l1= PMD_TYPE_TABLE,.prot_sect= PROT_SECT_DEVICE | PMD_SECT_S,.domain= DOMAIN_IO,},[MT_DEVICE_NONSHARED] = { /* ARMv6 non-shared device */.prot_pte= PROT_PTE_DEVICE | L_PTE_MT_DEV_NONSHARED,.prot_l1= PMD_TYPE_TABLE,.prot_sect= PROT_SECT_DEVICE,.domain= DOMAIN_IO, }, ..................... [MT_MEMORY] = { /* 内存 */.prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE,.domain = DOMAIN_KERNEL,},[MT_ROM] = { /* NOR 之类n */.prot_sect = PMD_TYPE_SECT,.domain = DOMAIN_KERNEL,},[MT_MEMORY_NONCACHED] = {.prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE,.domain = DOMAIN_KERNEL,},};

 

上面的表,其实就是我们页表中包括cache、权限管理等一些组合起来的配置,做成表后方便后建立页表时直接使用其中某一项。

#define__pfn_to_phys(pfn)((pfn) << PAGE_SHIFT) /* 页到物理地址转换 *//* * ARMv6 supersection address mask and size definitions. */#define SUPERSECTION_SHIFT24#define SUPERSECTION_SIZE(1UL << SUPERSECTION_SHIFT) /* 0x1000000 */#define SUPERSECTION_MASK(~(SUPERSECTION_SIZE-1)) /* 0xff00,0000 */const struct mem_type *get_mem_type(unsigned int type){return type < ARRAY_SIZE(mem_types) ? &mem_types[type] : NULL;}void __iomem * __arm_ioremap_pfn_caller(unsigned long pfn,unsigned long offset, size_t size, unsigned int mtype, void *caller){const struct mem_type *type;int err;unsigned long addr; struct vm_struct * area;/* * High mappings must be supersection aligned * 对高映射,即大于4G空间映射,必须是16M对齐,我们32位的一般不会出错在这里 */if (pfn >= 0x1000 && (__pfn_to_phys(pfn) & ~SUPERSECTION_MASK))return NULL;type = get_mem_type(mtype); /* 我们传入的MT_DEVICE(设备[寄存器]),可以得到对应的在表中的该项的地址 */if (!type)return NULL;/* * Page align the mapping size, taking account of any offset. * 将页面大小对其, 例如起始地址0x5fff,fff8,范围0x20字节,则也要映射两页 */size = PAGE_ALIGN(offset + size); /* 找到一块满足size大小的区域 */area = get_vm_area_caller(size, VM_IOREMAP, caller); if (!area) return NULL; /* 将来映射好后,在高端内存区域的首地址 */ addr = (unsigned long)area->addr; /* 下面就是根据高端内存区域动态映射区域查找的地址,以及页信息,进行映射*/#ifndef CONFIG_SMP /* 我们不是多核CPU,所以要运行 */if (DOMAIN_IO == 0 && (((cpu_architecture() >= CPU_ARCH_ARMv6) && (get_cr() & CR_XP)) || cpu_is_xsc3()) && pfn >= 0x100000 && !((__pfn_to_phys(pfn) | size | addr) & ~SUPERSECTION_MASK)) {area->flags |= VM_ARM_SECTION_MAPPING;err = remap_area_supersections(addr, pfn, size, type);} else if (!((__pfn_to_phys(pfn) | size | addr) & ~PMD_MASK)) {area->flags |= VM_ARM_SECTION_MAPPING;err = remap_area_sections(addr, pfn, size, type);} else#endiferr = remap_area_pages(addr, pfn, size, type);if (err) { vunmap((void *)addr); return NULL; }flush_cache_vmap(addr, addr + size);return (void __iomem *) (offset + addr); /* 返回的地址是映射到内核空间的地址了 */}

#define VMALLOC_OFFSET(8*1024*1024) /* 8M */#define VMALLOC_START(((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))#define VMALLOC_END (0xFC000000)/* GFP_KERNEL这个标志位分配内存的一个选项,GFP_KERNEL是内核内存分配时最常用的,无内存可用时可引起休眠 */#define GFP_KERNEL(__GFP_WAIT | __GFP_IO | __GFP_FS)struct vm_struct *get_vm_area_caller(unsigned long size, unsigned long flags,void *caller){ /* */return __get_vm_area_node(size, 1, flags, VMALLOC_START, VMALLOC_END,-1, GFP_KERNEL, caller);}static struct vm_struct *__get_vm_area_node(unsigned long size,unsigned long align, unsigned long flags, unsigned long start,unsigned long end, int node, gfp_t gfp_mask, void *caller){static struct vmap_area *va;struct vm_struct *area; /* #define VM_IOREMAP 0x00000001 /* ioremap() and friends */在前面定义传进来的 */BUG_ON(in_interrupt());if (flags & VM_IOREMAP) { int bit = fls(size);if (bit > IOREMAP_MAX_ORDER)bit = IOREMAP_MAX_ORDER;else if (bit < PAGE_SHIFT)bit = PAGE_SHIFT;align = 1ul << bit;}size = PAGE_ALIGN(size); /* 页对齐 */if (unlikely(!size))return NULL; /* 申请一个struct vm_struct空间,存放未使用空间信息初始化 */area = kzalloc_node(sizeof(*area), gfp_mask & GFP_RECLAIM_MASK, node); if (unlikely(!area))return NULL;/* * We always allocate a guard page. 每次都多申请一个保护页面 */size += PAGE_SIZE; /* start 和 end 分别为 VMALLOC_START 和 VMALLOC_END align 为 1 * 已经使用的 vm 的信息分别存在各个 vmap_area 结构体中 * 所有的 vmap_area 结构体都在红黑树 vmap_area_root 中 * alloc_vmap_area 函数的主要功能是,查找红黑树 vmap_area_root ,找到 start 和 end 之间满足 size 大小的未使用空间, */va = alloc_vmap_area(size, align, start, end, node, gfp_mask);if (IS_ERR(va)) {kfree(area);return NULL;} /* 将该结构体插入到红黑树 */insert_vmalloc_vm(area, va, flags, caller);return area;}/* 初始化该结构体,并将其插入到红黑树中 */static void insert_vmalloc_vm(struct vm_struct *vm, struct vmap_area *va, unsigned long flags, void *caller){struct vm_struct *tmp, **p;vm->flags = flags;vm->addr = (void *)va->va_start;vm->size = va->va_end - va->va_start;vm->caller = caller;va->private = vm;va->flags |= VM_VM_AREA;write_lock(&vmlist_lock);for (p = &vmlist; (tmp = *p) != NULL; p = &tmp->next) {if (tmp->addr >= vm->addr)break;}vm->next = *p;*p = vm;write_unlock(&vmlist_lock);}

主要做了下面几件重要的事:

1.参数检查,确定不是RAM,地址不要越界等

2.在VMALLOC_START到VMALLOC_END之间找到所需要大小的地址空间。

3.建立页表

 

iounmap

明显就是做一些,移除红黑树节点,释放内存空间之类的事情了。

 

说明:

在一个进程中,同一个(一组)寄存器可以多次ioremap,每次ioremap,都会为其建立新的页表,即也会有不同的虚拟地址。但这些虚拟地址都是映射的同一片物理地址,所以无论操纵那一个ioremap返回的虚拟地址,最终都操作能够的是那块物理地址。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。