linux内存管理基本概念

news2023/12/5 7:19:21

1. Linux物理内存三级架构

 对于内存管理,Linux采用了与具体体系架构不相关的设计模型,实现了良好的可伸缩性。它主要由内存节点node、内存区域zone和物理页框page三级架构组成。

    • 内存节点node

       内存节点node是计算机系统中对物理内存的一种描述方法,一个总线主设备访问位于同一个节点中的任意内存单元所花的代价相同,而访问任意两个不同节点中的内存单元所花的代价不同。在一致存储结构(Uniform Memory Architecture,简称UMA)计算机系统中只有一个节点,而在非一致性存储结构(NUMA)计算机系统中有多个节点。Linux内核中使用数据结构pg_data_t来表示内存节点node。如常用的ARM架构为UMA架构。

    •  内存区域zone

       内存区域位于同一个内存节点之内,由于各种原因它们的用途和使用方法并不一样。如基于IA32体系结构的个人计算机系统中,由于历史原因使得ISA设备只能使用最低16MB来进行DMA传输。又如,由于Linux内核采用

    •  物理页框page

 

2. Linux虚拟内存三级页表

      Linux虚拟内存三级管理由以下三级组成:

     • PGD: Page Global Directory (页目录)

     • PMD: Page Middle Directory (页目录)

     • PTE:  Page Table Entry  (页表项)

     每一级有以下三个关键描述宏:

     • SHIFT

     • SIZE

     • MASK

        如页的对应描述为:

/* PAGE_SHIFT determines the page size  asm/page.h */
#define PAGE_SHIFT		12
#define PAGE_SIZE		(_AC(1,UL) << PAGE_SHIFT)
#define PAGE_MASK		(~(PAGE_SIZE-1))

    数据结构定义如下:

/* asm/page.h */
typedef unsigned long pteval_t;
 
typedef pteval_t pte_t;
typedef unsigned long pmd_t;
typedef unsigned long pgd_t[2];
typedef unsigned long pgprot_t;
 
#define pte_val(x)      (x)
#define pmd_val(x)      (x)
#define pgd_val(x)	((x)[0])
#define pgprot_val(x)   (x)
 
#define __pte(x)        (x)
#define __pmd(x)        (x)
#define __pgprot(x)     (x)

 

2.1 Page Directory (PGD and PMD)

     每个进程有它自己的PGD( Page Global Directory),它是一个物理页,并包含一个pgd_t数组。其定义见<asm/page.h>。 进程的pgd_t数据见 task_struct -> mm_struct -> pgd_t * pgd;    

     ARM架构的PGD和PMD的定义如下<arch/arm/include/asm/pgtable.h>:

#define PTRS_PER_PTE  512 // PTE中可包含的指针<u32>数 (21-12=9bit) 
#define PTRS_PER_PMD  1 
#define PTRS_PER_PGD  2048 // PGD中可包含的指针<u32>数 (32-21=11bit)

#define PTE_HWTABLE_PTRS (PTRS_PER_PTE) 
#define PTE_HWTABLE_OFF  (PTE_HWTABLE_PTRS * sizeof(pte_t)) 
#define PTE_HWTABLE_SIZE (PTRS_PER_PTE * sizeof(u32))

/*  * PMD_SHIFT determines the size of the area a second-level page table can map  * PGDIR_SHIFT determines what a third-level page table entry can map  */ 
#define PMD_SHIFT  21 
#define PGDIR_SHIFT  21

虚拟地址SHIFT宏图:

 

     虚拟地址MASK和SIZE宏图:

 

 2.2 Page Table Entry

      PTEs, PMDs和PGDs分别由pte_t, pmd_t 和pgd_t来描述。为了存储保护位,pgprot_t被定义,它拥有相关的flags并经常被存储在page table entry低位(lower bits),其具体的存储方式依赖于CPU架构。

     每个pte_t指向一个物理页的地址,并且所有的地址都是页对齐的。因此在32位地址中有PAGE_SHIFT(12)位是空闲的,它可以为PTE的状态位。

     PTE的保护和状态位如下图所示:

 

2.3 如何通过3级页表访问物理内存

       为了通过PGD、PMD和PTE访问物理内存,其相关宏在asm/pgtable.h中定义。

       • pgd_offset 

       根据当前虚拟地址和当前进程的mm_struct获取pgd项的宏定义如下: 

/* to find an entry in a page-table-directory */
#define pgd_index(addr)		((addr) >> PGDIR_SHIFT)  //获得在pgd表中的索引
 
#define pgd_offset(mm, addr)	((mm)->pgd + pgd_index(addr)) //获得pmd表的起始地址
 
/* to find an entry in a kernel page-table-directory */
#define pgd_offset_k(addr)	pgd_offset(&init_mm, addr)

         • pmd_offset
             根据通过pgd_offset获取的pgd 项和虚拟地址,获取相关的pmd项(即pte表的起始地址) 

/* Find an entry in the second-level page table.. */
#define pmd_offset(dir, addr)	((pmd_t *)(dir))   //即为pgd项的值

        • pte_offset

      根据通过pmd_offset获取的pmd项和虚拟地址,获取相关的pte项(即物理页的起始地址)

#ifndef CONFIG_HIGHPTE
#define __pte_map(pmd)		pmd_page_vaddr(*(pmd))
#define __pte_unmap(pte)	do { } while (0)
#else
#define __pte_map(pmd)		(pte_t *)kmap_atomic(pmd_page(*(pmd)))
#define __pte_unmap(pte)	kunmap_atomic(pte)
#endif
 
#define pte_index(addr)		(((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
 
#define pte_offset_kernel(pmd,addr)	(pmd_page_vaddr(*(pmd)) + pte_index(addr))
 
#define pte_offset_map(pmd,addr)	(__pte_map(pmd) + pte_index(addr))
#define pte_unmap(pte)			__pte_unmap(pte)
 
#define pte_pfn(pte)		(pte_val(pte) >> PAGE_SHIFT)
#define pfn_pte(pfn,prot)	__pte(__pfn_to_phys(pfn) | pgprot_val(prot))
 
#define pte_page(pte)		pfn_to_page(pte_pfn(pte))
#define mk_pte(page,prot)	pfn_pte(page_to_pfn(page), prot)
 
#define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,pte,ext)
#define pte_clear(mm,addr,ptep)	set_pte_ext(ptep, __pte(0), 0)

其示意图如下图所示:

 

 
  2.4 根据虚拟地址获取物理页的示例代码

        根据虚拟地址获取物理页的示例代码详见 <mm/memory.c> 中的函数 follow_page

/**
 * follow_page - look up a page descriptor from a user-virtual address
 * @vma: vm_area_struct mapping @address
 * @address: virtual address to look up
 * @flags: flags modifying lookup behaviour
 *
 * @flags can have FOLL_ flags set, defined in <linux/mm.h>
 *
 * Returns the mapped (struct page *), %NULL if no mapping exists, or
 * an error pointer if there is a mapping to something not represented
 * by a page descriptor (see also vm_normal_page()).
 */
struct page *follow_page(struct vm_area_struct *vma, unsigned long address,
			unsigned int flags)
{
	pgd_t *pgd;
	pud_t *pud;
	pmd_t *pmd;
	pte_t *ptep, pte;
	spinlock_t *ptl;
	struct page *page;
	struct mm_struct *mm = vma->vm_mm;
 
	page = follow_huge_addr(mm, address, flags & FOLL_WRITE);
	if (!IS_ERR(page)) {
		BUG_ON(flags & FOLL_GET);
		goto out;
	}
 
	page = NULL;
	pgd = pgd_offset(mm, address);
	if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
		goto no_page_table;
 
	pud = pud_offset(pgd, address);
	if (pud_none(*pud))
		goto no_page_table;
	if (pud_huge(*pud) && vma->vm_flags & VM_HUGETLB) {
		BUG_ON(flags & FOLL_GET);
		page = follow_huge_pud(mm, address, pud, flags & FOLL_WRITE);
		goto out;
	}
	if (unlikely(pud_bad(*pud)))
		goto no_page_table;
 
	pmd = pmd_offset(pud, address);
	if (pmd_none(*pmd))
		goto no_page_table;
	if (pmd_huge(*pmd) && vma->vm_flags & VM_HUGETLB) {
		BUG_ON(flags & FOLL_GET);
		page = follow_huge_pmd(mm, address, pmd, flags & FOLL_WRITE);
		goto out;
	}
	if (pmd_trans_huge(*pmd)) {
		if (flags & FOLL_SPLIT) {
			split_huge_page_pmd(mm, pmd);
			goto split_fallthrough;
		}
		spin_lock(&mm->page_table_lock);
		if (likely(pmd_trans_huge(*pmd))) {
			if (unlikely(pmd_trans_splitting(*pmd))) {
				spin_unlock(&mm->page_table_lock);
				wait_split_huge_page(vma->anon_vma, pmd);
			} else {
				page = follow_trans_huge_pmd(mm, address,
							     pmd, flags);
				spin_unlock(&mm->page_table_lock);
				goto out;
			}
		} else
			spin_unlock(&mm->page_table_lock);
		/* fall through */
	}
split_fallthrough:
	if (unlikely(pmd_bad(*pmd)))
		goto no_page_table;
 
	ptep = pte_offset_map_lock(mm, pmd, address, &ptl);
 
	pte = *ptep;
	if (!pte_present(pte))
		goto no_page;
	if ((flags & FOLL_WRITE) && !pte_write(pte))
		goto unlock;
 
	page = vm_normal_page(vma, address, pte);
	if (unlikely(!page)) {
		if ((flags & FOLL_DUMP) ||
		    !is_zero_pfn(pte_pfn(pte)))
			goto bad_page;
		page = pte_page(pte);
	}
 
	if (flags & FOLL_GET)
		get_page(page);
	if (flags & FOLL_TOUCH) {
		if ((flags & FOLL_WRITE) &&
		    !pte_dirty(pte) && !PageDirty(page))
			set_page_dirty(page);
		/*
		 * pte_mkyoung() would be more correct here, but atomic care
		 * is needed to avoid losing the dirty bit: it is easier to use
		 * mark_page_accessed().
		 */
		mark_page_accessed(page);
	}
	if ((flags & FOLL_MLOCK) && (vma->vm_flags & VM_LOCKED)) {
		/*
		 * The preliminary mapping check is mainly to avoid the
		 * pointless overhead of lock_page on the ZERO_PAGE
		 * which might bounce very badly if there is contention.
		 *
		 * If the page is already locked, we don't need to
		 * handle it now - vmscan will handle it later if and
		 * when it attempts to reclaim the page.
		 */
		if (page->mapping && trylock_page(page)) {
			lru_add_drain();  /* push cached pages to LRU */
			/*
			 * Because we lock page here and migration is
			 * blocked by the pte's page reference, we need
			 * only check for file-cache page truncation.
			 */
			if (page->mapping)
				mlock_vma_page(page);
			unlock_page(page);
		}
	}
unlock:
	pte_unmap_unlock(ptep, ptl);
out:
	return page;
 
bad_page:
	pte_unmap_unlock(ptep, ptl);
	return ERR_PTR(-EFAULT);
 
no_page:
	pte_unmap_unlock(ptep, ptl);
	if (!pte_none(pte))
		return page;
 
no_page_table:
	/*
	 * When core dumping an enormous anonymous area that nobody
	 * has touched so far, we don't want to allocate unnecessary pages or
	 * page tables.  Return error instead of NULL to skip handle_mm_fault,
	 * then get_dump_page() will return NULL to leave a hole in the dump.
	 * But we can only make this optimization where a hole would surely
	 * be zero-filled if handle_mm_fault() actually did handle it.
	 */
	if ((flags & FOLL_DUMP) &&
	    (!vma->vm_ops || !vma->vm_ops->fault))
		return ERR_PTR(-EFAULT);
	return page;
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://nwjs.net/news/300096.html

如若内容造成侵权/违法违规/事实不符,请联系七分地网进行投诉反馈,一经查实,立即删除!

相关文章

华为鸿蒙10月17日,华为宣布10月17日重磅新机:鸿蒙系统+全球首发屏下摄像头...

今年华为手机太多大招&#xff0c;Mate30系列旗舰才开售不久&#xff0c;最近又曝光了一款顶级旗舰机&#xff0c;从曝光的真机渲染图来看&#xff0c;这款神秘机型将于10月17日在法国发布&#xff0c;手机渲染图四周超窄边框设计&#xff0c;值得注意的是手机右上角反光条纹&a…

linux 内存管理 page fault带来的性能问题

Linux进程如何访问内存 Linux下&#xff0c;进程并不是直接访问物理内存&#xff0c;而是通过内存管理单元(MMU)来访问内存资源。 原因后面会讲到。 为什么需要虚拟内存地址空间 假设某个进程需要4MB的空间&#xff0c;内存假设是1MB的&#xff0c;如果进程直接使用物理地址…

Linux 自动重启进程

Js代码 #!/bin/sh echo "Current DIR is " $PWD # ignore signal hup int quit tstp trap "" 1 2 3 24 start_server() { ulimit -c unlimited # export LD_LIBRARY_PATHpwd/deps/capn/libs:$LD_LIBRARY_PATH # rm -f *.pid # nohu…

Lucene学习笔记:Field.Store.* 域存储选项

2019独角兽企业重金招聘Python工程师标准>>> Store.YES&#xff1a; 指定存储域值。该情况下&#xff0c;原始的字符串值全部都被保存在索引中&#xff0c;并可以由IndexReader类恢复。该选项对于需要展示搜索结果的一些域很有用(如URL、标题或数据库主键)。如果索引…

第四次作业 孙保平034 李路平029

用C编写一个学生成绩管理系统 1、可以实现以下功能&#xff1a; cout<<"〓〓〓〓〓〓〓〓〓★ ☆ 1.增加学生成绩 ☆ ★〓〓〓〓〓〓〓〓〓"<<endl; 2、用链表存储信息 * 程序头部的注释结束 3、约定的规范&#xff1a; 1界面设计简介&#xff0c;人性化…

linux sar命令 性能监控

目录 1. CPU资源监控 2. inode、文件和其他内核表监控 3. 内存和交换空间监控 4. 内存分页监控 5. I/O和传送速率监控 6. 进程队列长度和平均负载状态监控 7. 系统交换活动信息监控 8.统计网络信息 9. 设备使用情况监控 10.常用命令汇总 sar命令常用格式 sar [optio…

android绘制高亮区域,实现高亮某行的RecyclerView效果

最终效果全部代码&#xff1a;github方式有二组合控件&#xff0c;RecyclerView View自定义RecyclerView1中只需要控制View&#xff0c;但是不好封装。2中需要重写RecyclerView中一些东西&#xff0c;最终就是一个CustomRecyclerView。所以采用的是第二种方法。代码100来行。主…

Android App监听软键盘按键的三种方式

前言: 我们在android手机上面有时候会遇到监听手机软键盘按键的时候&#xff0c;例如&#xff1a;我们在浏览器输入url完毕后可以点击软键盘右下角的“GO”按键加载url页面&#xff1b;在点击搜索框的时候&#xff0c;点击右下角的search符号键可以进行搜索&#xff1b;或者在全…

常用元素位置与大小总结

偏移量 offsetHeight: 元素在垂直方向占据的空间大小 > 内容高度 上下padding 上下boderoffsetWidth: 元素在水平方向占据的空间大小 > 内容宽度 左右padding 左右boderoffsetTop: 元素外边框距离offsetParent的上内边框的距离offsetLeft: 元素外边框距离offsetParen…

android将被代替的控件,Android 控件被忽略的属性

作者&#xff1a;ivm参考了 http://www.cnblogs.com/jisheng/archive/2013/01/10/2854088.html导航栏&#xff1a;1.TextView autoLinkHtml.fromHtml()2.Typeface &#xff1a;设置字体3.Switch 及 togglebutton4.AnalogClock DigitalClock 两个时钟的控件5.5.秒表控件Chronic…

AngularJS 、Backbone.js 和 Ember.js 的比较

2019独角兽企业重金招聘Python工程师标准>>> 译文: http://www.oschina.net/translate/javascript-framework-comparison 原文: http://www.airpair.com/js/javascript-framework-comparison 转载于:https://my.oschina.net/ilivebox/blog/307816

linux “大脏牛”漏洞分析(CVE-2017-1000405)

一、背景简介 脏牛漏洞(CVE-2016–5195)是公开后影响范围最广和最深的漏洞之一&#xff0c;这十年来的每一个Linux版本&#xff0c;包括Android、桌面版和服务器版都受到其影响。恶意攻击者通过该漏洞可以轻易地绕过常用的漏洞防御方法&#xff0c;对几百万的用户进行攻击。尽…

安卓音乐播放器开发实例

本文将引导大家做一个音乐播放器&#xff0c;在做这个Android开发实例的过程中&#xff0c;能够帮助大家进一步熟悉和掌握学过的ListView和其他一些组件。为了有更好的学习效果&#xff0c;其中很多功能我们手动实现&#xff0c;例如音乐播放的快进快退等。 先欣赏下本实例完成…

云计算&大数据 “下一幕”智能变革之力

2019独角兽企业重金招聘Python工程师标准>>> 2016年的互联网科技领域&#xff0c;云计算、大数据、人工智能成为最热词汇。阿里云“为了无法估算的价值”将中国的计算触角伸向海外&#xff0c;百度首次向外界展示“百度大脑”的科技成果&#xff0c;移动互联网利用人…

html和css有序列表,HTMLCSS基础学习笔记14—有序列表及列表嵌套

我们上篇讲到了无序列表&#xff0c;那么今天就来看看有序列表和他们的组合嵌套使用吧。有序列表现在我们要做那堆杂事了&#xff0c;但是发现这么多杂事&#xff0c;先做哪个好呢&#xff1f;于是我们给这堆杂事弄个优先级排序&#xff0c;让我们能够按照顺序做下去。HTML里用…

华硕笔记本BIOS设置详解

BIOS是英文Basic Input/Output System的缩写&#xff0c;即基本输入输出系统&#xff0c;合理的BIOS设置可以让我们的电脑更好的为我们服务&#xff0c;但由于大部分笔记本的BIOS界面是英文&#xff0c;并且BIOS里面的某些关键选项稍有差池就会导致各种各样的问题&#xff0c;导…

发表论文不用愁,十大技巧治秃头

鱼羊 编译整理 量子位 报道 | 公众号 QbitAI做研究跟发文章&#xff0c;完全是两码事。实验一时爽&#xff0c;落笔火葬场。好不容易憋出了初稿&#xff0c;又有多少研究僧跪在了反复修改的漫漫长路上。对于很多科研党来说&#xff0c;比起嗑盐的过程&#xff0c;恐怕写文章更让…

Ext4 MVC CRUD操作

项目目录结构如下&#xff1a; &#xff08;1&#xff09;index.html <!DOCTYPE html> <html><head><title>用户管理</title><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial…

手机拍视频,实时换背景,继马卡龙玩图后,Versa又出了一款更厉害的App

郭一璞 发自 凹非寺 量子位 报道 | 公众号 QbitAI在前不久荣耀9X的发布会上&#xff0c;一个名叫绿幕侠的App亮相了。现场的展示中&#xff0c;这个App能让手机拍摄的视频&#xff0c;产生电影绿幕一般的效果&#xff0c;实时从视频中去掉背景&#xff0c;只保留主角人物&#…

html中多边形图形怎么制作,CSS制作图形速查表

前面在《纯CSS制作的图形效果》一文中介绍了十六种CSS画各种不同图形的方法。今天花了点时间将这方面的制作成一份清单&#xff0c;方便大家急用时有地方可查。别的不多说了&#xff0c;直接看代码。为了节省时间&#xff0c;下面图形都采用的一个标签&#xff0c;可以是块元素…