进程地址空间(一)

news2024/12/26 11:14:27

目录

  • 1. 语言层面上的地址
  • 2. 引入新概念 ---- 地址空间的概念
  • 3. 进一步理解地址空间
  • 4. 为什么要有地址空间

在正式介绍进程地址空间之前,我们需要做一些铺垫,在父子进程同时运行时,从代码层面上的变量的地址,引入进程地址空间的概念,进一步理解之前关于进程的一些现象。

1. 语言层面上的地址

在这里插入图片描述

在学习 C/C++ 的时候,伙伴们肯定见过这张图,并且我们熟知,所谓的内存划分为栈区、堆区、全局变量区、常量区、代码区等区域。但是从这篇文章开始,往后讲进程地址空间,将会颠覆之前对 内存空间 或者 地址 的概念的认知!

接着,我们先来写一份代码。

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

int g_val = 100;

int main()
{
    printf("the value of the child and parent process are the same!\n");
    pid_t id = fork();
    if(id == 0)
    {
     	int cnt = 5;
        // 子进程
        while(1)
        {
            printf("i am child, pid : %d, ppid : %d, g_val: %d, &g_val: %p\n", getpid(), getppid(), g_val, &g_val);
            sleep(1);
            if(cnt) cnt--;
            else
            {
             	g_val=200;
                printf("the child process begin changing g_val : 100->200\n");
                sleep(1);
                cnt--;
            }
	}
    }
    else
    {
     	// 父进程
        while(1)
        {
            printf("i am parent, pid : %d, ppid : %d, g_val: %d, &g_val: %p\n", getpid(), getppid(), g_val, &g_val);
            sleep(1);
        }
    }
}

在这里插入图片描述

站在语言的层面上,出现了一个无法理解的认知,fork() 之后,父子进程同时运行,这没问题,曾经我们也说过,子进程在被创建出来的时候,大部分会以父进程为模板来初始化自己,并且父子进程共享同一份代码,数据上,采用 写时拷贝 的策略。而今天,在代码层面上,我们在子进程的范围内修改 g_val 这个变量,等同于子进程要修改父进程的数据了,所以系统会为子进程重新开辟一块空间,然后作为子进程独立的数据。

所以诡异的事情就出来了。豪嘛,在子进程需要修改父进程的数据时,你不是说系统会重新拿一块空间给子进程使用,然后让子进程在自己的空间上修改。但是我今天看到的好像并不是你说的这样!我看到的,父子进程对这个数据的地址空间是一模一样的!虽然我现在不知道为什么,同样的地址,它的值可以不一样。

没错!怎么可能同一个变量,同时读取的时候,读出了不一样的内容!?这是不可能的事的!

因为,我先抛出结论:如果变量的地址是物理地址,就不可能存在上面的现象,不然这绝对是一件无法理解的事情,所以我们代码层面上,打印出来的地址,绝对不是物理地址!!语言中展示出来的各种地址,我们称为 线性地址 或者 虚拟地址!


2. 引入新概念 ---- 地址空间的概念

在这里插入图片描述
初步认知:虚拟地址 与 物理地址 是通过页表这样的数据结构来映射的。

fork() 创建子进程时,子进程也会有独立的 task_struct ,然后基本以父进程为模板(除了一些 pid, ppid,优先级等信息)初始自己的 pcb 内核数据结构。再者,子进程也会从父进程拷贝一份进程地址空间 和 父进程的页表(每一个进程都会有自己的进程地址空间)。所以当子进程刚开始被创建出来,它的虚拟地址和页表内的数据一定是与父进程一样的。

所以在代码层面上打印出来的地址,明明是两个不一样的内容,但是指向的确实同一个地址空间,就是因为子进程的进程地址空间是拷贝父进程的,而能够出现两个不同的内容,是因为当子进程需要修改父进程的数据,操作系统就会先完成子进程的写时拷贝工作,然后修改页面表对应的映射关系(即修改映射关系中的物理地址,页面内的数据都是 键值对 的存在,所以也可以理解为,根据 拷贝自父进程的进程地址空间中的 key 值,修改其 value 值,让其映射到属于子进程独立的数据空间),这样之后,虽然父子进程的虚拟地址都是一个地址,但是经过页面映射到物理内存中,却是两个不一样的地址,因此才会出现两个不一样的内容!

进程概念(三)----- fork 初识 在这篇文章中,虽然我们理解了一个函数为什么会有两个返回值( fork() 之后父子进程共享代码,return 语句执行两次),也理解了为什么一个变量怎么做到两个不一样的内容(因为写时拷贝的问题,所以父子进程中的数据可能是不一样的)。所以现在,我们就能够理解,一个变量拥有两个不一样的内容,虽然变量名字是一样的,但底层是通过页面结构,虚拟地址映射到不同的物理地址,所指向的数据不同来实现的,而这是操作系统为进程做的工作!


3. 进一步理解地址空间

上面我们只是见过了地址空间,但现在我们依旧不理解什么是地址空间,地址空间上的区域划分又该如何理解。

我们知道的是,计算机只认识二进制,并不认识什么代码。但在理解地址空间之前,我们不仅要知道,计算机只认识 0 和 1,这件事是站在软件的层面上讲的;站在硬件层面,只有所谓的 高电平(充电) 和 低电平(放电),最终在软件层面上都会被计算机解读成 1 和 0。

在32位计算机上,有32位的地址和数据总线(cpu 和 内存数据交互的是系统总线,内存和外设的是 IO 总线),而 cpu 在内存中寻址的时候,靠的就是这32位地址线,通过对触发器的充放电,就可以实现对某一处地址空间的定位,然后做数据交互。换言之,我们常说的数据拷贝,无非就是一个设备在对另一个设备充放电的过程。

而地址总线有32位,每一位可以是0 或 1,所以一共可以有 2^32 种组合,内存的大小 = 2 ^32 * 1byte = 4GB!

所以什么是地址空间? ---- 地址总线排列组合形成的地址范围 [0, 2^32-1],就叫做地址空间!

那么现在大家就应该清楚,为什么一台32位的机器,它的内存最多就是4GB,因为它的寻址范围就不允许它有更大的内存空间,它的地址总线最多就只能 [0, 2^32-1] 在这么大的范围内寻址。

而地址空间上的区域划分可以怎么理解?

讲个故事,现有小帅、小美两个小学生,教室的课桌又不是特别的大,大家的空间都非常有限,因此为了公平保证,小帅和小美划分了三八线,一人一半,不得越界!而这种行为的本质就是 区域划分!。一人一半之后,小帅和小美总得对自己的区域进行管理使用,那他们是怎么管理自己的区域的呢?

还是一样,只要是管理,就是 “先描述,再组织” !

struct area								struct desktop_area		// 约定最大范围100cm
{										{
	int start;								struct area xiaoshuai;
	int end									struct area xiaomei;
}										}

所以今天小帅和小美想要对自己的区域范围做管理,只需要 struct desktop_area line_area = {{1,50}, {51,100}}

但是有一天,小帅不小心就越界了,还把小美桌上摆放整齐的各种文具弄乱了。于是小美很生气,不仅把小帅揍了一顿,还多占了小帅 10cm 的区域!

而上述这个行为的本质,就是空间区域的调整(变大或者变小),而用计算机语音进行描述,那就是 line_area.xiaoshuai.end -= 10; line_area.xiaomei.start -= 10; 这样,小帅的区域范围就变为 [1, 40] ,小美变成 [41, 100]。

小帅在自己的区域空间内,1 ~ 40cm 处的空间中的任何一厘米,小帅都可以自由使用!小帅可以决定 2-3 cm处用来放铅笔,4-5cm处用来放橡皮擦。所以我们不仅要看到小帅划分的地址空间的范围是多大,还要能看到范围内具体的某个地址!换言之,在连续的空间范围内,每一个最小单位都可以有地址,而这个地址可以被小帅所使用!

因此,所谓的进程地址空间,本质就是一个描述进程可视范围的大小。地址空间内一定要存在各种区域划分,对线性地址(虚拟地址)规定 start 和 end !

对于一个进程,操作系统不仅要为其创建一个 pcb 内核数据结构,同样也要为其创建一个进程地址空间的结构体,所以地址空间本质也是内核的一个数据结构对象,类似 PCB 一样,地址空间也是要被操作系统管理的:先描述,再组织!

struct mm_struct			// 32位机器下,默认划分的区域为 4GB
{
	unsinged long code_start;
	unsinged long code_end; 
	
	unsinged long readonly_start;
	unsinged long readonly_end; 
	
	unsinged long init_start;
	unsinged long init_end; 
	
	unsinged long uninit_start;
	unsinged long uninit_end; 
	
	unsinged long heap_start;
	unsinged long heap_end;

	unsinged long stack_start;
	unsinged long stack_end;
	
	......
	......
}

这就是我们讲的 进程地址空间 的结构体。对每一个进程来说,都要有一个 PCB,也要有一个进程地址空间,并且在 pcb 内部要有指向进程地址空间的指针。而面对进程地址空间划分的4GB,我们不仅仅要看到这个范围的大小,其内部的各个区域(100 - 2000),我们同样的也要能够细化到具体某一个地址处,换言之,在一段连续空间范围内,每一个最小单位都可以有地址,并且这个地址是可以被使用的!

到现在,我们应该要知道为什么操作系统总能够准确的发现我们代码中的越界。当我们在向某一处地址写入数据时,操作系统都会对该地址进行校验,如果发现这个地址落在常量代码区(这种不可写入的区域),就会对你写入的操作进行拦截!


4. 为什么要有地址空间

我们现在已经对地址空间有了初步认识,但为什么要有地址空间呢!?

由于篇幅过长等问题,关于进程地址空间更多的介绍,请跳转至 进程地址空间(二)。



如果感觉该篇文章给你带来了收获,可以 点赞👍 + 收藏⭐️ + 关注➕ 支持一下!

感谢各位观看!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2048543.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2.1算法的时间复杂度与空间复杂度

本篇博客介绍算法的时间复杂度与空间复杂度 一、算法效率 算法好坏从时间和空间两个维度衡量 二、时间复杂度 1&#xff0e;概念 时间复杂度是算法中基本操作的执行次数&#xff0c;定量描述了算法的运行时间 2&#xff0e;注意 &#xff08;1&#xff09;时间复杂度是偏…

vue项目中引入字体文件样式

需求:关于一些样式需要自定义的,所以需要ui提供字体文件,然后引入项目中,就可实现自定义 首先看一下实现效果图: 第一步:新建一个字体样式文件用于放字体文件和css样式 font.css文件: /* 数字特殊字体 */ font-face {/*给字体命名*/font-family: DINCondBold;/*引入字体文件*…

课堂助手小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;学生管理&#xff0c;教师管理&#xff0c;专业信息管理&#xff0c;部门信息管理&#xff0c;课程信息管理&#xff0c;教学内容管理&#xff0c;学生作业管理&#xff0c;系统管理 微信端账号功能包…

防止内存泄漏的神兵利器 — 智能指针

1.内存泄漏 1.1什么是内存泄漏 当我们在写C/C程序的时候&#xff0c;难免会出现内存泄漏的错误&#xff0c;因为C/C不像Java语言那样&#xff0c;拥有自己的垃圾回收机制&#xff0c;C/C中对于资源的管理&#xff0c;完全交给程序员自己打理&#xff0c;也就是说使用C/C的程序…

Ubuntu下NFS和SSH服务

本篇文章记录Ubuntu下如何对NFS和SSH服务进行配置和开启。 目录 一、NFS服务 二、SSH服务 1、安装SSH服务 2. 启动和检查SSH服务 3. 配置SSH服务 4. 连接到SSH服务 5. 设置防火墙 6. 测试连接 三、结语 一、NFS服务 NFS&#xff08;Network File System&#xff0…

设计模式(2)行为型模式和七大原则

1、目标 本文的主要目标是学习设计模式的行为型模式并举例说明 2、行为型模式 2.1 观察者模式&#xff08;Observer&#xff09; 观察者模式是对象之间存在一对多的依赖关系&#xff0c;当一个对象的状态发生变化时&#xff0c;所有依赖它的对象都会得到通知并自动更新&…

京东数据编织

计算引擎是Hbase 中间计算结果的物化【就是存下来】 自动物化 在这里插入图片描述

设计模式在芯片验证中的应用——状态

一、状态模式 状态模式是一种行为设计模式&#xff0c; 让你能在一个对象的内部状态变化时改变其行为&#xff0c; 使其看上去就像改变了自身所属的类一样。 在RTL中可能存在复杂的有限状态机FSM&#xff0c;在任何一个特定状态中&#xff0c; RTL的行为都不相同&#xff0c;…

pip install 遇到ValueError: check_hostname requires server_hostname的解决办法

我需要下载Cython来将py编译成c&#xff0c;结果在pip install的时候报错这个&#xff1a; ERROR: Exception: Traceback (most recent call last):File "F:\Anaconda3\envs\DouyinLive32\lib\site-packages\pip\_internal\cli\base_command.py", line 173, in _mai…

《人类群星闪耀时》

人类群星闪耀时&#xff0c;历史的舞台上&#xff0c;你未尝不是其中一颗。 【拜占庭的沦陷】具备决断力、创新力、执行力的领导者起到关键作用。 【享德尔的复活】人的心力一旦强大&#xff0c;便可创造非凡之事。 【一夜天才】闪耀的星离不开黑夜的衬托。所谓的英雄&#x…

HarmonyOS Developer之生成二维码

qrcode 生成并显示二维码 属性 样式 创建qrcode组件 在pages/index目录下的hml文件中创建一个qrcode组件 HTML <!-- xxx.hml--> <div class"container"><qrcode value"Hello"></qrcode> </div>CSS /* xxx.css */ .cont…

uniapp 微信小程序生成水印图片

效果 源码 <template><view style"overflow: hidden;"><camera device-position"back" flash"auto" class"camera"><cover-view class"text-white padding water-mark"><cover-view class"…

基于JAVA的社团管理系统的设计与实现

TOC springboot270基于JAVA的社团管理系统的设计与实现 第1章 绪论 1.1 课题背景 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供…

html+css 实现hover 边框彩色按钮

前言:哈喽,大家好,今天给大家分享html+css 绚丽效果!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 一、效果二、原理解析1.这是一个,hover后按钮边框==变彩色==的效果。每个按钮都是1个but…

matlab关于结构体的创建

网上搜了很多结构体的创建&#xff0c;都是什么student.name student.age,或者struct. 自己摸索了一下&#xff0c;根据你想要创建结构体的格式可以划分以下两类 clear all clcdata [1, 2, 2, 1;2, 1, 1, 3;4, 3, 2, 2];data1 [0, 2, 2, 1;0, 1, 1, 3;4, 3, 0, 2;4, 3, 0, 2…

解锁企业数字化转型的终极指南:《数字时代的敏捷架构》深度解读

在当前数字化浪潮的冲击下&#xff0c;企业面临着前所未有的挑战与机遇。为了解决这一难题&#xff0c;The Open Group 和 AZone 联手推出了《数字时代的敏捷架构》&#xff0c;《数字时代的敏捷架构》汇集了全球顶尖专家的智慧和经验&#xff0c;是企业数字化转型的必读之作。…

【IEEE独立出版】第四届人工智能、虚拟现实与可视化国际学术会议(AIVRV 2024)

第四届人工智能、虚拟现实与可视化国际学术会议&#xff08;AIVRV 2024&#xff09; 2024 4th International Conference on Artificial Intelligence, Virtual Reality and Visualization 会议时间&#xff1a;2024年11月1日-3日 会议地点&#xff1a;中国-南京…

C语言第20天笔记

文件操作 概述 什么是 文件 文件时保存在外存储器上&#xff08;一般代指磁盘&#xff0c;也可以是U盘、移动硬盘等&#xff09;的数据的集合。 文件操作体现在哪几个方面 1. 文件内容的读取 2. 文件内容的写入 数据的读取和写入可被视为针对文件进行输入和输出的操作&a…

函数递归那些事

什么是递归 递归就是函数自己调用自己&#xff0c;而递归的本质其实是一种解决问题的方法。 递归的思想 递归的思想是把复杂问题大事化小的过程。即把一个大型复杂的问题不断的拆分成与原问题相似&#xff0c;但规模较小的子问题&#xff0c;直到子问题不能被拆分&#xff0…

Python学习day16-类与对象

这里写目录标题 类示例 成员方法self关键字 类与对象构造方法其他类内置方法&#xff08;魔术方法&#xff09;_str_符号_Lt_符号le小于等于比较eq比较运算小结 类 在Python中&#xff0c;class&#xff08;类&#xff09;是一种用于创建对象的模板或蓝图。它封装了数据&#…