虚拟内存及页、区、页框的分配及其释放

引言

        本文是本系列博客的第一篇,本系列博客涉及的主题有:内存管理、内核映射、伙伴系统、slab告诉缓存、内存池、页缓存、cache等,凡是跟内存相关的都会记录在这个系列里面。

一、内存层次结构

        内存是操作系统的宝贵资源,管理内存是操作系统管理的核心功能,对内存的动态管理关系到系统的性能。在32位机器上,Linux内存一共4GB,其中0-3GB是进程地址空间,3GB-4GB是内核空间。先来看下Linux系统中的内存层次结构

         最底层的是硬件部分,底层的硬件决定着软件如何编写的问题。因此,先来介绍下虚拟内存部分--内存管理器(MMU)。

虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。
现代所有用于一般应用的操作系统都对普通的应用程序使用虚拟内存技术,老一些的操作系统,如DOS和1980年代的Windows,或者那些1960年代的大型机,一般都没有虚拟内存的功能
——维基百科

        维基百科上说的很清楚了,虚拟内存可以将物理不连续的内存在逻辑上连续起来。这会引起一个问题-物理地址和逻辑地址的转换,二者并非一一对应。下图是物理地址和逻辑地址地址转换结构图。

 目录、页面、页内偏移三者合在一起叫做页表,用32位数据表示,一共可寻址4GB。

页表的每项含义: 页内偏移:0--11,页面:12--21,目录:22--31。

其寻址过程如下:

        1)操作系统(OS)从寄存器CR3获得当前页面目录指针(基地址);
        2)基地址+页面目录偏移->页面表指针(基地址);
        3)页面表指针+页面表偏移->内存页基址;
        4)内存页基址+页内偏移->具体物理内存单元。

        页号=逻辑地址/页面大小;页面偏移量=逻辑地址%页面大小 。页目录保存页表项的地址,页表项保存物理地址,最后1项保存4k页内偏移。

        页表是系统为每个进程建立的页面映像表。在地址空间内的所有页(0~n),依次在页表中有一页表项,记录了相应页在内存块中对应的物理块号。

其中(0,2)、(1,3)叫做页表项。页表项长度指每个页表项占多大的内存空间。

页表在内存中占用的大小=页表长度×页表项长度;

内存大小=页面长度×页面大小(物理块大小)

二、页框及其分配、释放

1、页和页框的区别

        页框(物理块):将内存空间分成一个个大小相等的分区(页框号或物理块号从0开始)。

        页(页面):将用户进程的地址空间为与页框大小相等的一个个区域(页号一般也从0开始)。

        页框是实际的物理内存,页是对地址空间的划分。页可以装进页框中,这些页框不一定连续(物理上),但页框连续(逻辑上)。

2、页框描述符

        内核把物理页作为内存管理的最基本单元,尽管我们使用内存是以字节为单位,但MMU以页(Page)为单位管理内存,页大小4KB(大部分32位处理器),也可以位8KB(64位,其实不同的体系规定不一样,64位的英特尔处理器也有支持页大小为4MB,本文以32位处理器,页4KB为主)。Linux使用4KB作为标准,基于以下原因:

1)有分页单元引发的缺页异常很容易得到解释,或者是由于请求的页存在但不允许进程对其访问,或者是由于请求的页不存在。在第二种情况下,内存分配器必须找到一个4KB的空闲页框,并将其分配给进程。

2)虽然4KB和4MB都是磁盘块大小的倍数,但绝大多数情况下,当主存和磁盘之间传输小块数据时更高效。

——《深入理解Linux内核》

        内核使用struct page结构体描述每个物理页,记录每个页框的状态,该结构体位于<linux/mm_types.h>中。所有的页描述符存放在mem_map数组中,virt_to_page(addr)宏产生线性地址addr对应的页描述符地址,pfn_to_page(pfn)宏产生与页框号pfn对应的页描述符地址。

        内核需要区分哪些是进程的页框、哪些属于内核,还要确定动态内存中的页框是否空闲。在以下情况下页框是不空闲:包含用户态进程的数据、某个软件高速缓存的数据、动态分配的内核数据结构、设备驱动程序缓冲的数据、内核模块的代码等。struct page的结构体如下:

struct page {
	unsigned long flags; //描述页的状态,包括页脏、是否被锁在内存中	,每一位描述一个状态,原子操作 	
						 //<linux/page-flags.h>存放这些标志
	/*
		下面的联合体表示page用于何处,如页缓冲区等等,
		看对应的英文即可
	*/
	union {
		struct {	/* Page cache and anonymous pages */
			struct list_head lru;  //LRU算法指针
			struct address_space *mapping; //页高速缓存、匿名页的反向映射用到
			pgoff_t index;		//在不同内核成分中有不同含义
			unsigned long private; //用于正在使用页的内核成分
		};
		struct {	/* page_pool used by netstack */
		};
		struct {	/* slab, slob and slub */
		};
		struct {	/* Tail pages of compound page */
		};
		struct {	/* Second tail page of compound page */
		};
		struct {	/* Page table pages */
			};
		};
		struct {	/* ZONE_DEVICE pages */
		};

	};
    atomic_t _mapcount;  //页框中的页表项数目,没有则为-1
    atomic_t _refcount;  //引用次数,-1表示未被引用,可以在新的分配中使用
	void *virtual;		//虚拟地址,若为高端内存,其为NULL	

} ;

        中间有些不太重要的给删去了。flag用来描述页的状态,包括页是不是脏的、是不是被锁在内存,其每一位表示一个状态,标志定义在<linux/page-flags.h>中。_count存放页的引用计数,内核一般用page_count()函数判断是否有可用页。virtual是页的虚拟内存,高端动态内存描述该段再详细说明。page结构与物理页相关,而非虚拟页有关,内核仅仅用这个数据结构描述物理内存本身,而非包含在其中的数据。

 描述页框状态的标志

3、非一致内存访问

        我们希望内存是一种均匀、共享的资源,但由于CPU和内存之间传输速度的巨大差异,我们不得不在二者之间添加各种高速缓存(各种cache)来满足CPU的访问。Linux支持非一致内存访问(Non-Uniform Memory Access,NUMA)模型,在这种模型中,给定CPU对不同内存单元的访问时间可能不一样。系统的物理内存被划分为几个节点(node)。在一个单独的节点中,任一给定CPU访问页面所需的时间都是相同的。然而,对不同的CPU,这个时间不一定成立。对每个CPU而言,内核都试图把耗时节点的访问次数减到最少。

        每个节点的物理内存可以划分为几个管理区(Zone),每个节点都有一个类型为pg_data_t的描述符,它的第一个元素有pgdat_list变量指向,其结构体如下。

类型        参数说明
  struct zone[]  node_zones节点中管理区描述符的数组
  struct zonelist[]  node_zonelists页分配器使用的  node_zonelists数据结构的数组
int                nr_zones节点中管理区的个数
struct page*node_mem_map节点中页描述符的数组
struct bootmem_data*bdata用在内核初始化阶段
unsigned longnode_start_pfn节点中第一个页框的下标
unsigned longnode_present_pages内存结点的大小(页框为单位)
unsigned longnode_spanned_pages节点的大小(页框为单位)
intnode_id节点标识符
pg_data_t *pgdat_next内存节点链表中的下一项
wait_queue_head_tkswapd_waitkswapd也患处守护进程使用的等待队列
struct task_struct*kswapd指针指向kswapd内核线程的进程描述符
intkswapd_max_orderkswapd将要创建的空闲块大小取对数的值

4、内存管理区

        理想的模型中,一个页框就是一个内存储存单元,但实际上,受到硬件的约束,限制了页框的使用方式。在x86上,Linux必须两种硬件约束:

1)ISA总线的直接内存存取(DMA)处理器有一个严格的限制:只能访问RAM的前16MB。

2)在具有大容量的RAM中,CPU不能访问所有的物理内存(线性空间太小),一些内存无法永久的映射到内核空间上。

因此,Linux将内存划分为3个管理区(Zone),在x86中,管理区为:

ZONE_DMA:包含低于16MB的内存页框,用于DMA操作

ZONE_NORMAL:包含高于16MB、低于896MB的内存页框,正常映射的页

ZONE_HIGHMEM:包含高于896MB的内存页框,这些页并不能永久映射到内核空间。

这些划分和具体的体系有关,有的体系中,ZONE_DMA为空,ZONE_NORMAL就可直接用于分配。

        Linux将系统的页划分为区,形成不同的内存池,可以根据用途对其进行分配。区的划分没有任何物理意义,仅仅是为了管理页而进行的逻辑上的划分。有些特殊用途的页必须从特地区域分配,但也可以从其他区分配。如一般用途的内存既可以从ZONEZ-NORMAL中分配,也可以从ZONE_DMA中分配(ZONEZ-NORMAL资源不够时)。

管理区描述符的字段 

        当内核调用一个内存分配函数时,必须指明请求页框所在的管理区。为了在内存分配中指定首选管理区,内核使用zonelist数据结构管理区描述符指针数组。

5、分区页框分配器

        分区页框分配器(zoned page frame allocator)处理对连续页框组的内存分配请求,组成如图。

        管理区分配器接受动态内存的分配和释放。在请求分配的情况下,其搜索一个能满足要求的一组连续页框内存的管理区。在每个管理区内,使用伙伴算法来处理,为了更好的性能,一部分页框保留在高速缓存中用于快速地满足对单个页框的分配请求(在后面介绍伙伴系统中介绍该部分)。

6、请求页

        内核提供一组接口用于请求页框,以页为单位分配内存,接口在<linux/gfp.h>,分别介绍如下。

接口名        功能
struct page* alloc_pages(gfp_t gfp_mask, unsigned int order);分配2order(1 << order)个连续物理页,并返回一个指针,指向第一个物理页的page结构体,错误返回NULL。
void* page_address(struct page*  page)转化为逻辑地址,指定当前物理页的逻辑地址。
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order);该函数作用与alloc_pages函数和相同,返回第一个物理页的逻辑地址。
struct page* alloc_page(gfp_t gfp_mask);分配一页内存,相当于order参数为0。
unsigned long __get_free_page(gfp_t gfp_mask);分配一页内存,相当于order参数为0。
unsigned long get_zeroed_page(gfp_t gfp_mask)返回页的内容为0,该函数与__get_free_pages函数一样,只不过返回的内容被填充为0;分配的页返回给用户空间时非常有用。

请求页框用到的标志。

在实际中,Linux使用预定义标志值的组合,这些才是常用的标志。

什么时候用哪种标志位呢?这里进行以下总结。

标志描述
GFP_AROMIC可以用在中断处理程序、下半部、持有自旋锁以及其他不能睡眠的地方
GFP_NOWAIT与GFP_AROMIC类似,不同之处在于调用故不会退给紧急内存池,这增加了内存分配失败的可能性。
GFP_NOIO可以阻塞,但不会启动磁盘IO。
GFP_NOFS在必要时可能引起阻塞,也可能启动磁盘IO,但不会启动文件系统操作。
GFP_KERNEL常规的分配方式,可能会阻塞,在睡眠安全时用在进程上下文代码中。为了分配内存,内核尽力而为,该标志时首选标志。
GFP_USER常规分配内存方式,可能会引起阻塞,用于为用户空间进程分配内存时。
GFP_HIGHUSER从ZONE_HIGHMEM分配内存,可能回族赛,用于为用户空间进程分配内存时。
GFP_DMA从ZONE_DMA分配内存,通常与其他标志一起使用。

大部分情况下,要么用GFP_KERNEL,要么用GFP_AROMIC。

GFP_KERNEL:进程上下文,可以休眠

GFP_AROMIC:进程上下文,不可以休眠;中断处理程序;软中断;tasklet;

在睡眠之前使用GFP_AROMIC:或者在睡眠以后使用GFP_KERNEL执行内存分配内存。

在需要用到DMA时且可以休眠:GFP_DMA  |  GFP_KERNEL

在需要用到DMA时且不可以休眠:GFP_DMA  |  GFP_AROMIC,或者在睡眠之前执行内存分配。

GFP_NOIO分配不会启动任何磁盘IO来帮助满足请求,GFP_NOFS可能会起到磁盘IO,但绝不会启动系统IO,二者分别用在某些低级块IO或者文件系统的代码中。

7、释放页

释放页相关接口如下,均用于释放页框。

接口名        功能
void __free_page(struct page* page, unsigned  int  order);释放page对应的页框,一共释放2^order个连续页框。
void  free_pages(unsigned long adrr, unsigned int order);参数为要释放的第一个页框的线性地址addr
void  free_page(unsigned long addr)释放地址为addr的页框

三、总结

        内存管理是操作系统的一个核心部分,其牵扯到硬件相关东西,内存管理的好坏直接影响系统性能。抛开这些不谈,对内存的掌握也是技术大牛的必备了。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
服务器运行报告模板 LT " " " " "虎门港7、8号泊位硬件标 " "服务器运行测试报告 " " " " " "2011-11-7 " " " [本次测试内容主要包括服务器硬件系统、操作系统、应用软件系统等性能测试] 1.设备信息 "设备 " " "硬件配置信息 " "机型号 " " "CPU " " "内存 " " "硬盘 " " "系统信息 " "操作系统 " " "IP " " "主机名 " " 3、操作系统及应用系统检查 "检查项 "检查操作 "参考标准 "运行状况 " "操作系统启动和运 "加电启动 " " 正常 不正常 " "行状况检查 " " " " "检查系统内存利用 "通过windows操作系统"任 "检测三次,每次5分" 正常 不正常 " "率 "务管理器" "钟,记录大约平均 " " " " "的利用率 " " "检查系统CPU利用率"通过windows操作系统"任 "检测三次,每次5分" 正常 不正常 " " "务管理器" "钟,记录大约平均 " " " " "的利用率 " " "操作系统版本检查 "执行命令winver.exe " " 正常 不正常 " "主机连接系统网络 "在其它机器上采用ping命 "观察5分钟是否有丢" 正常 不正常 " "情况 "令 "包情况 " " "主机网络配置情况 "执行命令ipconfig /all "IP地址、子网掩码 " 正常 不正常 " " " "正确 " " "系统账户检查 "利用administrator身份、"能够正常登陆到系 " 正常 不正常 " " "口令登陆 "统 " " "应用程序启动和运 "应用使用测试 " " 正常 不正常 " "行情况 " " " " 4、检查记录 (1)内存、cpu使用情况巡检 用命令taskmgr.exe打开任务管理器 检查方法: Windows下使用任务管理器,记录占用内存、cpu最多的前五位进程或应用程序; 性能 CPU使用情况:表明处理器工作时间百分比的图表,该计数器是处理器活动的主要指示器 ,查看该图表可以知道当前使用的处理时间是多少。 CPU使用记录:显示处理器的使用程序随时间的变化情况的图表,图表中显示的采样情况 取决于"查看"菜单中所选择的"更新速度"设置值,"高"表示每秒2次,"正常"表示每两秒 1次,"低"表示每四秒1次,"暂停"表示不自动更新。 PF使用情况:正被系统使用的面文件的量。 面文件使用记录:显示面文件的量随时间的变化情况的图表,图表中显示的采样情 况取决于"查看"菜单中所选择的"更新速度"设置值。 总数:显示计算机上正在运行的句柄、线程、进程的总数。 认可用量:分配给程序和操作系统的内存,由于虚拟内存的存在,"峰值"可以超过最大 物理内存,"总数"值则与"面文件使用记录"图表中显示的值相同。 物理内存:计算机上安装的总物理内存,也称RAM,"可用"表示可供使用的内存容量," 系统缓存"显示当前用于映射打开文件的面的物理内存。 内核内存:操作系统内核和设备驱动程序所使用的内存,"面"是可以复制到面文件 中的内存,由此可以释放物理内存;"非分"是保留在物理内存中的内存,不会被复制 到面文件中。 (2)硬盘使用情况, 检查方法:Windows下通过查看'我的电脑'—'管理'—'磁盘管理'可以查看磁盘分与 对应分使用情况, A、要定期清理磁盘垃圾文件,选择分'属性'—'常规'—'磁盘清理'。 B、定期检查磁盘的错误信息,选择分'属性'—'工具'—'开始检查'。 C、定期整理磁盘碎片,选择分'属性'—'工具'—'开始整理'。 (3)系统信息检查,显示关于计算机及其操作系统的详细配置信息,包括操作系统 配置、安全信息、产品 ID 和硬件属性,如 RAM、磁盘空间和网卡,系统运行时间。 (4)系统端口检查 查看开启了哪些端口,显示所有连接和监听端口。所接开启端口的服务。 "服务器整体巡检结果 " "一.硬件配置符合合同要求 符合 不符合 " "二.硬件系统运行稳定 稳定 不稳定 " "三.操作系统运行稳定 稳定 不稳定 " "四.应用软件运行稳定 稳定 不稳定 " "记录人签字: "业主签字: " ----------------------- 服务器运行报告模板(1)全文共13,当前为第1。 服务器运行报告模板(1)全文共13,当前为第2。 服务器运行报告模板(1)全文共13,当前为第3。 服务器运行报告模板(1)全文共13,当前为第4。 服务器运行报告模板(1)全文共13,当前为第5。 服务器运行报告模板(1)全文共13,当前为第6。 服务器运行报告模板(1)全文共13,当前为第7。 服务器运行报告模板(1)全文共13,当前为第8。 服务
《操作系统原理》实验指导书 实验一 生产者-消费者模型模拟进程调度 一、实验任务 1、在WINDOWS 2000环境下,创建一个控制台进程,此进程包括4个线程:2个生产者线程和2个消费者线程。 2、用信号量机制解决进程(线程)的同步与互斥问题。 二、实验目的 1.掌握基本的同步互斥算法,理解生产者和消费者模型。 2.了解Windows 2000/XP中多线程的并发执行机制,线程间的同步和互斥。 3.学习使用Windows 2000/XP中基本的同步对象,掌握相应的API。 三、实验要求 1.生产者消费者对缓冲进行互斥操作。 2.缓冲大小为10,缓冲满则不允许生产者生产数据,缓冲空则不允许消费者消费数据。 3.生产者消费者各循环操作50次。 四、设计思路和采取的方案 1.利用windows提供的API函数CreateSemaphore()创建信号量对象; CreateThread()创建线程; WaitForSingleObject()执行P操作; ReleaseSemaphore()执行V操作; WaitForMultipleObjects()主进程等待线程的结束等函数进行设计。 2.在Windows中,常见的同步对象有:信号量(Semaphore)、互斥量(Mutex) 。 使用这些对象都分为三个步骤,一是创建或者初始化; 接着请求该同步对象,随即进入临界,这一步对应于互斥量的上锁;最后释放该同步对象,这对应于互斥量的解锁。这些同步对象在主进程中创建,在其子线程中都可。 实验二 存储管理 一、目的和要求 1. 实验目的 (1)掌握时间片轮换的进程调度算法; (2)掌握带优先级的进程调度算法; (3)选用面向对象的编程方法。 2、实验学时:2学时 3、实验要求 (1)自定义PCB的数据结构; (2)使用带优先级的时间片轮转法调度进程,每运行一个时间片,优先级减半。 (3)命令集 A)create 随机创建进程,进程的优先级与所需要的时间片随机决定; B)ps 查看当前进程状态 C)sleep 命令将进程挂起 D)kill 命令杀死进程 E)quit命令退出 二、实验内容 根据教师指定的实验课题,完成设计、编码、测试工作。 实验三 虚拟存储器 一、目的和要求 1. 实验目的 (1)掌握先进先出面置换算法; (2)掌握随机替换面置换算法; (3)掌握OPT面置换算法; (4)掌握最近最少使用面置换算法; (5)熟悉抖动现象及其产生原理; (6)熟悉C/C++编程。 2、实验学时:2学时 3、实验要求 (1)进程占用内存空间共640K,面大小是1K/2K/4K/8K; (2)随机生成256个面置换次序; (3)用于分配面大小的内存总空间是32K; (4)给出四种面置换算法的换过程,并计算各自的缺率。 二、实验内容 编写程序,使用四种不同的面替换策略算法进行面替换。分别是先进先出,随机替换,时钟面替换,最近最久未使用面替换,并计算缺率。
虚拟内存是一种计算机内存管理技术,它允许程序使用比实际可用物理内存更大的内存空间。虚拟内存通过将内存分为固定大小的面来实现,每个面可以被映射到物理内存中的一个面帧或者磁盘上的一个面。当程序需要访问一个不在物理内存中的面时,操作系统会将该面从磁盘中读取到物理内存中,如果物理内存中没有足够的空间,则需要使用面置换算法将一个已经在物理内存中的面替换出去。 常见的面置换算法有以下几种: 1.最优面置换算法(OPT):选择在未来最长时间内不再被访问的面进行置换,但是由于无法预测未来的访问模式,因此该算法无法实现。 2.先进先出面置换算法(FIFO):选择最早进入物理内存的面进行置换,但是该算法可能会将最常用的面替换出去,导致缺率增加。 3.最近最少使用面置换算法(LRU):选择最近最少被访问的面进行置换,该算法可以较好地模拟人类的访问模式,但是实现起来比较复杂。 4.时钟面置换算法(Clock):将物理内存中的面组织成一个环形链表,每个面都有一个访问位,当面被访问时,访问位被设置为1,当需要置换面时,从当前位置开始扫描环形链表,如果访问位为0,则选择该面进行置换,否则将访问位设置为0并继续扫描。 5.最不经常使用面置换算法(NFU):选择最不经常被访问的面进行置换,每个面有一个计数器,每次被访问时计数器加1,当需要置换面时,选择计数器最小的面进行置换。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

紫川宁520

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值