页
内存管理是运行在计算机上的应用程序通过软硬件协作来访问内存的一种方法。
内存管理子系统的职责为:进程请求内存时分配可用内存,进程释放内存后回收内存,以及跟踪系统中内存的使用状况。
操作系统的生命周期分为两个阶段:正常执行阶段和自举阶段;自举阶段使用临时内存,而正常运行阶段使用的内存有两种情况:一种是有一部分固定的内存分配给内核代码和数据,另一种是动态内存请求分配内存。
虚拟内存依靠透明的使用磁盘空间,得以使程序运行起来好像他们使用比系统物理内存更多的内存空间。
在使用虚拟内存时,程序数据被分割成基本单位,这些单元可以在磁盘与内存键来回移动。这些数据单位称为页
当程序从内存中取数据时,会从虚拟地址空间中之处需要访问的内存位置,每个进程都有自己独立的虚拟地址范围,这样做的好处防止非法读取或写覆盖其他进程的数据
内存管理器在OS中负责维护虚拟地址和物理地址之间的关系,并且实现分页机制。页是基本的内存单元,MMU是完成实际的地址转换工作的硬件工作。
linux页的数据结构:
struct page{
unsigned long flags;
atomic_t count;
struct list_head list;
struct address_space * mapping;
unsigned long index;
struct list_head lru;
union {
struct pte_chain *chain;
pte_add_r direct;
}pte;
void *vitrual;
.....
各个成员详解:
count:相当于计数器,统计一个页使用或引用的次数,数值为0表示页是可重用的;证书表示访问该页数据的进程数
list:是list_head结构,结构中的next和prev指针指向双向链表中的对应元素
mapping: 指向每个页关联的address_space结构的指针
lru:存放的next和prev指针指向最近最少使用链表中的相应元素,这些链表用于页面护手处理
virtual:是一个指向页所对应虚拟地址的指针
内存管理区
内存管理区是由页面(或叫物理页)组成的,在linux存在三个内存管理区,ZONE_DMA(用于DMA的页面),ZONE_NORMAL(具有虚拟映射的非DMA页),ZONE_HIGHMEM(这些页的地址不在虚拟地址控件中)
spinlock_t lock; //存放的是自旋锁便是保护描述符的,该锁保护的是内存管理区描述符本身
unsigned int compact_considered;
unsigned long free_pages; //存放内存管理区中剩余的空闲页个数
unsigned int compact_defer_shift;
int compact_order_failed;
unsigned long wait_table_hash_nr_entries; //与内存管理区上页的进程等待队列相关
unsigned long wait_table_bits;...
细节:缓冲对齐和区填充:缓冲对齐是为了提高对描述符中字段的访问性能,缓冲对齐通过减少复制一组数据所需的指令数量来提高性能。
页面
页面是存放页的基本内存单位,只要进程请求内存,内核便会请求一个页面给他;当页面不再使用,那么内核便将其释放。
请求页面的函数
1 alloc_pages()和alloc_page()
alloc_page()用于请求单页,不需要说明内存的大写,__alloc_pages()是真正的处理页面请求的函数
宏__free_page()和free_pagge()用于释放单页面,在调用页面释放函数时,将需释放的页面数量参数置为0
#define __free_page(page) __free_pages((page), 0)
#define free_page(addr) free_pages((addr), 0)
伙伴系统
每当页面被分配和回收时,系统都会遇到名为外部碎片的内存碎片问题。这是由于可用页面散布与整个内存空间中,即使系统可用页面总数足够多,但也无法分配大块连续页面。Linux使用伙伴系统的内存管理算法实现来达到该目的。
伙伴系统把内存中的空闲块组成链表,每个链表都指向不同大小的内存快,大小是2的幂次方。当分配的块释放时,伙伴系统搜索与所释放块大小相等的可用空闲内存快,如果找到相邻的空闲块,那么就将他们合并成两倍与自身大小的一个新内存快
进程映像的分布
当用户空间被载入内存后,它就用有自己的线性地址空间,该空间被划分成各种内存区或叫段。与进程相关的主要段有6个:
text:该段页称作代码段,存放程序的可执行指令。mm_struct中的start_code和end_code字段保存了text段的其实位置和结束位置
Data:该段存放所有已初始化的数据,初始化数据包含静态分配的数据和初始化的全局数据
gvar:全局变量,被初始化存放在数据段中,该段具有可读,可写属性。mm_struct结构的start_data和end_start字段存放数据段的起始地址和结束地址
BSS:该段存放为初始化数据。这些数据包括程序执行是被系统初始化成0的全局变量,该段的另一个名字叫做初始化为0的数据段
堆:malloc分配的线性地址空间,系统调用sys_brk()和brk指针移动到新位置,从而扩展了堆空间
栈:包含所有已分配内存的局部变量。mm_struct结构中的start_stack字段标记了进程栈的起始位置
他们映射到地址空间的3个内存区:text data stack; data段包括 data段 bss和堆
页表
程序内存的有效管理是通过虚拟地址完成的。虚拟地址和对应的物理地址之间的管理便要靠内核中页表来维护。
Linux中使用3级页表的分页机制:顶层目录(pdg_t), PMD(p d_t),PTE(pte_t)
PGD存放的项指向PMD,PMD存放的相指向PTE,PTE存放的项指向特定页面,每个进程都有自己的页表,mm_struct->pgd指向进程的PGD