一、内存初始化
1、内存的初始化一般由BIOS或boot loader完成,由bios或boot loader将内存的大小传递给linux内核
2、在arm设备中,设备由DTS(Device Tree Source)描述,安卓中也是如此
3、在内核启动过程中,需要解析dts文件,其中使用early_init_dt_scan_memory()函数来解析内存信息,解析内存相关的信息得到基地址(base address)和大小(size),最后通过memblock_add()函数添加到memblock中
4、memblock是一种早期内存分配器,用于在内核启动初期进行简单的内存管理
5、在内存映射时,需要首先清理页表(prepare_page_table()->pmd_clear()),然后创建新的页表(map_lowmem()->create_mapping()),完成初始化
6、上述的初始化过程(物理内存映射)会从内存开始处(dts中)覆盖至arm_lowmem_limit处
7、紧接着会进行zone初始化,主要在bootmem_init函数中完成
8、zone初始化完成后,内核会进行空间划分,通常用户空间和内核空间按照3:1划分
二、物理页面分配
1、内核使用alloc_pages()函数来分配一个或多个了i按需的物理页面,但分配的页面只能是2的整数幂次个
2、
三、内存管理
1、内存节点的概念是针对大型机器(多处理器机器)而提出的,单处理器机器中也有这个机制(硬件兼容性),只不过只使用一个节点来管理内存
2、在NUMA模式下,CPU被划分成多个节点,根据处理器访问内存的代价(距离)不同,将不同的内存簇指派给不同的处理器,这样的内存簇被认为是一个内存节点
3、节点被分为许多zone,由zone结构体描述,在安卓使用的Linux内核中,定义了ZONE_DMA,ZONE_DMA32,ZONE_HIGHMEM,ZONE_NORMAL
4、节点、zone、页关系如下图

5、zone结构体字段及作用如下(新版源码中还包含有其他字段)
| 字段 | 描述 |
|---|---|
| watermark | 每个 zone 在系统启动时会计算出 3 个水位值, 分别为 WMAKR_MIN, WMARK_LOW, WMARK_HIGH 水位, 这在页面分配器和 kswapd 页面回收中会用到 |
| lowmem_reserve[MAX_NR_ZONES] | zone 中预留的内存, 为了防止一些代码必须运行在低地址区域,所以事先保留一些低地址区域的内存 |
| pageset | page管理的数据结构对象,内部有一个page的列表(list)来管理。每个CPU维护一个page list,避免自旋锁的冲突。这个数组的大小和NR_CPUS(CPU的数量)有关,这个值是编译的时候确定的 |
| lock | 对zone并发访问的保护的自旋锁 |
| free_area[MAX_ORDER] | 页面使用状态的信息,以每个bit标识对应的page是否可以分配 |
| lru_lock | LRU(最近最少使用算法)的自旋锁 |
| wait_table | 待一个page释放的等待队列哈希表。它会被wait_on_page(),unlock_page()函数使用. 用哈希表,而不用一个等待队列的原因,防止进程长期等待资源 |
| wait_table_hash_nr_entries | 哈希表中的等待队列的数量 |
| zone_pgdat | 指向这个zone所在的pglist_data对象 |
| zone_start_pfn | 和node_start_pfn的含义一样。这个成员是用于表示zone中的开始那个page在物理内存中的位置的present_pages, spanned_pages: 和node中的类似的成员含义一样 |
| name | zone的名字,字符串表示: “DMA”,“Normal” 和“HighMem” |
| totalreserve_pages | 每个区域保留的不能被用户空间分配的页面数目 |
| ZONE_PADDING | 由于自旋锁频繁的被使用,因此为了性能上的考虑,将某些成员对齐到cache line中,有助于提高执行的性能。使用这个宏,可以确定zone->lock,zone->lru_lock,zone->pageset这些成员使用不同的cache line. |
| managed_pages | zone 中被伙伴系统管理的页面数量 |
| spanned_pages | zone 中包含的页面数量 |
| present_pages | zone 中实际管理的页面数量. 对一些体系结构来说, 其值和 spanned_pages 相等 |
| lruvec | LRU 链表集合 |
| vm_stat | zone 计数 |
6、zone的初始化在bootmem_init函数中完成
7、find_limit函数中会计算出min_low_pfn、max_low_pfn、max_pfn,其中min_low_pfn是内存块开始地址的页帧号,max_low_pfn是normal区结束的区帧号(这个值由arm_lowmem_init得来),max_pfn是内存块结束的页帧号
Q&A
1、 数据删除的原理是什么,如何将数据删除干净?
美国国防部在《国家工业安全程序操作手册》中对硬盘数据清除作了专门的描述。该手册建议对所要清除的数据区进行三次覆盖,第一次使用一个8位的字符,第二次使用该字符的互补字符,最后再使用随机字符进行覆盖。保存有极端重要数据的硬盘应该被永久消磁或者彻底销毁。
对于数据删除,本质上讲是将想要删除的数据标所对应空间标记为空闲,让对应空间可以重新储存数据,但是原先的数据仍旧存在。
对于Linux来说,删除文件的行为是将inode结构体中的i_nlink字段(硬链接数目)值减少,直到i_nlink 和 i_count 均为 0 时,文件被删除,也就是说文件名到inode的链接删除了,磁盘上的数据块并未被删除
2、数据恢复的方法和原理都是什么
在Linux下恢复数据,ext3和ext4文件系统中的元数据在日志文件中的备份来进行恢复,但日志文件空间优先且是循环使用的,所以经历大量读写后可能无法恢复。




