[Linux] 进程地址空间

1.程序地址空间铺垫(以32位为例)

a.粗力度验证上面的规则:

        

 这时候我们就可以看出地址由低到高

b.地址有增长方向的验证

 栈区变量怎么办?

  • 天然的*heap等这些就是天然的指针变量在栈上的, 堆 上开辟一块空间,由栈上变量指向他
  • 栈向下(地址空间减少的方向)增长
  • 堆向上增长

代码验证: 

不难看出堆是向上增长的,栈是向下增长的.

结论:堆栈相对而生.

c.命令行参数和环境变量的验证

核心代码:

 

不难看出

  • 命令行参数整体比栈区的大, 环境变量的表又比命令行参数的大
  • 先有命令行参数表才有环境变量表

d.环境变量字符串和命令行字符串指向哪里

我们打的是表里面的字符串的地址,也就是表里的内容

 可以看出

  • 无论是表,还是表指向的项目,都在栈上部。
  • 未初始化变量和初始化变量会在我们进程运行期间,一直存在。

e.全局变量又是在哪个位置呢? 

static变量已经被当做全局变量了 


2 .这张图是物理内存吗?

 先看一个奇怪的现象

核心代码:

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

int g_val = 100;

int main()
{
  pid_t id = fork();
  if (id == 0)
  {
    //child
    int cnt = 0;
    while (1)
    {
      printf("child,  pid: %d, ppid: %d, g_val: %d, &g_val: %p\n", getpid(), getppid(), g_val, &g_val);
      sleep(1);
      cnt++;
      if (cnt == 5)
      {
        g_val = 200;
        printf("child change g_val: 100 -> 200\n");
      }
    }
  }
  else 
  {
    while (1)
    {
      printf("father, pid: %d, ppid: %d, g_val: %d, &g_val: %p\n", getpid(), getppid(), g_val, &g_val);
      sleep(1);
    }
  }

  return 0;
}

运行结果:

我们不难发现内容不一样但是地址一样, 所以我们可以知道这个地址绝对不能是物理地址,

其实这个地址叫做:虚拟地址/线性地址

结论: 平时我们用到的所有地址,全都都不是物理地址.

所以我们最开始放的那张图不是物理内存,正确的叫法:进程地址空间.


3.进程地址空间

先说结论: 

1. 每个进程都会有自己的PCB(task_struct), 也会有所对应的地址空间.

2. 地址空间和物理内存之间有个映射表 

  • 左侧虚拟地址到右侧的物理地址建立映射关系

 整个过程: 子进程和父进程都有自己的表,子进程拷贝父进程的表类似于浅拷贝. 然后g_val值从100 -> 200也就是子进程开始改了,因为进程具有独立性的把原始的表拷贝一份,修改一下子进程的映射关系,不能直接修改父进程的数据,通过不同的映射关系找到不同的物理内存所以他们值是不一样的。每个进程都有自己的映射关系。操作系统会帮我们处理.

所以我们之前用if else进行分流,一个id怎么可能即>0 又等于0? 今天就能理解了父子进程执行不同的代码块时,id所映射到的是不同的物理内存变量,所以虽然虚拟地址一样但是,映射关系不同,被映射到了不同的物理内存,所以读到的物理值就不一样. 这就是为什么if else可以进行分流,同时成立的根本原因.

我们说的这么一大堆在哪里呢? 操作系统内部

 而我们在哪里呢? 在上面,所以底层这一堆我们就不太清楚了

关键问题在于这些是什么?


第一个是地址空间,问题来了

什么是地址空间呢? 

每一个进程,都会存在一个进程地址空间。32[0, 4GB]

小故事:剧情大概就是,大富翁有10个亿,有四个儿子,和每个儿子说你好好努力将来把10个亿给你.

在这里面,10个亿就是物理内存, 大富翁就是操作系统,饼就是进程空间地址,每个儿子就是进程.

这里可以引出地址空间的本质:就是操作系统给每个进程画的一张大饼,人均一个.

饼要管理吗?即操作系统要不要对地址空间做管理?

先描述,再组织.

什么是进程地址空间? 

数据结构,具体到进程中,就是特定的数据结构对象.

在操作系统内部就是数据结构之间的关系. 

进程地址空间属性有哪些?

如何理解各个区域?

故事2: 

大家上小学的时候,都是两人一组的坐,这个时候都会出现同桌文具什么的到自己的这边来了,占用了自己的位置,这时候就会画界限,谁都不能越界.

小A,小B画界限的目的是什么呢?

区域划分.

如何用计算机的语言描述小B的动作呢?

这个时候小B怎么判断小A越界了呢?

小A文具和在60 的位置,在属于小B的区域. 

小B为了惩罚小A,小B扩大了区域划分(伪代码看个理解就行了)

结论:

1.区域划分可以判断是否越界

2.区域划分可以扩大或者缩小范围

如果让你设计进程的地址空间,该地址空间中必定有的字段是什么? 

用整数描述的各个区域,包含区域的开始和结束。

可以再内核里边看,有一个指针指向内核进程地址空间.

区域划分的本质是,区域内的各个地址,都可以使用。

我们的地址空间,不具备对我们的代码和数据的保存能力! 也就是在物理内存中存放!

将地址空间上的地址(虚拟/线性)转化到 物理内存中!

给我们的进程提供一张映射表 -- 页表

意思就是:这个是我们进程PCB的大概流程

虚拟地址到物理地址经过页表转换到物理内存就可以访问它了.

这个转换过程是谁来做的?

CPU中一个叫MMU的硬件自动完成

CR3保存的是物理地址,虚拟地址是给进程的 -- 用户的

地址空间里的指针也可以找到页表

为什么要有地址空间+页表?

左边那一块叫进程管理,右边的一块叫内存管理.内存管理释放不需要考虑进程管理. 所以放在物理内存的程序代码位置会很乱,但是我不怕因为有页表, 经过页表映射将在物理内存不怎么连续的代码统一映射同一个区域当中.也就是将物理内存从无序变有序,让进程以统一的视角,看待内存.

双方因为页表的存在,几乎可以做到互不干扰.甚至进度调度的时候你不用我都可以不给你开空间. 通过页表将进程管理和内存管理进行解耦合.


写了这么多到这里暂时结束了,谢谢大家的观看.

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

No more cages

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值