当前位置: 首页 > news >正文

虚拟地址空间:揭秘Linux内存

在这里插入图片描述

文章目录

  • 前言
  • 虚拟地址空间
  • 虚拟地址
    • 现象
    • 概念
    • 页表
    • 作用

前言

在学C语言或者C++时,我们学过内存分区的概念,对栈区、堆区等分区有了基本的理解。这篇文章我们会对这部分进行更详细的介绍。

虚拟地址空间

程序地址空间的分布图如下所示:
在这里插入图片描述
我们可以通过以下代码来验证一下:

#include <stdio.h>#include <unistd.h>#include <stdlib.h>int g_unval;int g_val = 100;int main(int argc, char *argv[], char *env[])
{const char *str = "helloworld";printf("code addr: %p\n", main);printf("init global addr: %p\n", &g_val);printf("uninit global addr: %p\n", &g_unval);static int test = 10;char *heap_mem = (char*)malloc(10);char *heap_mem1 = (char*)malloc(10);char *heap_mem2 = (char*)malloc(10);char *heap_mem3 = (char*)malloc(10);printf("heap addr: %p\n", heap_mem); //heap_mem(0), &heap_mem(1)printf("heap addr: %p\n", heap_mem1); //heap_mem(0), &heap_mem(1)printf("heap addr: %p\n", heap_mem2); //heap_mem(0), &heap_mem(1)printf("heap addr: %p\n", heap_mem3); //heap_mem(0), &heap_mem(1)printf("test static addr: %p\n", &test); //heap_mem(0), &heap_mem(1)printf("stack addr: %p\n", &heap_mem); //heap_mem(0), &heap_mem(1)printf("stack addr: %p\n", &heap_mem1); //heap_mem(0), &heap_mem(1)printf("stack addr: %p\n", &heap_mem2); //heap_mem(0), &heap_mem(1)printf("stack addr: %p\n", &heap_mem3); //heap_mem(0), &heap_mem(1)printf("read only string addr: %p\n", str);for(int i = 0 ;i < argc; i++){printf("argv[%d]: %p\n", i, argv[i]);}for(int i = 0; env[i]; i++){printf("env[%d]: %p\n", i, env[i]);}return 0;
}

运行的结果如下所示:
在这里插入图片描述
对比不同变量在内存中的分布,地址空间的布局与分布图相符。
同时,我们可以发现,堆区向地址增大的方向增长,栈区向地址减小的方向增长。以栈为例子,开辟空间时,得到的地址是众多字节中最小的一个,也就是说,开辟空间时,地址向下增长,但是,访问时,是向上访问的,访问多少字节由类型来决定,如下图所示:
在这里插入图片描述
堆和栈是相对而生的。一个向上增长,另一个向下增长。
以上就是虚拟地址空间的一个简单介绍。

虚拟地址

现象

之前我们了解过进程的相关概念,我们来看以下一个程序:

#include <stdio.h>    
#include <unistd.h>    int g_val = 100;    int main()    
{    printf("g_val: %d, &g_val:%p\n",g_val, &g_val);    pid_t id = fork();    if(id == 0)    {    while(1)    {    printf("I am a child process,pid:%d,ppid:%d,g_val: %d, &g_val:%p\n",getpid(),getppid(),g_val, &g_val);    sleep(1);    g_val++;}    }    else    {    while(1)    {    printf("I am a parent process,pid:%d,ppid:%d,g_val: %d, &g_val:%p\n",getpid(),getppid(),g_val, &g_val);                                                                    sleep(1);    }    }    
} 

以上代码的运行结果如下:
在这里插入图片描述
因为父子进程具有独立性,子进程对变量进行修改,父进程对应的变量没有变化。但是我们发现,父子进程变量的地址空间是一样的,这也就说明,这里显示的地址不是物理地址,而是虚拟地址。
每次运行程序的时候,都会将程序转化为进程,就会有task_struct,操作系统加载代码和数据时,会加载到对应的物理内存中。操作系统会为进程创建一个虚拟地址空间,虚拟地址对应的范围为000…00~FFF…FF,也会在虚拟地址空间上为变量和数据开辟空间。同时,操作系统也会创建一张页表,页表的主要作用是实现虚拟地址到物理地址的映射。用户所得到的,是对应的虚拟地址,操作系统会通过页表映射到对应的物理地址中。
fork之后,创建了一个子进程,子进程创建的时候,是以父进程为模板的,而进程=内核数据结构+代码和数据,所以子进程也会拷贝一份虚拟地址空间和页表。因此,虚拟地址空间和页表,每一个进程各自有一套,同时,子进程会拷贝父进程的页表,这也是父子进程共享代码和数据,但是进程具有独立性的原因。 如下图所示:
在这里插入图片描述

父子进程任何一个进程尝试对共享的变量进行修改时,不能直接修改,而要发生"写时拷贝",会先将原数据在物理内存中重新开辟空间并进行拷贝,同时操作系统会修改页表中的映射关系。如下图所示:
在这里插入图片描述
而真实的物理地址,被操作系统隐藏起来了。
之前我们使用fork()函数创建进程时,会用一个变量来接收函数的返回值,但是可以根据同一个变量接收两个不同的返回值,这也是因为操作系统将虚拟地址映射为不同的物理地址的原因。

概念

之前我们说过,操作系统管理数据的方式是"先描述,再组织"。同样的,操作系统对虚拟地址空间的管理原则也是如此,操作系统中,描述Linux下进程的地址空间的所有信息的结构体是mm_struct(内存描述符)。该结构体中规定了各区域开始的地方和结束的地方,像栈和堆这样的空间,若对应的空间有变化,对结构体当中的成员属性进行修改即可。

页表

页表除了上文提到的虚拟地址和物理地址之外,还包括很多标志位,比如权限位,通过权限位,就可以控制对应的变量的读写权限。同时还有一个权限位表示映射的物理数据是否在内存中,1表示存在,可以访问;0表示不存在,证明对应的内存数据被挂起到磁盘的swap分区中。其它更详细的过程和更多的标志位会在后面逐步介绍。
在Linux系统中,创建一个进程,是先创建内核的数据结构,然后再加载代码和数据,但是,如果此时不着急执行这个进程,可以在需要执行代码和访问数据的时候,再进行加载数据,这种现象称为懒加载。当我们用malloc函数申请空间时,本质是在虚拟地址空间的堆区申请,申请的时候,操作系统会在页表虚拟地址处填上对应的虚地址,但是实际的物理地址,可以先不填写,在真正用的时候动态申请。这种只申请虚拟地址空间,在真正使用再申请空间的过程,叫做缺页中断引起的内存二次申请,这个后面也会逐步介绍。

作用

使用虚拟地址和虚拟地址空间,主要有以下几个作用:

  1. 控制进程的行为,拦截进程的非法行为,进一步保护物理内存
  2. 有了虚拟地址空间和页表,可将进程的内存空间布局,无序变为有序
  3. 使进程管理和内存管理模块解耦合
http://www.dtcms.com/a/586328.html

相关文章:

  • 嵌入式开发安装Samba服务实现方法
  • 网站开发人员要求备案个人可以做视频网站
  • 网站为契机建设校园数字化上海网站建设哪家技术好
  • 人工智能决策系统和传统决策模型相比有什么优势?
  • 简述dw网站建设步骤网站建设哪家便宜
  • 未来新科技:纳米酶——类酶纳米材料的突破性应用与前景
  • 数据结构(c++版):深入理解哈希计数器
  • 网站侧边栏代码网站设计制作哪种快
  • Flutter SlideTransition 实现平移动画
  • Android EDLA 认证提测前的基本开发和准备简要说明
  • 潍坊网站设计制作seo软件服务
  • 心连网网站wordpress4.9博客模板
  • 数据库要进行分表分库是开发一个项目就要设计好了,还是上线后根据需要再分表分库?
  • TimeBridge: Non-Stationarity Matters for Long-term Time Series Forecasting论文阅读
  • 揭阳网站建设解决方案太原注册公司流程
  • 第2节:程序逻辑与控制流——让程序“思考”
  • 别人网站 自己的二级域名国外家居创意空间设计
  • 东营科技官方网站网站开发入门书籍推荐
  • CSS Grid与Flexbox:2025年响应式布局终极指南
  • 在哪公司建设网站网站优化团队
  • 郑州汉狮哪家做网站好网页设计技巧
  • 【Jenkins 】配置从节点之后,环境配置
  • 如何防范恶意网站PHP手机网站开发工程师
  • Maven项目及Tomcat配置(IDEA)
  • 地方门户网站规划答题小程序开发教程
  • py day34 装饰器
  • 珠海市外贸网站建设公司中美军事的最新消息
  • 制作静态网站当前网站开发用什么软件
  • 天津制作网站wordpress能用手机管理吗
  • Redis面试