【Linux】进程地址空间


需要云服务器等云产品来学习Linux的同学可以移步/-->腾讯云<--/-->阿里云<--/-->华为云<--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。


 目录

一、虚拟地址

二、对进程地址空间的理解

三、32位下的进程地址空间

那么进程如何找到内存中的数据呢?

四、为什么要通过虚拟地址映射的方式访问物理地址


一、虚拟地址

先看一段父子进程共存的程序,由子进程对全局变量grobal_val进行修改:

#include <stdio.h>
#include <unistd.h>
int grobal_val=10;
int main()
{
    pid_t id=fork();
    if(id==0)
    {
        int cnt=0;
        while(1)
        {
            printf("子进程:pid=%d,ppid=%d | grobal_val=%d,&grobal_val=%p\n",getpid(),getppid(),grobal_val,&grobal_val);
            sleep(1);
            ++cnt;
            if(cnt==10)
            {
                grobal_val=200;
                printf("子进程已更改全局变量grobal_val\n");
            }
        }
    }
    else if(id>0)
    {
        while(1)
        {
            printf("父进程:pid=%d,ppid=%d | grobal_val=%d,&grobal_val=%p\n",getpid(),getppid(),grobal_val,&grobal_val);
            sleep(1);
        }
    }
    else 
    {
        printf("fork error\n");
        return 1;
    }
    return 0;
}

父子进程谁先执行不确定,由系统进行调度。

当子进程将全局变量grobal_val由10改为200,我们可以看到,父子进程的grobal_val的地址相同,但是父子进程从这个地址中获取的值却并不相同!

从同一块物理地址中取出的值是相同的,所以这个程序取出的地址(指针)并不是物理地址,而是虚拟地址(线性地址、逻辑地址)。注:逻辑地址指可执行程序编译完成后内部函数、变量的地址。逻辑地址有两种表示方法,一种是各个区域地址递增,另一种是每个区域的地址都从零偏移量开始(这种是比较老的表示方式)。

在Linux中的逻辑地址是第一种表示方式,所以Linux中逻辑地址就是虚拟地址。

之前学习的C/C++内存区域,是一块虚拟内存空间,每个进程有它自己的虚拟内存空间,即进程地址空间。所以上面的代码用fork创建子进程,因为子进程是父进程的拷贝,父子进程的grobal_val虽然虚拟地址一样,但会被映射到不同的物理地址上。

当grobal_val未被改变时,父子进程映射同一块grobal_val的物理地址,一旦父子进程的一方对共享数据进行修改,由于进程的独立性,操作系统会在物理内存中再开辟一块空间,并拷贝原数据,提出修改的进程的页表映射关系将会被改变,然后再让进程对数据进行修改,所以我们看到父子进程的数据并不一样。这种技术称为写时拷贝对不同进程的数据进行分离

二、对进程地址空间的理解

1、进程它自己会认为它独占CPU资源,但其实并不是。因为进程以时间片轮转的形式占用CPU资源,时间一到,马上从运行状态进入休眠状态,实质上是通过虚拟地址空间,让进程认为它独占CPU资源。

2、进程地址空间是操作系统给进程开辟的一块虚拟内存空间,这块空间用内核的一种数据结构来描述、组织。

操作系统给每个进程一块4GB的虚拟内存,进程每次想使用,按需申请即可,但不会全部给进程。(注意这里给的是虚拟内存,就像老板给员工画饼一样)

对Linux操作系统中进程的理解中提到过,进程使用进程控制块task_struct结构体进行管理,同样的,每个进程地址空间也需要被管理,管理进程地址空间的结构体叫mm_struct,task_struct中有一个指针指向自己的mm_struct。

mm_struct伪代码:

struct mm_struct
{
	uint32_t code_start,code_end;
	uint32_t data_start,data_end;
	uint32_t heap_start,heap_end;
	uint32_t stack_start,stack_end;
	······//存储进程地址空间各区域的起始位置
};

三、32位下的进程地址空间

地址空间中的最小单元是字节,所以在32位系统下共有2^(32)个地址空间,也就是4GB。

mm_struct结构体对象中存放各个区域的起始位置,栈区堆区的动态调整,本质上是修改各个区域的起始地址。

那么进程如何找到内存中的数据呢?

操作系统将进程中的虚拟地址通过页表映射到内存,找到对应的物理地址。

四、为什么要通过虚拟地址映射的方式访问物理地址

1、直接访问物理内存是非常不安全的,例如越界操作、恶意进程读取等。

2、页表会拦截不合理的请求,可以保护物理内存,防止恶意进程的访问 。

所以写代码出现野指针、内存越界等情况并不会造成操作系统的崩溃。

3、进程地址空间的存在,可以让进程和进程间的代码进行解耦(互不干扰),保证了进程独立性的特征。

4、进程和编译器均遵守进程地址空间这一套规则,编完即可使用。

编译器也遵守进程地址空间这一套规则:

我们的代码在磁盘时,程序的函数、变量等通过虚拟地址建立联系,满足程序间的互相跳转;

当程序由磁盘被加载到内存中时,就具备了物理地址。函数、变量等通过页表映射至虚拟地址。

根据可执行程序的虚拟地址初始化mm_struct结构体中每个虚拟内存中的边界。

当程序在CPU中跑起来时,CPU根据虚拟地址运行完程序后,通过页表映射至物理地址。

  • 30
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 28
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

蒋灵瑜

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

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

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

打赏作者

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

抵扣说明:

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

余额充值