Linux内存地址管理

文章目录

  • 系统内存布局
    • 内核地址的低端和高端内存概念
      • 低端内存
      • 高端内存
  • 地址转换和MMU
    • Linux中的四级分页模型
      • 虚拟地址字段
      • 页表处理
  • 将虚拟地址转换物理地址

Linux系统中的每个内存地址都是虚拟的,它们不直接指向任何物理内存地址。每当访问内存位置时,可以执行转换机制以匹配相应的物理内存,所以我们在程序中必须用虚拟地址来访问数据。

注:下面所说的内核空间和用户空间这样的术语指的都是虚拟地址空间

系统内存布局

在Linux系统中,每个进程都有自己独立的虚拟地址空间。它是一种内存沙箱,存在于进程的生命周期中。在32位系统上,该地址空间大小是4GB。针对每一个进程,4GB的地址空间被分割成两个部分:

  • 内核空间虚拟地址
  • 用户空间虚拟地址

分割方式依赖于特殊的内核配置选项CONFIG_PAGE_OFFSET,这个选项定义内核虚拟地址部分在进程地址空间的起始位置。典型的进程虚拟地址空间布局如下图:
在这里插入图片描述
内核空间和用户空间所使用的地址都是虚拟地址,不同的是,访问内核空间地址需要特权模式,当CPU运行用户空间代码时,活动进程被认为运行在用户模式下,当CPU运行内核空间代码时,活动进程被认为运行在内核模式下。

内核与每个进程共享其地址空间,原因如下:因为每个进程在给定的时刻都使用系统调用,这将涉及内核。将内核的虚拟内存地址映射到每个进程的虚拟地址空间能够避免每次进入(或者退出)内核时内存地址切换产生的开销。这就是内核地址空间被永久映射到每个进程顶部的原因—加快系统调用对内核的访问。

每个进程地址空间顶部都是内核的虚拟地址空间,这一部分每个进程都是相同的。

内存管理单元把内存组织为大小固定的单元—页面,内存页(虚拟页)指的是连续虚拟内存块,内核数据结构也使用相同的名称页面来表示内存页。帧(页面帧)指一段固定长度的连续物理内存块,操作系统在其上映射内存页。每个页面帧都有一个号码,叫做页面帧号(PFN)。

内核地址的低端和高端内存概念

Linux内核具有自己的虚拟地址空间。比如32位的x86,内核的虚拟地址空间是1GB大小,分成两个部分:

  • 低端内存或LOWMEM:第一个896MB
  • 高端内存或HIGHMEM:顶部的128MB

在这里插入图片描述

低端内存

内核地址空间的第一个896MB空间构成低端内存区域。在启动早期,内核永久映射这896MB的空间。该映射产生的地址为逻辑地址,这些都是虚拟地址,但是减去固定的偏移量后就可以将其转换为物理地址。因为映射是永久的,并且事先知道。大多数内核内存函数返回低端内存。事实上,为了满足不同的用途,内核内存被划分为区域,LOWMEM的第一个16MB内存保留为DMA使用。内核空间可以确定3种不同的内存区域:

  • ZONE_DMA:包含的内存页面帧在0~16MB,用于直接内存访问(DMA)
  • ZONE_NORMAL:包含的内存页面帧为16MB~896MB,常规使用
  • ZONE_HIGHMEM:包含的内存页面帧位于896MB及其以上

这就是说,512MB的系统上,不存在以上的划分。

逻辑地址的另一个定义:线性映射到物理地址上的内核空间中的地址,可以用偏移量或者应用位掩码将其转为物理地址,使用__pa(地址)宏可以将逻辑地址(内核中的虚拟地址)转换为物理地址,使用__va(地址)可以做相反的操作。

高端内存

内核地址空间顶部顶部128MB称为高端内存区域,内核用它临时映射1GB以上的物理内存,当需要访问896MB以上的物理内存时,内核会使用这128MB创建到其虚拟地址空间的临时映射,也就是将需要访问数据的物理页映射到这128MB内核虚拟地址空间来,从而实现访问所有物理页面的目标。可以把高端内存定义为逻辑地址存在的内存,但不会将其永久映射到内核地址空间。896MB以上的物理内存按需映射到HIGHMEM区域的128MB。

访问高端内存的映射由内核动态创建,访问后销毁,这使高内存访问速度变慢,64位系统上不存在高端内存这一概念。

地址转换和MMU

每次访问内存位置时,由CPU完成从虚拟地址到物理地址的转换。该机制称为地址转换,这由CPU中的内存管理单元(MMU)来执行。MMU转换的都是虚拟地址,所以访问数据,必须是虚拟地址,不能是物理地址,否则访问不了数据。

对于虚拟内存,内存组织为固定大小的页,而物理内存则按帧组织,页面表(PTE)概念的引入是为了管理页面和帧之间的映射。页面分部在表间,因此每个PTE的表项对于一个页面和帧之间的映射,然后给每个进程一组页面表来描述其整个内存空间。

Linux中的四级分页模型

Linux采用了一种同时适用于32位和64位系统的普通分页模型。从2.6.11版本开始,采用了四级分页模型:请添加图片描述
上图展示的4种页表分别被称为:

  • 页全局目录(Page Global Directory,PGD)
  • 页上级目录(Page Upper Directory,PUD)
  • 页中间目录(Page Middle Directory,PMD)
  • 页表(Page Table,PTE)

虚拟地址被分为5个部分,每个页表项指向一个页框,每一部分的大小与具体的计算机体系结构有关。

MMU如何知道进程页面表?很简单,MMU不存储任何地址。但CPU有一个特殊的寄存器,称为页面表基址寄存器(PTBR)或转换基址寄存器0(TTBR0),它指向进程1级页面表(PGD)的基址。这正是struct mm_struct的字段pgd指向的地址:current->mm.pgd == TTBR0.上面图中的cr3保存的就是该值

虚拟地址字段

下面宏简化了页表处理:

  • PAGE_SHIFT:指定Offset字段的位数,这个宏由PAGE_SIZE使用返回页的大小。最后,PAGE_MASK宏用以屏蔽Offset字段的所有位。
  • PMD_SHIFT:指定虚拟地址的offset字段和table字段的总位数,PMD_MASK宏用于屏蔽Offset字段与Table字段的所有位
  • PUD_SHIFT:确定页上级目录项能映射的区域大小的对数,PUD_MASK宏用于屏蔽Offset字段,Table字段、Middle Air字段和Upper Air字段的所有位
  • PGDIR_SHIFT:确定页全局目录项能映射的区域大小的对数。PGDIR_MASK宏用于屏蔽Offset、Table、Middle Air及Upper Air字段的所有位
  • PTRS_PER_PTE,PTRS_PER_PMD,PTRS_PER_PUS以及PTRS_PER_PGD:用于计算页表、页中间目录、页上级目录和页全局目录表中表现的个数。

页表处理

pte_t、pmd_t、pud_t和pgd_t分别描述页表项、页中间目录项、页上级目录项和页全局目录项。

五个类型转换宏(__pte,__pmd,__pgd和__pgprot)把一个无符号整数转换成所需的类型。另外的五个类型转换宏(pte_val,pmd_val,pud_val,pgd_val和pgport_val)执行相反的转换。

如果相应的表项值位0,那么,宏pte_none、pmd_none、pud_none、和pgd_none产生的值为1,否则产生的值为0

将虚拟地址转换物理地址

进程访问的都是用户空间内虚拟地址,内核访问的都是内核虚拟地址,进程用不了内核虚拟地址,同样内核用不了进程内虚拟地址。如果我们将用户空间地址传给内核,内核必须先将找到该用户空间虚拟地址对应的物理地址,再将物理地址转换成内核虚拟地址,内核才能访问数据

代码地址:http://edsionte.com/techblog/archives/1966

static void get_pgtable_macro(void)
{printk("PAGE_OFFSET = 0x%lx\n", PAGE_OFFSET);printk("PGDIR_SHIFT = %d\n", PGDIR_SHIFT);printk("PUD_SHIFT = %d\n", PUD_SHIFT);printk("PMD_SHIFT = %d\n", PMD_SHIFT);printk("PAGE_SHIFT = %d\n", PAGE_SHIFT);printk("PTRS_PER_PGD = %d\n", PTRS_PER_PGD);printk("PTRS_PER_PUD = %d\n", PTRS_PER_PUD);printk("PTRS_PER_PMD = %d\n", PTRS_PER_PMD);printk("PTRS_PER_PTE = %d\n", PTRS_PER_PTE);printk("PAGE_MASK = 0x%lx\n", PAGE_MASK);
}
static unsigned long vaddr2paddr(unsigned long vaddr)
{pgd_t *pgd;pud_t *pud;pmd_t *pmd;pte_t *pte;unsigned long paddr = 0;unsigned long page_addr = 0;unsigned long page_offset = 0;pgd = pgd_offset(current->mm, vaddr);printk("pgd_val = 0x%lx\n", pgd_val(*pgd));printk("pgd_index = %lu\n", pgd_index(vaddr));if (pgd_none(*pgd)) {printk("not mapped in pgd\n");return -1;}pud = pud_offset(pgd, vaddr);printk("pud_val = 0x%lx\n", pud_val(*pud));if (pud_none(*pud)) {printk("not mapped in pud\n");return -1;}pmd = pmd_offset(pud, vaddr);printk("pmd_val = 0x%lx\n", pmd_val(*pmd));printk("pmd_index = %lu\n", pmd_index(vaddr));if (pmd_none(*pmd)) {printk("not mapped in pmd\n");return -1;}pte = pte_offset_kernel(pmd, vaddr);printk("pte_val = 0x%lx\n", pte_val(*pte));printk("pte_index = %lu\n", pte_index(vaddr));if (pte_none(*pte)) {printk("not mapped in pte\n");return -1;}//页框物理地址机制 | 偏移量page_addr = pte_val(*pte) & PAGE_MASK;page_offset = vaddr & ~PAGE_MASK;paddr = page_addr | page_offset;printk("page_addr = %lx, page_offset = %lx\n", page_addr, page_offset);printk("vaddr = %lx, paddr = %lx\n", vaddr, paddr);return paddr;
}
static int __init v2p_init(void)
{unsigned long vaddr = 0;printk("vaddr to paddr module is running..\n");get_pgtable_macro();printk("\n");vaddr = (unsigned long)vmalloc(1000 * sizeof(char));if (vaddr == 0) {printk("vmalloc failed..\n");return 0;}printk("vmalloc_vaddr=0x%lx\n", vaddr);vaddr2paddr(vaddr);printk("\n\n");vaddr = __get_free_page(GFP_KERNEL);if (vaddr == 0) {printk("__get_free_page failed..\n");return 0;}printk("get_page_vaddr=0x%lx\n", vaddr);vaddr2paddr(vaddr);return 0;
}
static void __exit v2p_exit(void)
{printk("vaddr to paddr module is leaving..\n");vfree((void *)vaddr);free_page(vaddr);
}

整个程序的结构如下:

  1. get_pgtable_macro()打印当前系统分页机制中的一些宏。

  2. 通过vmalloc()在内核空间中分配内存,调用vaddr2paddr()将虚拟地址转化成物理地址。

  3. 通过__get_free_pages()在内核空间中分配页框,调用vaddr2paddr()将虚拟地址转化成物理地址。

  4. 分别通过vfree()和free_page()释放申请的内存空间。

vaddr2paddr()的执行过程如下:

  1. 通过pgd_offset计算页全局目录项的线性地址pgd,传入的参数为内存描述符mm和线性地址vaddr。接着打印pgd所指的页全局目录项。

  2. 通过pud_offset计算页上级目录项的线性地址pud,传入的参数为页全局目录项的线性地址pgd和线性地址vaddr。接着打印pud所指的页上级目录项。

  3. 通过pmd_offset计算页中间目录项的线性地址pmd,传入的参数为页上级目录项的线性地址pud和线性地址vaddr。接着打印pmd所指的页中间目录项。

  4. 通过pte_offset_kernel计算页表项的线性地址pte,传入的参数为页中间目录项的线性地址pmd和线性地址vaddr。接着打印pte所指的页表项。

  5. pte_val(*pte)先取出页表项,与PAGE_MASK相与的结果是得到要访问页的物理地址;vaddr&~PAGE_MASK用来得到线性地址offset字段;两者或运算得到最终的物理地址。

  6. 打印物理地址。

我们可以获得物理地址了,就可以使用__pa(地址)宏可以将物理地址转换为逻辑地址(内核中的虚拟地址),使用__va(地址)可以做相反的操作

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

录制caf 转 mp3

编译需要使用的 lame库http://www.cocoachina.com/bbs/read.php?tid108237参考的文章http://blog.csdn.net/ysy441088327/article/details/7392842说起来,我一直在找一个音频转换成mp3的方法。一年前,我成功编译出了一个lame for armv7的库。苦于不会使…

開發記要 詭異的變量

告別繁體文盲,從寫blog開始 Variable命名很重要,有多重要,看看.net和java的加密就知道, 都是把variable改到一塌糊塗,你想看看都沒門. 但是這幾天看遺留系統的代碼,真是大開眼界。 我一直以為別人寫a,b,c,d這些單字節variable已經很過分。直到我看到以下這幾個&#xff0…

排序算法---快速排序、堆排序、冒泡排序

排序算法1 快速排序代码实现stdlib库快排2 堆排序堆排序的基本思想如何构造一个大顶堆排序3 冒泡排序1 快速排序 文章原地址:https://blog.csdn.net/morewindows/article/details/6684558 快速排序的平均时间复杂度是0(NlogN),它采用了一种分治的策略&a…

项目总结:华南师范大学校园开发教育android客户端总结

忽略之前小打小闹,这个项目算是我的第一个项目--SCNU的网络公选课的android版本的客户端。项目是从5月中旬开始的,中间经历了几个星期的复习考试时间,到现在可以说是完工了吧(或许还有写细节要修改)。这个项目带给我蛮…

Linux系统编程---守护进程

1 守护进程的概述 Daemon(守护进程)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。Linux系统的大…

邮箱服务器

一.邮箱服务器的基本概念 邮件的客户端:可以只安装在电脑上(C/S)的也可以是网页形式(B/S)的 邮件服务器:起到邮件的接受与推送的作用 邮件发送的协议: 协议:就是数据传输…

类加载器

一、类加载器 1,什么是类加载器? 类加载器就是用来加载字节码文件 2,类加载器的种类有哪些? 1)BootStrap:引导类加载器:加载都是最基础的文件 2)ExtClassLoader:扩展类加…

算法---链表

文章目录反转链表合并两个有序链表删除重复元素反转链表 反转链表包括两种,反转全部元素或者反转部分元素。在这里,我们约定:数据元素类型是struct LinkNode,要反转链表的第一个节点是head,head的前面一个节点是pre&a…

SSM

二、环境设置(MyEclipse) 1,字体设置 window–>Preference->General->Appearance->Colors and Fonts->Basic Text->Font 2,workspace字符集设置 window–>Preference->General->Appearance->W…

设计模式--Strategy 策略模式

所谓策略模式(Strategy Pattern),就是将策略 (算法) 封装为一个对象,易于相互替换,如同 USB 设备一样可即插即用;如果将策略、具体的算法和行为,编码在某个类或客户程序内部,将导至事后的修改和扩展不易。 …

打开eclipse出现Failed to load the JNI shared library “D:\java\jdk\bin\...\jre\bin\server\jvm.dll”如何解决?

eclipse打开的时候出现Failed to load the JNI shared library “D:\java\jdk\bin…\jre\bin\server\jvm.dll”如何解决?? 如图所示: 即代表你的jdk与eclipse的位数不一样!!! 你可以查看一下eclipse和jd…

圆形坠落模拟算法设计

目标:实现一个算法,模拟在一个封闭二维区域,圆形小球朝给定方向坠落的过程,实现二维区域的紧密填充。 像下面这样: 难点,及其简单解决: 1.如何把粒子移动尽可能远? 图中的粒子i&…

Maven详细教学

一、Maven简介 maven:是apache下的一个开源项目,是纯java开发,并且只是用来管理java项目的 依赖管理:就是对jar包的统一管理 可以节省空间 项目一键构建:mvn tomcat:run该代码可以将一个完整的项目运行起来&#xff0…

网站后台中对html标签的处理

最近做一个CMS&#xff0c;后台中需要使用在线编辑器对新闻进行编辑&#xff0c;然后发表。我用的在线编辑器是CKEditorCKFinder。也许是我为了让CKEditor更本地化吧&#xff0c;改了很多。后来发现在CKEditor中对文字设置字体、颜色、字号大小时文字的<span>标签会出现N…

<各国地图轮廓app>技术支持

如在app使用过程中遇到任何问题&#xff0c;请与开发者联系caohechunhotmail.com

ffplay SDL_OpenAudio (2 channels, 44100 Hz): WASAPI can‘t initialize audio client“

windows下&#xff1a; ffplay 提示"SDL_OpenAudio (2 channels, 44100 Hz): WASAPI can’t initialize audio client" 添加环境变量&#xff1a;SDL_AUDIODRIVERdirectsound

(扩展)欧几里德快速幂

GCD模板 __int64 gcd(__int64 a,__int64 b) {return b0? a:gcd(b,a%b); } 欧几里德算法又称辗转相除法&#xff0c;用于计算两个整数a,b的最大公约数。其计算原理依赖于下面的定理&#xff1a; gcd函数就是用来求(a,b)的最大公约数的。 gcd函数的基本性质&#xff1a; gcd(a,…

Silverlight + WCF异步调用 例子

看大家好像对我的NParsing框架不是很感兴趣&#xff08;写NParsing帖没人顶我&#xff09;&#xff0c;那就给大家来点“甜品”&#xff0c;换换口谓。来说说Silverlight方面的东西。 在Silverlight中数据通信只能用异步。有人会觉得写起来很麻烦&#xff0c;其实不然。也有很简…

小议SqlMapConfig.xml配置文件

①、mybatis-3-config.dtd 主要用于mybatis的核心配文件sqlMapConfig.xml的约束 sqlMapConfig.xml代码如下&#xff1a; <?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN&q…

ffmepg 命令提取音视频数据

原文件&#xff1a; 1&#xff1a; 原音频数据提取&#xff08;保留还是mp4的封装格式的&#xff09;&#xff1a; ffmpeg -i test_1920x1080.mp4 -acodec copy -vn audio.mp4 -vn 就是没有视频&#xff0c; -acodec copy 音频拷贝不进行任何转码 原视频数据提取&#xff0…