当前位置: 首页 > 编程日记 > 正文

linux内存布局及页面映射

在Linux系统中,以32bit x86系统来说,进程的4GB内存空间(虚拟地址空间)被划分成为两个部分

------用户空间和内核空间,大小分别为0-3G,3-4G。

       用户进程通常情况下,只能访问用户空间的虚拟地址,不能访问到内核空间。

 

       每个进程的用户空间存放用户的程序和代码(堆栈,数据区,代码区等),因为是虚拟地址,所以每个进程的用户空间是完全独立的,互不影响。用户进程有自己的进程页表。

      内核空间是内核负责映射物理地址的(所有的进程共享一份内核空间的映射???这个还不太确定),内核空间有自己对应的页表,它与用户空间是独立的。

 

1.我们先来看一下用户空间的内存布局(即进程在内存的布局--C运行时库对内存的分配和管理)(下图中0--3G的范 围):

 

    从上图我们可以看到,用户进程的代码区一般从虚拟地址空间的0x08048000开始,这是为了便于检查空指针。代码区之上便是数据区,未初始化数据区,堆区,栈区,以及参数、全局环境变量。用户空间内存布局可以分为以下几个方面(用户程序中所申请分配的都是用户空间的虚拟地址):

   代码段(.text/ code segment):这里存放程序执行代码(cpu要执行的指令)的一块内存区域。这部分区域在程序运行前就确定了,通常情况下是只读的(防止程序由于错误而修改自身指令)。某些结构也允许代码段可写,即可以修改程序。代码段是可以共享的,相同的代码在内存中只有一份拷贝。除了代码外,这个区域里面也可能包含一些只读的常数变量,如字符串常量  char *p="12345" ,这里“12345”就存在代码段里面。


    初始化数据段(.data segment):这里存放的是程序中需要明确赋初始值的变量,如已经初始化的全局变量。数据段属于静态内存分配。包括static变量。


    未初始化数据段(.bss):存放未经初始化的全局变量。内核在执行该程序前,将其初始化为0或者null。BBS(block started by symbol), 属于静态内存分配。

   堆(Heap):存放程序中进行动态内存申请,例如经常用到的malloc,new系列函数就是从这个段中申请内存。堆的大小不固定,可以动态增加(malloc)和缩减(free)

    栈(Stack):又称堆栈,函数中的局部变量以及在函数调用过程中产生的临时变量都保存在此段中。(在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。 )

 

注:

1. 代码段(.text)和初始化数据段(.data) 都位于程序的可执行文件中,内核在调用exec函数启动该程序时从源程序文件中读入。
2. 栈是自顶向下扩展,栈是有界的(系统为 stack 区域保留了 128M 内存地址空间)。 堆是自底向上扩展,mmap映射区自顶向下扩展,mmap 映射区域和堆相对扩展,直至耗尽虚拟地址空间中的剩余区域,这种结构便于 C 运行时库使用 mmap 映射区域和堆进行内存分配。

3.栈和 mmap 映射区域并不是从一个固定地址开始,并且每次的值都不一样,这是程序在启动时随机改变这些值的设置,使得使用缓冲区溢出进行攻击更加困难。当然也可以让程序的栈和 mmap 映射区域从一个固定位置开始,只需要设置全局变量 randomize_v a_space 值为 0 ,这个变量默认值为 1 。用户可以通过设置/proc/sys/kernel/randomize_va_space 来停用该特性,也可以用如下命令:

       sudo sysctl -w kernel.randomize_va_space=0

4.bss段(未手动初始化的数据)并不给该段的数据分配空间,只是记录数据所需空间的大小。bss中未经初始化 的变量由exec初始化为0.
5.data(已手动初始化的数据)段则为数据分配空间,数据保存在目标文件中。

6. 代码段的大小在连接器链接之前,就得到,数据段包含经过初始化的全局变量以及它们的值,BSS段的大小从可执行文件中得到 ,然后链接器得到这个大小的内存块,紧跟在数据段后面。当这个内存区进入程序的地址空间后全部清零。包含数据段和BSS段的整个区段此时通常称为数据区

 

2.在进程空间的3-4G之间的内核空间中,从低地址到高地址依次为:物理内存映射区—隔离带—vmalloc虚拟内存分配区—隔离带—高端内存映射区—专用页面映射区—保留区

 

对照左右两个图(右边的图对于3G--3G+896范围分的更加详细一些),主要有以下几个区域(从上到下)

(1)高端128M由3部分组成(3G+896M--4G): ---这段空间需要通过页表转换才能与物理地址空间映射

  • 高端内存临时内核映射区(temporary kernel mapping)
  • 高端内存永久内核映射区(persistent kernel mapping)
  • vmalloc用来分配物理地址非连续的内存空间(vmalloc area) 

(2)系统物理内存映射区(从下到上3G--3G+896):----这段空间可以跟物理地址空间896M以下进行直接映射

  • 3G--3G+16M(低端16M区域):用于DMA操作
  • kernel image
  • mem_map
  • 其他内存区域

 

  【内核空间内存动态申请】主要包括三个函数:kmalloc(), __get_free_pages, vmalloc。

     kmalloc(),__get_free_pages申请的内存位于物理地址映射区(3G--3G+896),且在物理上也是连续的,它们与真实的物理地址只有一个固定偏

    移(可以直接映射,因此存在较简单的转换关系。而vmalloc申请的内存位于vmalloc虚拟内存分配区(这些区都是以线性地址为度量),它在

    虚拟内存空间给出一块连续的内存区,实质上,这片连续的虚拟内存在物理内存中并不一定连续,需要通过页表转换。

   因为vmalloc申请的在虚拟内存空间连续的内存区在物理内存中并不一定连续,可以想象为了完成vmalloc,新的页表需要被建立,因此,调用

    vmalloc来分配少量内存是不妥的。一般来讲,kmalloc用来分配小于128K的内存,而更大的内存块需要用vmalloc来实现

 

  【虚拟地址与物理地址关系】

   对于内核物理内存映射区的虚拟内存(用kmalloc(), __get_free_pages申请的),使用virt_to_phys()和phys_to_virt()来实现物理地址和内核虚拟

   地址之间的互相转换。它实际上,仅仅做了3G的地址移位。

 

 

 

3. 下面我们来看下x86实际的物理地址空间布局:

   

 

linux中页为单位来管理内存,内核将所有的物理地址划分为2^12个页面(Linux系统在初始化时,会根据实际的物理内存的大小,为每个物理页面创建一个page对象,所有的page对象构成一个mem_map数组),所有物理页面从图上可以看到划分到3类内存管理区中,ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM。 

  • ZONE_DMA的范围是0~16M:

         0-640K 被PCI设备的I/O内存映射占据,它们的大小和布局由PCI规范所决定。

         640K~1M这段地址空间被BIOS和VGA适配器所占据。

         由于这两段地址空间的存在,导致相应的RAM空间不能被CPU所寻址(当CPU访问该段地址时,北桥会自动将目的物理地址“路由”到相应的I/O设备上,不会发送给RAM),从而形成RAM空洞。

 在系统初始化阶段,内核首先在实模式下建立一个物理地址映射来指定哪些物理地址范围对内核可用而哪些不可用(主要是根据映射硬件设备I/O的共享内存,或者根据相应的页框含有的BIOS数据)。

   内存某些部分永久地分配给BOIS或内核,用来存放BIOS信息、内核代码以及静态内核数据结构。所以内核将下列页框记为保留:
• 在不可用的物理地址范围内的页框,一般用来存放BIOS信息。
• 含有内核代码和已初始化的数据结构的页框。
  标记为保留页框中的页,绝不能被动态分配或交换到磁盘上。

  一般来说,Linux内核安装在RAM中从物理地址0x00100000开始的地方,也就是说,从第二个MB开始。所需页框总数依赖于内核的配置方案:典型的配置所得到的内核可以完全被安装在小于3MB的RAM中。为什么不从第一个M开始?因为第0个页给了BIOS使用,存放加电自检(Power-On Self-Test,POST)期间检查到的硬件配置。因此,很多膝上型电脑的BIOS甚至在系统初始化后还将数据写到该页框。物理地址从0x000a0000 到 0x000fffff的范围通常留给BIOS例程,虽然前1M里BIOS并没有使用完,但是为了避免把内核装入一组不连续的页框里,影响性能,Linux便跳过第1MB的RAM,之间从第2个MB开始加载。

     1M-16M 该区域的物理页面专门供I/O设备的DMA使用。之所以需要单独管理DMA的物理页面,是因为DMA使用物理地址访问

     内存,不经过MMU,并且需要连续的缓冲区,所以为了能够提供物理上连续的缓冲区,必须从物理地址空间专门划分一段区

     域用于DMA。 

  • ZONE_NORMAL的范围是16M~896M,该区域的物理页面是内核能够直接使用的(可以到内核3G--3G+896M直接映射)。 
  • ZONE_HIGHMEM的范围是896M~结束,该区域即为高端内存,内核不能直接使用。

 

4. 内核空间和物理空间的映射:

   

 

   1. 由于ZONE_NORMAL和内核线性空间存在直接映射关系,所以内核会将频繁使用的数据如kernel代码、GDT、IDT、PGD、mem_map数组等放在ZONE_NORMAL里。而将用户数据、页表(PT)等不常用数据放在ZONE_ HIGHMEM里,只在要访问这些数据时才建立映射关系(kmap())。比如,当内核要访问I/O设备存储空间时,就使用ioremap()将位于物理地址高端的mmio区内存映射到内核空间的vmalloc area中,在使用完之后便断开映射关系。 

   2.由于开启了分页机制,内核想要访问物理地址空间的话,必须先建立映射关系,然后通过虚拟地址来访问。为了能够访问所有的物理地址空间,就要将全部物理地址空间映射到1G的内核线性空间中,这显然不可能。于是,内核将0~896M的物理地址空间一对一映射到自己的线性地址空间中,这样它便可以随时访问ZONE_DMA和ZONE_NORMAL里的物理页面;此时内核剩下的128M线性地址空间不足以完全映射所有的ZONE_HIGHMEM,Linux采取了动态映射的方法,即按需的将ZONE_HIGHMEM里的物理页面映射到kernel space的最后128M线性地址空间里,使用完之后释放映射关系,以供其它物理页面映射。虽然这样存在效率的问题,但是内核毕竟可以正常的访问所有的物理地址空间了。

 

5. 一般用户空间要通过内核空间,最终映射到物理地址空间(????),用户空间与物理空间也可能存在直接的映射:

  当RAM足够多时,内核会将用户数据保存在ZONE_ HIGHMEM,从而为内核腾出内存空间。

https://blog.csdn.net/sinat_16790541/article/details/42244757

相关文章:

codeforces Kyoya and Colored Balls

题解见:http://blog.csdn.net/libin56842/article/details/46650209 注意这里的组合数取模~~~ 1 /*Author :usedrose */2 /*Created Time :2015/8/7 13:31:44*/3 /*File Name :2.cpp*/4 #pragma comment(linker, "/STACK:1024000000,1024000000") 5 #inc…

存储mysql数据存在特殊字符时处理_转义 存储数据时特殊符号的处理

function url_base64_encode($str){//将这个方法处理后的数据可以存储,不会有特殊符号if($str"")return "";$codebase64_encode($str);//$codedHQ;$codestr_replace(,"!",$code);//把所用""替换成"!"$codestr_re…

虚拟化中的SR-IOV

虚拟化环境中有很多的硬件加速技术,这些技术标准来源于行业内的领导者或各种组织机构,但是在实际项目落地时又有哪些会被启用呢?哪些启用的功能带来了性能上明显的提升呢?那么这些加速技术如果不痛不痒的话那么它们的存在究竟意义…

查看线程的运行状态

实例说明线程共有六个状态,即新建、运行(可运行)、阻塞、等待、计时等待和终止。当使用new操作符创建新线程时,线程处于“新建状态”。当调用start方法时,线程处于运行(可运行)状态。当线程需要…

Linux 的内存管理工具和调优参数

1. free 2. top 3. vmstat 4. slabtop; 5. pmap 6. dmesg 7. /proc/meminfo 8. /proc/sys/vm 目录下的文件 9. sync 10./proc/zoneinfo 11./proc/pagetypeinfo 查看内存工具:1.free free - Display amount of free and used memory in the system rootubuntu:/home/…

java多线程查询_利用Java函数式接口处理多线程查询

Java函数式接口有且只有一个抽象方法的接口被称为函数式接口.FunctionalInterface注解: 该注解可用于一个接口的定义上, 一旦使用该注解来定义接口, 编译器将会强制检查该接口是否确实有且仅有一个抽象方法, 否则将会报错.该注解不是必须的, 只要符合函数式接口的定义,那么这个…

奇妙的算法之LCS妙解

LCS算法妙解 LCS问题简述:最长公共子序列 一个数列 S,如果分别是两个或多个已知数列的子序列,且是所有符合此条件序列中最长的,则S 称为已知序列的最长公共子序列。 LCS问题的分支:最长公共子串与最长公共子序列 子串&…

关于PreferenceActivity的使用和一些问题的解决(自己定义Title和取值)

android的Setting往往用PreferenceActivity来写的 我们在建立layout文件: <PreferenceScreen xmlns:android"http://schemas.android.com/apk/res/android"> <PreferenceCategory android:title"常规设置" android:key"set_local">&…

python学习-25 函数递归

递归 例如&#xff1a; def abc(n):print(n)if int(n/2) 0:return nreturn abc(int(n/2))abc(10) 运行结果&#xff1a; 10 5 2 1Process finished with exit code 0 2.小程序实例 import time people_list [小明,小红,小刚,小王,小青]def ask(people_list):if len(people_li…

二维指针删除单向链表

Linus slashdot: https://meta.slashdot.org/story/12/10/11/0030249 原文&#xff1a; https://coolshell.cn/articles/8990.html Linus大婶在slashdot上回答一些编程爱好者的提问&#xff0c;其中一个人问他什么样的代码是他所喜好的&#xff0c;大婶表述了自己一些观点…

对比java_java集合对比

list与Set、Map区别及适用场景1、List,Set都是继承自Collection接口&#xff0c;Map则不是2、List特点&#xff1a;元素有放入顺序&#xff0c;元素可重复 &#xff0c;Set特点&#xff1a;元素无放入顺序&#xff0c;元素不可重复&#xff0c;重复元素会覆盖掉&#xff0c;(注…

.ARM.exidx

简介&#xff1a; .ARM.exidx is the section containing information for unwinding the stack. If your C program has functions that print out a stack backtrace, the functions will likely depend on this section being present. 相关的编译选项 -funwind-tables 二问…

Oracle VM VirtualBox安裝Windows 2000失败

问题&#xff1a;VirtualBox下安装Windows2000&#xff0c;设置网络后进入最后一步&#xff0c;复制组件……然后就是重启&#xff1b;再试还是重启&#xff01;解决&#xff1a;在Oracle网站上查了一下资料&#xff1a;http://www.virtualbox.org/manual/ch12.html#idp1278616…

用户/目录操作

用户操作 useradd/adduser 创建用户 passwd 修改用户密码 userdel 删除用户 usermod 修改用户信息 -g<群组> 修改用户所属群组 -G<群组> 修改用户所属的附加群组 -l<帐户名> 修改账户名称 -u 修改用户ID -L锁定用户密码 -U 解除密码锁定 adduser -u用…

linux内核 -内存管理模块概图

1.从进程(task)的角度来看内存管理 每个进程对应一个task_struct;每个task_struct 里面包含指向mm_struct 的指针mm, mm_struct 里面的主要成员&#xff1a; a. 指向vma链表的头指针&#xff1a;mmap b. 指向vma红黑树的根节点: mm_rb c. 指向进程列表的指针pgb;vma(vm_are…

求一个字符串中连续出现的次数最多的子串

求一个字符串中连续出现的次数最多的子串。例如字符串“abababc”,最多连续出现的为ab&#xff0c;连续出现三次。要和求一个字符串中的最长重复子串区分开来&#xff0c;还是上面的字符串&#xff0c;那么最长的重复子串为abab。两个题目的解法有些类似&#xff0c;都用到了后…

java ftp 判断文件是否存在_FTP判断文件是否存在

FTP Client使用的是Apache Commons Net 3.3/*** 检查FTP上指定文件是否存在* param remoteFilePartNameList 文件路径* throws BusinessException* throws IOException*/private void checkFtpFileExist(List remoteFilePartNameList) throws BusinessException, IOException {…

软件定义光网络-SDON

为什么80%的码农都做不了架构师&#xff1f;>>> 软件定义光网络-SDON 随着宽带业务与应用的持续增长&#xff0c;光网络面临着新的发展机遇与技术挑战。作为当前业界研究热点之一&#xff0c;SDON聚焦于将软件定义技术融入光网络的综合解决方案&#xff0c;其关键技…

记录一次爬取某昵称网站的爬虫

同学跑去实习了...然后工作的时候要她用python写一个爬虫&#xff0c;爬取一万个可以用的用户昵称。&#xff08;为什么他们都能找到工作啊QAQ&#xff09; 然后&#xff0c;她找到了我...然后在我动笔的时候&#xff0c;发现之前写过的爬虫基本上忘完了...无奈下只好对着以前…

《LINUX3.0内核源代码分析》第一章:内存寻址

https://blog.csdn.net/ekenlinbing/article/details/7613334 摘要&#xff1a;本章主要介绍了LINUX3.0内存寻址方面的内容&#xff0c;重点对follow_page函数进行注释&#xff0c;以帮助读者大致了解ARM A9的页表组织。 读者需要理解一些基本概念&#xff1a;虚拟地址、物理地…

java integer int 比较_java Integer和int之间的比较问题是什么?

展开全部java Integer和int之间e68a84e8a2ad3231313335323631343130323136353331333365633864的比较问题。求解释public static void main(String[] args) { // TODO Auto-generated method stub Integer a new Integer(1); Integer b new Integer(1); int c1; Integer e 1;…

Oracle 12C -- 基于sequence的列的默认值

12C支持先创建一个sequence&#xff0c;然后再将该sequence指定为某个列的值的默认表达式。 和"identity column"具有以下不同点&#xff1a; 对列的个数没有限制 sequence必须在列定义之前定义 如果删除了sequence&#xff0c;会导致后面的insert报错 表的owner&…

Python的XML-RPC学习

编写客户端提交数据到服务器处理是程序员最常碰到的几个问题之一。各种不同的语言对此都有相应的解决方案。比如Unix下&#xff0c;C程序员们可以用SUNRPC&#xff0c;Java程序员则使用RMI来处理。大多数语言还都可以使用Web Service或者ICE。它们的使用方法类似&#xff0c;编…

Anaconda安装,jupyter notebook 使用说明

conda install pandas---安装pandas包 conda remove package_names conda update package_names conda list ---列出该环境下安装的package conda install nb_conda --------安装nb_conda用于notebook自动关联nb_conda的环境 conda create -n env_name package_name -------…

ARM32页表-虚拟地址到物理地址的转换

ARM32的页表 页表就是用于将虚拟地址转换为物理地址的转换关系表。访问虚拟地址时&#xff0c;计算机通过页表找到对应的实际物理地址访问。 我们在上一节介绍了内存管理模块概图, 怎么完成从pgd 到 page的转化呢&#xff1f; linux 内核code是通过follow_page来完成的…

java 重载 参数子类_java - Java中带有子类参数的函数重载 - 堆栈内存溢出

这个问题已经在这里有了答案&#xff1a;我有一个扩展了另一个类的类(在这种情况下&#xff0c;这是一个例外)&#xff1a;public class NewTypeException extends Exception {private String exceptionField;public String getExceptionField() {return exceptionField;}publi…

Caused by: java.sql.BatchUpdateException

Caused by: java.sql.BatchUpdateException: Table (%s) has been dropped, altered or renamed.解决方法重启项目转载于:https://www.cnblogs.com/mySummer/p/4723561.html

do{ ...}while(0)应用技巧

辅助定义复杂的宏example: #define A(args) do { a(args); b() } while(0);如果定义#define A(args) a(args);b();if(i > 0) A(i) if(i > 0 )do { a(2);b();} while(0) 或者while(1)a(args);b(); 这不是我们想要的&#xff0c;因为第二个b();不会被执行。代替g…

Idea--使用Idea调试设置

参考 https://blog.csdn.net/yyjava/article/details/81453748 关闭一些Idea默认设置&#xff0c;否则懵逼到爆炸.. 1.关闭集合类视图 2.关闭watch视窗默认调用toString&#xff08;真的很懵逼&#xff01;&#xff01;&#xff09; 转载于:https://www.cnblogs.com/microcat/p…

基于i2c子系统的驱动分析

https://blog.csdn.net/qq_28992301/article/details/52467766