linux内核linux 物理内存管理理有哪些

linux内存管理之内核对物理内存的描述 - CSDN博客
linux内存管理之内核对物理内存的描述
Linux将物理内存按固定大小的页面(一般为4K)划分内存,在内核初始化时,会建立一个全局struct page结构数组mem_map[ ]。如系统中有76G物理内存,则物理内存页面数为76*k/4K= 个页面,mem_map[ ]数组大小,即为数组中每个元素和物理内存页面一一对应,整个数组就代表着系统中的全部物理页面。 在服务器中,存在NUMA架构(如Nehalem、Romly等),Linux将NUMA中内存访问速度一致(如按照内存通道划分)的部分称为一个节点(Node),用struct
pglist_data数据结构表示,通常使用时用它的typedef定义pg_data_t。系统中的每个结点都通过pgdat_list链表pg_data_t-&node_next连接起来,该链接以NULL为结束标志。每个结点又进一步分为许多块,称为区域(zones)。区域表示内存中的一块范围。区域用struct zone_struct数据结构表示,它的typedef定义为zone_t。
每个区域(Zone)中有多个页面(Pages)组成。节点、区域、页面三者关系如下图。
1 节点(Node)
节点(Node),在linux中用struct pglist_data数据结构表示,通常使用时用它的typedef定义pg_data_t,数据结构定义在文件include/linux/mmzone.h中。当分配一个页面时,linux使用本地结点分配策略,从运行的CPU最近的一个结点分配。 因为进程倾向于在同一个CPU上运行,使用内存时也就更可能使用本结点的空间。对于象PC之类的UMA系统,仅有一个静态的pg_data_t结构,变量名为contig_page_data。遍历所有节点可以使用for_each_online_pgdat(pgdat)来实现。
2 区域(Zone)
节点(Node)下面可以有多个区域, 共有以下几种类型:
1)ZONE_DMA
它是低内存的一块区域,这块区域由标准工业架构(Industry Standard Architecture)设备使用,适合DMA内存。这部分区域大小和CPU架构有关,在x86架构中,该部分区域大小 限制为16MB。
2)ZONE_DMA32
该部分区域为适合支持32位地址总线的DMA内存空间。很显然,该部分仅在64位系统有效,在32位系统中,这部分区域为空。在x86-64架构中,这部分的区域范围为0~4GB。
3)ZONE_NORMAL
属于ZONE_NORMAL的内存被内核直接映射到线性地址。这部分区域仅表示可能存在这部分区域,如在64位系统中,若系统只有4GB物理内存,则所有的物理内存都属于 ZONE_DMA32,而ZONE_NORMAL区域为空。许多内核操作都仅在ZONE_NORMAL内存区域进行,所以这部分是系统性能关键的地 方。
4)ZONE_HIGHMEM
是系统中剩下的可用内存,但因为内核的地址空间有限,这部分内存不直接映射到内核。
在x86架构中内存有三种区域:ZONE_DMA,ONE_NORMAL,ZONE_HIGHMEM,不同类型的区域适合不同需要。在32位系统中结构中,1G(内核空间)/3G(用户空间) 地址空间划分时,三种类型的区域如下:
ZONE_DMA & & & & & & &内存开始的16MB
ZONE_NORMAL & & &&16MB~896MB
ZONE_HIGHMEM & &&896MB ~ 结束
4G(内核空间)/4G(用户空间)地址空间划分时,三种类型区域划分为:
ZONE_DMA & & & & & & &内存开始的16MB
ZONE_NORMAL & & &&16MB~3968MB
ZONE_HIGHMEM & &&3968MB ~ 结束
在64位Linux系统中,内存只有三个区域DMA、DMA32和NORMAL。
ZONE_DMA & & & & & & &内存开始的16MB
ZONE_DMA32 & & & & &16MB~4GB
ZONE_NORMAL & & & 4GB ~ 结束
下图是32位系统和64位系统对应的内存区域划分:
3 页面(Page)
系统内存由固定的块组成,称为页帧,每个页帧由struct page结构描述。内核在初始化时,会根据内存的大小计算出由多少页帧,每个页帧都会有一个page结构与之对应,这些 信息保存在全局数组变量mem_map中。mem_map通常存储在ZONE_NORMAL区域中, 在内存较小的机器中,会保存在加载内核镜像后的一片保留空间里。有多少个物理页面,就会有多个struct page结构,如系统安装128GB物理内存,struct page结构体大小为40字节,则mem_map[ ]数组就占用物理内存大小为128*k/4k
* 40 = 1280MB,即Linux内核要使用1280MB物理内存来保存mem_map[ ]数组,这部分内存是不可被使用的,因此 struct page结构体大小不能设计很大。
页面标志尤为重要,在内存分配与回收、I/O操作等重要内核活动过程中都会使用到页面标志。所有的标志在include/linux/page-flags.h中定义。下面解释一下重要的几个页面标志:
PG_locked:页面是否被锁住,若该位设置了该位,则不允许内核其他部分访问该页面。这用来防止内存管理过程中遇到的竞争条件,如当从硬盘读取数据到一个页面时,就不允许其他内核部分访问该页面,因为读数据的过程中,其他内核部分能访问的话,则读取到的数据是不完整的。
PG_error:I/O出错,且操作和页面有关,就设置该标志。
PG_referenced和PG_active:控制系统使用页面的活跃程度,这个信息对swap系统选择待交换出的页面非常重要。
PG_update:表示成功完成从块设备上读取一个页面的数据。该标志和块设备I/O操作有关。
PG_dirty:当内存页面中的数据和块设备上的数据不一致时,就设置该标志。在写数据到块设备时,为了提高将来的读性能,数据并不是立即回写到块设备上,而只是设置页面脏 标志,表示该页面数据需要回写。
PG_lru:该标志用来实现页面回收和交换。
PG_highmem:表示该页面为属于高端内存。
本文已收录于以下专栏:
相关文章推荐
我们知道命令hang,一般问题都出在内核里, 如果我们能根据自己的需要打印内核的信息,可能会事半功倍,很快定位到问题。幸运的是linux真的有提供这样的方式,就是通过SysRq,今天在这里我就介绍一下...
manpage类别整理
linux X86 64位内存布局图
几大开源公司的开源项目:
https://01.org/zh/projects
Linux内核采用页式存储管理,进程的地址空间被划分成固定大小的“页面”(page),物理内存同样被分为与页面大小相同的“页帧“(page frame),由MMU在运行时将虚拟地址“映射”成某个物理内...
XFS核心架构设计及实现的介绍
最近通过perf在分析Ceph对象存储RGW的性能,趁机也生成了一个Flame Graph,可以更直观的帮助我们找到RGW I/O各部分的耗时。先上图,生成的图本来是svg格式的(可以用浏览器打开,并...
他的最新文章
讲师:宋宝华
讲师:何宇健
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)115被浏览3569分享邀请回答/*
256 * Structure of
an inode on the disk
258 struct ext3_inode {
/* File mode */
/* Low 16 bits of Owner Uid */
/* Size in bytes */
/* Access time */
/* Creation time */
/* Modification time */
/* Deletion Time */
/* Low 16 bits of Group Id */
/* Links count */
/* Blocks count */
/* File flags */
i_block[EXT3_N_BLOCKS];/* Pointers to
/* File version (for NFS) */
第281行有一个“
i_block[];”最前面的le 全称为 little-endian,表示排序方式:低阶字节在高位位置与其对应的是be :big-endian。 如代码注释这个数据指向了具体存放文件数据的块(这里我们不讲间接块)。到此,估计已经了解了文件在磁盘上的存放的管理。
下面来说说文件在内存中的存放,文件在内存中存放由VSF层管理,VFS层在打开文件时在内存中依据磁盘的 ext3_inode而建立VFS的inode结构,VFS的inode与磁盘inode是一一对应的,下面摘出该结构的一些内容 (同前面的内核版本,文件在source/include/linux/fs.h):*/
527 struct inode {
unsigned short
unsigned int
struct timespec
struct timespec
struct timespec
spinlock_t
i_ /* i_blocks, i_bytes, maybe i_size */
unsigned short
unsigned int
struct file_lock
struct address_space
}是不是发现和磁盘上的inode结构存在相似的地方,但又多了一些内容? 这里与文件映射关系最为密切的是 594行的 struct address_spaces i_data ,该结构内容如下:(同前面的内核版本,文件在source/include/linux/fs.h):412 struct address_space {
struct inode
/* owner: inode, block_device */
struct radix_tree_root
/* radix tree of all pages */
spinlock_t
/* and lock protecting it */
unsigned int
i_mmap_/* count VM_SHARED mappings */
struct rb_root
/* tree of private and shared mappings */
struct list_head
i_mmap_/*list VM_NONLINEAR mappings */
429 } __attribute__((aligned(sizeof(long))));
需要关注的是414行的 radix_tree_root 结构,描述如下(source/include/linux/radix-tree.h):64 struct radix_tree_root {
unsigned i
struct radix_tree_node__rcu *
Height描述树的高/深度,*rnode指向了其子树,根树与子树的关系
:图中也描述了子树的结构的重要部分slots,slots指针数组指向了具体物理页的页描述符,如果基树树高为1,则slots可以指向[RADIX_TESS_MAP_SIZE]个页面,如下面的结构描述:(source/include/linux/radix-tree.h):50 struct radix_tree_node {
/* Height from the bottom */
struct radix_tree_node * /* Used when ascending tree */
struct rcu_headrcu_
/* Used when freeing node */
void __rcu
*slots[RADIX_TREE_MAP_SIZE];
unsigned long
tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS];
最后再来看看物理页描述符的结构(source/include/linux/mm_types.h,内容和注释太多,贴出来行号会乱掉,我只截了部分内容,且去掉了行号):struct page {
struct address_space *
/* slab first object */
pgoff_ /* Our offset within mapping. */
其中两个字段 mapping 和index, 如果该页被用于文件映射,则mapping用于指向文件inode的address_space结构,而index(页索引)呢?index是与文件inode-&address_space-&radix_tree_root-&radix_tree_node-&slots[]相关的,我直接载图好了:(且下面载图中最后说的图15-1就是前面已贴出的
根树与子树的关系图):总结起来就是:VFS从磁盘读取inode,并在内存立对应的vfs的inode,vfs层inode中address_space中radix_tree_root中radix_tree_node中slots指向了内存中存放文件的物理页描述符(找到页描述符就能找到页面)。 而页描述符中的
mapping指向了 inode的address_space, 同时,页描述符中index指定了页在slots中的具体位置 。2912 条评论分享收藏感谢收起64 条评论分享收藏感谢收起查看更多回答用户名:Kernel开发者
文章数:55
访问量:21003
注册日期:
阅读量:1297
阅读量:3317
阅读量:584279
阅读量:469603
51CTO推荐博文
0.12内核的内存管理比较简单粗暴,内核只用了一个页目录,只能映射4G的线性空间,所以每个进程的虚拟空间(逻辑空间)只能给到64M,最多64个进程;每个进程都有对应的任务号nr,当一个进程需要分配进程空间时,只需要nr乘以64M就可以得出该进程空间的线性起始地址。然后该进程的代码段、数据段描述符里面的基址字段会被设定为(nr x 64M),同时可以为进程分配页目录项和页目录表用以承载映射关系。之后如果进程要访问自己空间内的某个地址时就会首先用基地址与程序内32位偏移地址(逻辑地址)合成出线性地址,这个合成出来的线性地址一定在(段基址)~(段基址+段限长)之间,也就是(nr x 64M ~&nr x 64M+64M)之间。然后用这个线性地址遵循:“页目录项-页表-页表表项”这样的顺序找到对应的页表表项,也就找到了物理地址,就可以真正的存取数据了。但是在现代内核里,内存管理有很多不同。首先,线性地址空间不会改变,32位CPU可寻址4G线性空间。这个是唯一的。但是每个进程都有自己各自独立的4G虚拟空间,那么这是如何做到的呢?其实是每个进程给它一个自己的页目录,这样每个进程就能拥有4G的虚拟空间(逻辑空间)了。注意:0.12内核两个进程各自合成出的线性地址一定不相同,因为每个进程占据线性空间的不同区域。但是现代内核里,A、B两个进程可能合成出相同的线性地址,因为每个进程都有4G的虚拟空间,也就是说虚拟空间和线性空间对等了。但是由于两个进程的页目录和页表都不同,所以这两个进程会把各自合成出的数值相等的线性地址,映射到不同的物理地址。也就是两者线性空间到物理空间的映射是不同的。换句话说在现代内核里,虚拟空间(逻辑空间)和线性空间几乎成了一个概念,以下不作区分。举个例子,即使A和B进程同时访问各自线性空间的0x0804800地址处,分段分页地址变换机制也会把0x0804800这个线性地址映射到不同的物理地址上去。而这个过程,进程自己是看不到的,A和B都认为自己成功访问了0x0804800这个地址,但是实际上,他们访问的是各自线性空间里“数值相等的”线性地址,最终这两个“数值上相等”的线性地址将映射到不同的的物理内存地址处。这就实现了进程隔离。每个进程都具备4G的线性空间(虚拟空间),进程的线性空间之间相互隔离,互不干涉,每个进程都在自己的世界里干活。上面说的都是从操作系统原理的角度说的,放到具体的Linux操作系统上会有点不同。因为Linux内核里面规定,虽然每个进程各自拥有4G的线性空间,但是他们并不能随意使用这全部的4个G。0-3G是用户空间,的确是可以自由使用的,但是3G-4G之间内核空间,不能被随意使用。所以,上面的理论可以完善为,每个进程都有一个大小为4G的线性空间,这4G的线性空间分为两部分:大小为3G的用户空间,各进程在“特权级3级”下可以自由独立使用,各进程的这块空间是完全独立的互不影响的,是指向物理内存不同位置的。大小为1G的内核空间,各进程在“特权级0级”下才可以使用这块空间,各进程的这块空间不是独立的,是指向同一物理内存位置的。也就是说是所有进程共享的。本文出自 “” 博客,请务必保留此出处
了这篇文章
类别:┆阅读(0)┆评论(0)博客访问: 100760
博文数量: 20
博客积分: 317
博客等级: 二等列兵
技术积分: 809
注册时间:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: LINUX
随着用户程序的执行和结束,系统不断的为其进行分配与回收物理页面,这必然会产生大量的碎片,这些碎片被分成了两类:内部碎片与外部碎片。如下图:那么接下去我们要为程序分配一个3个连续的物理页面显然不可行了,虽然我们实际的物理内存中存在着3个物理页面这么大的空间,这些不连续的物理页面就成为了外部碎片。在linux系统中采用了伙伴算法来解决这个外部碎片的问题。在内核中有一个free_area这个结构体(include/linux/mmzone.h),它表示了内存中的空闲物理页面。结构体代码如下:struct free_area{&&&struct list_head
free_list[MIGRATE_TYPES];&&&unsigned long
nr_free;};对于伙伴算法中的伙伴是有条件的,即:1)、两个快的大小相同;2)、两个块的物理地址连续。下面我们结合上面这张图来说明该算法的工作原理。假设要求分配的块的大小为128个页面。该算法先在块大小为128个页面的链表中查找,看是否有这样一个空闲块。如果有,就直接分配;如果没有,该算法会查找下一个更大的块,具体地说,就是在块大小为256个页面的链表中查找一个空闲块。如果存在这个空闲块,内核就把256个页面分为两等份,一份分配出去,另外一份插入到块大小为128的链表中。如果在块大小为256的链表中也没有找到空闲页块,就继续找更大的块,即512个页面的块。如果存在内核就从512分出128个页面满足请求,然后从384个页面中取出256个页面插入到大小256的链表中。然后把剩余的128个页面插入到大小为128个页面的链表中。如果512也没找到,那就继续向上找,一直找不到则算法放弃分配,并发出出错信号。以上过程的逆过程就是块的释放过程。算法把满足伙伴条件的两个块合并为一个块,该算法是个迭代算法,如果合并后的块还可以跟相邻的块进行合并,那么该算法就继续合并。slab机制:伙伴算法也有不足,它不能解决内部碎片的问题,为了解决这个问题,内核又引入了slab机制,它主要是为了减少伙伴算法的调用次数。slab分配器为每个对象都建立了一个高速缓存,内核对这个对象的分配和释放都是在这个缓存中实现的。比如task_struct这个结构,在每次创建进程都会为这个结构体分配空间,在进程销毁后又回收这部分空间,那么内核就为其建立一个专用的缓冲区,而task_struct就是如上图所示的对象。每个高速缓冲区在内核中都是由kmem_cache这个结构表示的,每个slab都处于三种状态之一:满、部分满、空。下面是一个slab分配实例,就用刚才提高到了的task_struct这个结构举例:代码在kernel/fork.c中。首先,内核用一个全局变量存放指向task_struct高速缓冲区的指针:struct kmem_cache *task_struct_task_struct_cachep = kmen_cache("task_struct",sizeof(struct task_struct),& & & & & & & & & & & & & & & & & & & & & & & & & & & & & ARCH_MIN_TASKALIGN,& & & & & & & & & & & & & & & & & & & & & & & & & & & & & SLAB_PANIC|SLAB_NOTRACK,NULL);这样就创建了一个名为task_struct的高速缓存,存放的数据类型为struct task_struct的对象。在创建进程时,将会调用以下内容(dup_task_struct()中完成):struct task_struct *tsk = kmen_cache_alloc(task_struct_cachep,GFP_KERNEL);if(!tsk)& & return NULL;这些函数分配的都是专用缓冲区。但是当一个数据结构使用的不频繁,或者其大小不足一个页面时就没有必要给其分配专用缓冲区,而是通过kmalloc()或者数据结构大小接近一个页面时使用__get_free_pages()分配。这些分配的就是在通用缓冲区的分配。
阅读(2839) | 评论(0) | 转发(7) |
相关热门文章
给主人留下些什么吧!~~
请登录后评论。linux驱动开发--内核空间中内存的申请与释放
1、Linux内存管理
linux内存最小管理单位为页(page),通常一页为4kb。
linux系统中,在初始化时,内核为每个物理内存页建立一个page的管理结构,操作物理内存时实际上就是操作page页。
物理地址:出现在cpu地址总线上的寻址物理内存的地址信号,是地址变换的最终结果。
线性地址(虚拟地址):在32位cpu
1、Linux内存管理
linux内存最小管理单位为页(page),通常一页为4kb。
linux系统中,在初始化时,内核为每个物理内存页建立一个page的管理结构,操作物理内存时实际上就是操作page页。
物理地址:出现在cpu地址总线上的寻址物理内存的地址信号,是地址变换的最终结果。
线性地址(虚拟地址):在32位cpu架构上,可以表示4G的地址空间,也就是0xxFFFFFFFF。
逻辑地址:实际上是一个相对地址,是程序代码经过编译之后在汇编程序中出现的地址。
linux内核地址转换:出现在机器语言指令(程序编译后得到的二进制机器码序列)中的内存地址都是逻辑地址,逻辑地址必须被转换为线性地址,MMU将线性地址转换成物理地址,最终实现对应物理内存的访问。在linux系统中,逻辑地址和线性地址(虚拟地址)是一致的。
2、实例代码
*Copyright (c) 2013.TianYuan
*All rights reserved.
*文件名称: kernelspace.c
*文件标识: 内核空间中内存的申请与释放
*当前版本:1.0
*作者:wuyq
*取代版本:xxx
*原作者:xxx
*完成日期:
#include &linux/init.h&
#include &linux/module.h&
#include &linux/fs.h&
#include &linux/slab.h&
#include &linux/vmalloc.h&
MODULE_LICENSE("GPL");
#define PAGE_NUM 4/*编码幻数*/
unsigned char *kernelkmalloc = NULL;
unsigned char *kernelpagemem = NULL;
unsigned char *kernelvmalloc = NULL;
static int __init kernelspace_init(void)
int ret = -ENOMEM;
*GFP_KERNEL:分配内存,分配过程中可能导致睡眠。
*GFP_ATOMIC:分配过程中不会导致睡眠。
*GFP_DMA:申请到的内存通常情况下,位于0~16M之间
*__GFP_HIGNMEM:申请高端内存(物理地址896M以上的)
kernelkmalloc = (unsigned char *)kmalloc(100, GFP_KERNEL);
if(IS_ERR(kernelkmalloc)){
printk("kmalloc failed!\n");
ret = PTR_ERR(kernelkmalloc);
goto failure_
printk("kmalloc space :0x%lx!\n",(unsigned long)kernelkmalloc);
kernelpagemem = (unsigned char *)__get_free_pages(GFP_KERNEL, PAGE_NUM);/*请求或释放的页数的2的幂*/
if(IS_ERR(kernelpagemem)){
printk("get_free_pages failed!\n");
ret = PTR_ERR(kernelpagemem);
goto failure_get_free_
printk("get_free_pages address:0x%lx\n",(unsigned long)kernelpagemem);
kernelvmalloc = (unsigned char *)vmalloc();
if(IS_ERR(kernelvmalloc)){
printk("vmalloc failed!\n");
ret = PTR_ERR(kernelvmalloc);
goto failure_
printk("vmalloc address: 0x%lx\n",(unsigned long)kernelvmalloc);
failure_vmalloc:
free_pages((unsigned long)kernelpagemem, PAGE_NUM);
failure_get_free_pages:
kfree(kernelkmalloc);
failure_kmalloc:
static void __exit kernelspace_exit(void)
vfree(kernelvmalloc);
free_pages((unsigned long)kernelpagemem, PAGE_NUM);
kfree(kernelkmalloc);
module_init(kernelspace_init);
module_exit(kernelspace_exit);
KERNELDIR ?=/root/Desktop/work/ldd3/linux-2.6.31_TX2440A
PWD := $(shell pwd)
obj-m += kernelspace.o
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
@rm -f *.o *.ord* *.sy* *.mod.* *.ko
版权声明:本文内容由互联网用户自发贡献,本社区不拥有所有权,也不承担相关法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至: 进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
用云栖社区APP,舒服~
【云栖快讯】红轴机械键盘、无线鼠标等753个大奖,先到先得,云栖社区首届博主招募大赛9月21日-11月20日限时开启,为你再添一个高端技术交流场所&&
阿里云移动APP解决方案,助力开发者轻松应对移动app中随时可能出现的用户数量的爆发式增长、复杂的移动安全挑战等...
一种高性能、高可靠、可平滑扩容的分布式内存数据库服务。
操作审计(ActionTrail)会记录您的云账户资源操作,提供操作记录查询,并可以将记录文件保存到您指定的OS...
为您提供简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本...
阿里云双11狂欢,不只是5折
Loading...}

我要回帖

更多关于 linux内核内存管理 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信