linux内存管理
嵌入式处理器的分页管理为二级映射,内存空间与I/O空间统一编址,而x86处理器采用三级映射,内存空间与I/O空间独立编址。
在32位嵌入式系统中,存储空间的地址范围从0x0000_0000到0xFFFF_FFFF,内存和I/O共享从这4GB地址空间范围。
其主要包含以下几种存储空间:
1)设备空间(MT_DEVICE):二级分页
2)内部高速SRAM空间(MT_CACHECLEAN):一级分段
3)内部mini cache空间(MT_MINICLEAN):一级分段
4)低端中断向量(MT_LOW_VECTORS):两级分页
5)高端中断向量(MT_HIGH_VECTORS):两级分页
6)RAM内存空间(NT_MEMORY):一级分段
7)ROM(flash)空间(MT_ROM):一级分段
--------------------------------------------- S3C6410 datasheet -----------------------------------------------------
S3C6410 支持32 位物理地址域,并且这些地址域分成两部分,一部分用于存储,另一部分用于外设。
通过SPINE 总线访问主存,主存的地址范围是0x0000_0000~0x6FFF_FFFF。主存部分分成四个区域:
引导镜像区、内部存储区、静态存储区和动态存储区。
引导镜像区的地址范围是从0x0000_0000~0x07FF_FFFF,但是没有实际的映射内存
每块内部存储器的起始地址是确定的。
内部ROM 的地址范围是0x0800_0000~0x0BFF_FFFF,但是实际存储仅32KB。该区域是只读的,并且当内部ROM 启动被选择时,该区域能映射到引导镜像区。
内部SRAM 的地址范围是0x0C00_0000~0x0FFF_FFFF,但是实际存储仅4KB。该区域能被读和写,当NAND 闪存启动被选择时能映射到引导镜像区。
静态存储区的地址范围是0x1000_0000~0x3FFF_FFFF。通过该地址区域能访问SROM、SRAM、 NOR Flash、同步NOR接口设备、和Steppingstone。
动态存储区的地址范围是0x4000_0000~0x6FFF_FFFF。DMC0有权使用地址0x4000_0000~0x4FFF_FFFF,并且DMC1有权使用地址0x5000_0000~0x6FFF_FFFF。
外设区域通过PERI 总线被访问,它的地址范围是0x7000_0000~0x7FFF_FFFF。这个地址范围的所有的SFR 能被访问。而且如果数据需要从NFCON 或CFCON 传输,这些数据需要通过PERI 总线传输。
--------------------------------------------- 我是分割线 -------------------------------------------------------------
内存页(page)
ARM处理器支持1KB-4KB的页框大小。
ARM处理器默认页框打下为4KB。
页内存的结构为 struct page:
struct page { unsigned long flags; /* Atomic flags, some possibly * updated asynchronously */ atomic_t _count; /* Usage count, see below. */ union { atomic_t _mapcount; /* Count of ptes mapped in mms, * to show when page is mapped * & limit reverse map searches. */ struct { /* SLUB */ u16 inuse; u16 objects; }; }; union { struct { unsigned long private; /* Mapping-private opaque data: * usually used for buffer_heads * if PagePrivate set; used for * swp_entry_t if PageSwapCache; * indicates order in the buddy * system if PG_buddy is set. */ struct address_space *mapping; /* If low bit clear, points to * inode address_space, or NULL. * If page mapped as anonymous * memory, low bit is set, and * it points to anon_vma object: * see PAGE_MAPPING_ANON below. */ }; #if USE_SPLIT_PTLOCKS spinlock_t ptl; #endif struct kmem_cache *slab; /* SLUB: Pointer to slab */ struct page *first_page; /* Compound tail pages */ }; union { pgoff_t index; /* Our offset within mapping. */ void *freelist; /* SLUB: freelist req. slab lock */ }; struct list_head lru; /* Pageout list, eg. active_list * protected by zone->lru_lock ! */ /* * On machines where all RAM is mapped into kernel address space, * we can simply calculate the virtual address. On machines with * highmem some memory is mapped into kernel virtual memory * dynamically, so we need a place to store that address. * Note that this field could be 16 bits on x86 ... ;) * * Architectures with slow multiplication can define * WANT_PAGE_VIRTUAL in asm/page.h */ #if defined(WANT_PAGE_VIRTUAL) void *virtual; /* Kernel virtual address (NULL if not kmapped, ie. highmem) */ #endif /* WANT_PAGE_VIRTUAL */ #ifdef CONFIG_WANT_PAGE_DEBUG_FLAGS unsigned long debug_flags; /* Use atomic bitops on this */ #endif #ifdef CONFIG_KMEMCHECK /* * kmemcheck wants to track the status of each byte in a page; this * is a pointer to such a status block. NULL if not tracked. */ void *shadow; #endif }; /* * A region containing a mapping of a non-memory backed file under NOMMU * conditions. These are held in a global tree and are pinned by the VMAs that * map parts of them. */ struct vm_region { struct rb_node vm_rb; /* link in global region tree */ unsigned long vm_flags; /* VMA vm_flags */ unsigned long vm_start; /* start address of region */ unsigned long vm_end; /* region initialised to here */ unsigned long vm_top; /* region allocated to here */ unsigned long vm_pgoff; /* the offset in vm_file corresponding to vm_start */ struct file *vm_file; /* the backing file or NULL */ int vm_usage; /* region usage count (access under nommu_region_sem) */ bool vm_icache_flushed : 1; /* true if the icache has been flushed for * this region */ };
内存区段(bank)
一个内存bank表示一块连续内存区域,,一个bank一般对应处理器的一个RAM片选管脚(pin)上链接的RAM芯片内存空间。
现代处理器通常至少有4个RAM片选管脚,每个管脚所连RAM芯片在系统总的起始地址和大小可以通过寄存器设置。
对应内存bank的数据结构有struct meminfo:
struct meminfo { int nr_banks; struct membank bank[NR_BANKS]; };
内存节点(node)
内存节点是指由一个或者多个内存bank组成的内存集合,如果一个内存节点由多个内存bank组成,这些内存bank之间可以连续也可以不连续,即节点内可以存在内存孔洞。
在设置中
CONFIG_DISCONTIGMEM /* 未设置,则只有一个bank,即只有一个内存node,内存地址连续; 处理器地址寻址间每两个RAM的地址范围固定,若接入的RAM小于这个最大寻址范围,将出现内存孔洞。此时,若不设置CONFIG_DISCONTIGMEM,所有内存bank都属于内存节点0,如果设置了CONFIG_DISCONTIGMEM,将浪费一定的时间为孔洞内存创建struct page。 孔洞对系统启动后无影响,因为孔洞已经被mem_init()回收。 */
对应内存节点的重要数据结构有 struct pglist_data(pg_data_t):
typedef struct pglist_data { struct zone node_zones[MAX_NR_ZONES]; struct zonelist node_zonelists[MAX_ZONELISTS]; int nr_zones; #ifdef CONFIG_FLAT_NODE_MEM_MAP /* means !SPARSEMEM */ struct page *node_mem_map; #ifdef CONFIG_CGROUP_MEM_RES_CTLR struct page_cgroup *node_page_cgroup; #endif #endif #ifndef CONFIG_NO_BOOTMEM struct bootmem_data *bdata; #endif #ifdef CONFIG_MEMORY_HOTPLUG /* * Must be held any time you expect node_start_pfn, node_present_pages * or node_spanned_pages stay constant. Holding this will also * guarantee that any pfn_valid() stays that way. * * Nests above zone->lock and zone->size_seqlock. */ spinlock_t node_size_lock; #endif unsigned long node_start_pfn; unsigned long node_present_pages; /* total number of physical pages */ unsigned long node_spanned_pages; /* total size of physical page range, including holes */ int node_id; wait_queue_head_t kswapd_wait; struct task_struct *kswapd; int kswapd_max_order; } pg_data_t;
内存页区(zone)
内存页区是定义在内存节点(node)中的概念,每个内存节点可以分成3个内存页区,即
1、DMA页区(ZONE_DMA=0):可DMA操作
2、Normal页区(ZONE_NORMAL=1):禁止DMA操作
3、HighMem页区(ZONE_HIGHMEM=2):高端内存区域
对应内存页区的重要数据结构有struct zone(内容比较长,就不粘贴了~、~,上LXR自己找找呗)
空闲内存区域(free area)
空闲内存区域是内存页区内连续2^N页空闲内存组成的内存区域,其中N的范围在0~MAX_ORDER-1之间的整数,MAX_ORDER默认为11.
对应的数据结构有struct free_area:
struct free_area { struct list_head free_list[MIGRATE_TYPES]; unsigned long nr_free; };