我是靠谱客的博主 勤奋灯泡,最近开发中收集的这篇文章主要介绍进程地址空间(跑路人笔记)前言铺垫正文好处,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

  • 前言
  • 铺垫
  • 正文
    • g_val问题解决
    • mm_struct
    • 页表
  • 好处

前言

好久没写博客了,手可能会有些生疏。语言表达如果有问题还请指出。

本文章讲进程地址空间的概念。

铺垫

我们之前学过地址的空间分布

地址空间图:

image-20221004112522673

但是根据我们刚刚学过的(进程概念(跑路人笔记)的部分我们知道我们的操作系统有多个进程需要管理,难道我们所有的进程都是在同一个区域遵循这种规则的吗?我们先来看一段代码及运行结果。

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
int g_val = 0;
int main()
{
    pid_t id = fork();//创建子进程的函数,父进程的返回值为子进程pid,子进程为0
    if(id<0)
    {
        perror("fork");
        return 0;
    }
    else if(id == 0)
    {
        printf("子进程的g_val = %d g_val地址%pn",g_val,&g_val);
    }
    else if(id>0)
    {
        printf("父进程的g_val = %d g_val地址%pn",g_val,&g_val);
    }
    return 0;
}

image-20221004114349505

合情合理我们的父子进程共用了一块区域地址和值都是相同的,但是当我们更改了父进程的g_val的值会发生什么呢?

我们对代码稍加修改

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
int g_val = 0;
int main()
{
    pid_t id = fork();//创建子进程的函数,父进程的返回值为子进程pid,子进程为0
    if(id<0)
    {
        perror("fork");
        return 0;
    }
    else if(id == 0)
    {
        
        printf("子进程的g_val = %d g_val地址%pn",g_val,&g_val);
    }
    else if(id>0)
    {
        g_val = 100;
        printf("我对g_val进行了修改修改成了100请注意查收n");
        printf("父进程的g_val = %d g_val地址%pn",g_val,&g_val);
    }
    return 0;
}

image-20221004125035040

可以看出我们子进程和父进程的g_val这个变量他的地址是相同的但是值却不同???

这是为什么呢?这就是我本篇文章要讲解的重点.

正文

我要知道什么是进程地址空间

首先进程地址空间不是内存! 每个进程在启动的时候都会让操作系统给他创建一个属于他的地址空间,该地址空间就是进程地址空间

解释一下进程地址空间不是内存,我们在[铺垫](# 铺垫)里的那张图并不是我们内存的空间结构,而只是我们进程地址的结构.

g_val问题解决

我们来解决一下铺垫中的父子进程g_val的问题.

其实这个问题也好解决及虚拟地址:

我们的C/C++中使用的地址都是虚拟地址(也叫线性地址,逻辑地址).

每个进程在启动的时候都会让操作系统给他创建一个属于他的地址空间,该地址空间就是进程地址空间.

进程地址空间是进程独立性的表现.及让每个进程都认为是自己独占了系统上的所有资源

mm_struct

然后每个进程都会在tast_struct中保存一个mm_struct(一个结构体)这个结构体结构大体如下:

struct mm_struct
{
    long code_start;
    long code_end;
    
    long init_start;
    long init_end;
    
    long heap_start;
    long heap_endl;
    //.......
}

这个结构体就是保存每个区域的起始和结束位置.(比如堆的区域栈的区域起始和结束位置.)

这样我们每个进程就可以互不打扰了.所以哪怕父子进程的变量地址相同也不会影响到对方.

但是我们只使用虚拟地址而没有真实地址肯定是不行的.

页表

所以我们就有了页表的概念:

页表也是每个进程都有的,页表是我们通过虚拟地址找到真实地址的一种方法.

我们可以具体的将页表想象成一个表格类似下图:

image-20221004123655825

因为页表的存在所以我们得到的进程的地址其实都是虚拟地址而不是真实地址,所以我们父子进程才会出现相同的地址不同的值的情况。

我们整体的进程地址空间大致如下图:

image-20221004124443611

好处

我们这样做的好处是什么呢?

  1. 安全性问题(3.11)
    对访问内存增加了一层软件层,可以对转化过程进行审核,非法的访问,就可以直接拦截了。

比如我们有个野指针,他如果直接指向真实地址的话,万一他不行的指向了操作系统的内容并将其修改很有可能产生极大的问题,但是当他是虚拟地址的时候,我们就可以在他转化的时候对他进行审核和拦截,拒绝此类非法的访问。

  1. 进程管理 Linux内存管理。(3.19)

当你一次申请的巨大的堆空间但是暂时不使用我们的操作系统就会将不使用的空间暂时交给其他进程使用当你需要使用的时候再重新给你申请供你使用。

在进程申请了但是还不使用的时候因为又页表的存在所以我们可以先先申请及将mm_struct中的区域数据扩大,及给了你使用的权力但是既然你先不使用,操作系统就可以先将你申请但是不使用的区域给其他进程。
及当你用的时候操作系统再给你申请。

  1. 让进程或程序可以 以统一的视角看待内存。
    方便统一的方式编译和加载所有的进程空间从而简化进程本身的设计。

    当所有的进程结构都相同的时候我们操作系统的管理就可以变的相对简单。

最后

以上就是勤奋灯泡为你收集整理的进程地址空间(跑路人笔记)前言铺垫正文好处的全部内容,希望文章能够帮你解决进程地址空间(跑路人笔记)前言铺垫正文好处所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(42)

评论列表共有 0 条评论

立即
投稿
返回
顶部