进程地址空间

验证地址空间的基本排布

#include <stdio.h>
   #include <stdlib.h>
   //初始化变量
   int g_val = 0;
   //未初始化变量
   int g_unval;
   int main(int argc,char* argv[],char* env[])
   {
     // 以main函数为代表,打印代码区
    printf("code addr : %p\n",main);
    //初始化数据
    printf("val addr : %p\n",&g_val);
    //未初始化数据
    printf("unval addr : %p\n",&g_unval);
  
    char* mem = (char*) malloc(10);
  
    //堆
    printf("heap addr : %p\n",mem);
  
    //栈:指针变量(函数内,局部变量)
    printf("stack addr : %p\n ",&mem);
  
    //第一个命令行参数                                                                                                                         
    printf("opt addr : %p\n",argv[0]);
  
    //最后一个命令行参数
    printf("opt addr : %p\n",argv[argc - 1]);
    //环境变量
    printf("env addr : %p\n",env[0]);
    
    return 0;
  }

 可以看到代码区的地址是最小的,依次向上递增,其中,堆栈相对而生。栈向地址减小方向生长,堆向地址增大方向生长。堆栈之间的区域称为共享区
代码段不是从0号地址开始的,它有一个确定的地址
共享区里一般放的是动态库,共享内存等

进程地址空间

虚拟地址

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()

{
  pid_t ret = fork();
  int i = 0;
  if(ret < 0)
  {
    perror("fork");
    return 0;
  }
  else if(ret == 0)
  {
    //child
    i = 100;
    printf("child[%d] : %d : %p\n",getpid(),i ,&i);
  }
  else
  {
    //parent
    printf("parent[%d] : %d : %p\n",getppid(),i,&i);
  }
  return 0;
}

 我们发现,父子进程,输出地址是一致的,但是变量内容不一样

变量内容不一样,所以父子进程输出的变量绝对不是同一个变量

但地址值是一样的,说明,该地址绝对不是物理地址

 在Linux地址下,这种地址叫做 虚拟地址

在创建子进程且子进程没有改写全局变量时,因为没有进行写入,OS本质不允许浪费一点空间的原则,父子进程共享这段空间,但是,一旦有任何一方进行写入,父子进程就要进行数据独立,单独给修改一方开辟一块空间,重新构建一种映射关系,整个操作过程中,父进程没有收到任何影响,而对子进程来讲,只是在物理内存中重新开辟了空间,本身的虚拟地址并没有发生变化,改变的只是虚拟地址与物理地址的映射关系

我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理,OS必须负责将 虚拟地址 转化成 物理地址

 

 灵魂三问理解地址空间

1.是什么?

地址空间的本质是所占有空间的一张表,,在操作系统内核当中,就是一个数据结构,将内存划分成了若干种区域

每个进程都有一个进程的地址空间,系统会存在多个进程,所以,也一定存在着多个地址空间,那么操作系统也需要将这些地址空间管理起来

只要是管理,就要遵循先描述,再组织

先描述

上面也说了地址空间本身就是一个数据结构

在Linux当中,叫做struct mm_struct(linux内核当中的地址空间结构体)
它包含了一些区域信息,能够实现区域划分

struct mm_struct
{
    //区域划分
    Unsigned long code_start;
	Unsigned long code_end;
	
    Unsigned long init_data_start;
	Unsigned long init_data_end;

	Unsigned long uninit_start;
	Unsigned long uninit_end;
	
	Unsigned long head_start;
	Unsigned long head_end;
	....

}

通过大量的起始地址和结束地址来划分区域

申请空间的本质:

向内存索要空间,得到物理地址。然后在特定区域申请没有被使用的虚拟地址,通过页表建立映射关系,返回虚拟地址

在向OS申请内存时,OS不会立马给你,在申请成功后,和在使用之前,就有一段小小的时间窗口,在这个时间段中,这个空间没有被正常使用,但是别人也用不了,这种转态叫做闲置状态!

再组织:

在进程的task_struct里有mm_struct的指针

mm_struct相当于task_struct的一张执行地图

我们知道一个进程 = PCB + 代码数据
因此,就把进程的PCB和mm_struct关联起来了

2.为什么

地址空间存在的意义是什么呢?

假设如果没有地址空间,那么进程访问的地址都是物理地址

若发生非法访问或异常时,其他空间的代码和数据就可能会遭到破坏,同时,也会存在一个进程的数据存放的位置是不连续的,访问起来特别不方便

增加了虚拟地址空间后

1.可以防止地址的随意访问,保护物理内存和其他地址

2.将进程管理和内存解耦合(解除两者或多着相互之间的影响,增强各自的独立性)

3.可以让进程以同一的视角,看待自己的代码和数据

3.怎么做

地址空间是如何工作的

地址空间上所呈现暴露给上层的所有的地址都叫做虚拟地址,而实际进程访问时,要通过页表映射转换到物理内存,然后拿到对应的代码和数据

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值