首页 > 系统相关 >【Linux进程详解】进程地址空间

【Linux进程详解】进程地址空间

时间:2024-09-10 21:20:14浏览次数:12  
标签:val 空间 物理地址 地址 详解 内存 Linux 进程

目录

1.直接写代码看现象

2.引入最基本的理解

3.细节问题-理解它


1.直接写代码看现象

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <unistd.h>
int g_val = 100;

int main()
{
    printf("father is running, pid: %d, ppid: %d\n", getpid(), getppid());


    pid_t id = fork();
    if(id == 0)
    {
        //child
        int cnt = 0;
        while(1)
        {
            printf("I am child process, 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 = 300;
                printf("I am child process, change %d -> %d\n", 100, 300);
            }
        }
    }
    else
    {
        //father
        while(1)
        {
            printf("I am father process, pid: %d, ppid: %d. g_val: %d, &g_val: %p\n", getpid(), getppid(), g_val, &g_val);
            sleep(1);
        }
    }
}

并且我们还能观察到,从一开始父子进程便是相同的

由此可推出,我们打印出来的地址绝对不是物理地址,而是虚拟地址,故我们在C/C++中看到的地址是虚拟地址,由操作系统负责转化为物理地址

关于地址,从低地址到高地址存储的依次是:代码段、初始化全局数据区、未初始化全局数据区、堆区、栈区、命令行参数与环境变量。其中,堆区的空间是从小到大增长的,而栈区的空间是从大到小增长的

其中在32位机器中,使用32位(比特位)来表示一个地址,这意味着它可以表示 2^32个不同的地址,也就是4GB;在64位机器中,使用64位(比特位)来表示一个地址,它可以表示 2^64 个不同的地址,也就是16EB

需要注意的是,这里的EB指的是艾字节(Exabyte),1EB等于 1,0241,024 PB(拍字节),而1PB等于 1,0241,024 TB(太字节),1TB等于 1,0241,024 GB(千兆字节),1GB等于 1,0241,024 MB(兆字节),1MB等于 1,0241,024 KB(千字节),1KB等于 1,0241,024 字节。因此,16EB是一个非常巨大的数字,远远超过了当前大多数应用场景的需求。

2.引入最基本的理解

我们在回到刚刚代码示例那里,当子进程修改g_val的值时,为了保证进程之间的独立性(也即是说,子进程的数值修改不应该影响父进程),此时就会发生写时拷贝,会给子进程的g_val开辟独立的物理地址空间,而不是与父进程共享同一块空间,通过观察我们发现,代码依旧共享同一块空间,只是数据区不同了而已

 

3.细节问题-理解它

  • 什么叫做地址空间?

在32位机器下,数据与地址总共32根线,每根数据与地址线可以产生充电和放电两种状态,即产生0或1。因此地址总线排列组合形成地址范围为[0, 2^32 ],这就是地址空间

  • 如何理解地址空间上的区域划分

【示例】小学生划分38线

张三和李四是同桌,桌子共长200cm,他们约定每人100cm。即课桌划分为[0,100], [101,200]这两个区域划分,如果要记录区域的结果,我们就需要先描述再组织

struct desktop{
  int zhangsan_start;
  int zhangsan_end;
  int lisi_start;
  int lisi_end;
};

操作系统为每个进程创建了进程地址空间和mm_struct,用于记录每个进程的各个区域的起始位置和结束位置。在已经被分配给某进程的空间范围内,该进程可以随意使用和访问

  • 为什么要有进程地址空间?

【示例】大富翁的3个私生子

远在几千万千里的大富翁有三个私生子,3个私生子互相不知道对方的存在。这个大富翁对每一个私生子说,我有100亿,等我某一天驾鹤西去,这100亿都是你的。当某个儿子有需求时,他像父亲申请10w,此时大富翁就给他10w。但如果申请100亿,可能无法成功(因为有一部分被大富翁其他私生子占用了),但他并不会觉得这100亿不是他的,而是觉得自己申请的太多了

 

而这里的大富翁等同于操作系统,而这三个私生子等同于系统上的进程。操作系统有4GB的内存空间,进程坚信自己拥有操作系统的全部空间,但通常情况下,进程并不会申请过大的空间。

 

因此:1️⃣将无序变成有序,让进程以统一视角看待物理内存以及自己运行的各个区域,

在操作系统中,虚拟内存是一种内存管理功能,它让每个进程都认为自己拥有连续的、独立的地址空间,即使物理内存可能是不连续的,并且被多个进程共享。这样,每个进程都感觉自己拥有整个内存,而实际上它们是在操作不同的物理内存区域。这种抽象提供了以下几个好处:

因此:1️⃣将无序变成有序,让进程以统一视角看待物理内存以及自己运行的各个区域,

在操作系统中,虚拟内存是一种内存管理功能,它让每个进程都认为自己拥有连续的、独立的地址空间,即使物理内存可能是不连续的,并且被多个进程共享。这样,每个进程都感觉自己拥有整个内存,而实际上它们是在操作不同的物理内存区域。这种抽象提供了以下几个好处:

进程隔离:每个进程都不能访问其他进程的内存,这增强了系统的稳定性和安全性。

内存保护:操作系统可以设置权限,防止进程访问它不应该访问的内存区域。

内存扩展:通过虚拟内存,系统可以使用硬盘空间作为临时的内存使用,即交换空间(swap space),从而扩展可用内存的大小。

【示例2】:压岁钱的管理

小明新年获得了200块压岁钱,母亲担心小明会乱花钱,于是和小明说“你的压岁钱由我来保管,你需要买什么就和我说,我在从这里给你钱”。于是有一天小明要买一款游戏机150元,找妈妈去要,结果妈妈说“游戏机,会害了你的学习,不给买”。再一次小明要去买学习资料,向妈妈申请50块钱,妈妈同意了。因此,新增一个人,作为中间层,可以对非法请求进行拦截

因此:在操作系统中,页表除了包含虚拟地址到物理地址的映射关系,还记录了该区域的读写权限。当用户对其已申请空间做了超出读写权限外的操作,则会被操作系统识别到,并终止该进程。

也就是2️⃣操作系统会拦截非法请求-->对物理内存进行保护

注意:物理地址本身没有读写权限,我们在语言中的const等限制某个地址空间的读写权限,本质是在页表中添加读写权限

如果直接使用物理地址而不是虚拟地址,当我们对也指针进行访问时,由于物理地址没有读写权限控制,导致我们修改了其他进程的数据,破坏了进程的独立性。因此,使用虚拟地址+页表的方式可以保证进程的独立性

在操作细则中,由于内存空间十分宝贵,进程中的代码和数据不一定会被全部加载到内存,因此页表中还有一个字段,表示虚拟地址指向的代码和数据是否在磁盘上。如果虚拟地址映射物理地址时,发现该数据或代码位于磁盘上,则会引发页中断(即当前页表无法映射),此时系统在将对应的代码和数据加载到内存中

★ 惰性加载是一种按需加载资源(如图片、视频、脚本或其他数据)的策略,仅在真正需要时才进行加载。这与预加载(Preloading)相反,预加载是在页面加载时提前加载所有可能需要的资源。

同时,如果因为内存资源紧张,可能会将某个进程挂起,即将它的代码和数据先保存到磁盘中,待内存资源不紧张时再重新加载,但重新加载后的物理地址可能与之前的物理地址不再相同。假设进程没有使用虚拟地址空间+页表映射的方式,则每次将进程代码和数据加载到内存就需要改动PCB到地址空间内容,而不是修改页表的内容。这样将使得进程管理与内存管理耦合度过高。同时,物理内存中几乎所有的数据和代码都是乱序的,由于页表的存在,它可以将物理地址和虚拟地址进行映射,在进程视角,可以将内存分布有序化

因此:3️⃣因为有地址空间和页表的存在,将进程管理模块和内存管理模块进行了解耦合

此时我们也可以知道C/C++申请的地址是虚拟地址。

此时我们要重新定义一下我们对进程的概念进程=内核数据结构(PCB+页表+进程地址空间)+代码和数据

 

标签:val,空间,物理地址,地址,详解,内存,Linux,进程
From: https://blog.csdn.net/2202_75331338/article/details/142109553

相关文章

  • 【整理】虚拟地址全解析:操作系统内存管理与进程调度的深度揭秘!
    原创freedom47概述在现代计算机系统中,虚拟地址是内存管理的关键组成部分。虚拟地址不仅帮助操作系统高效地管理物理内存,还在进程的内存分配中发挥重要作用。本文将详细介绍虚拟地址的定义、作用、操作系统的内存管理、进程内存分配、32位与64位架构的内存分配差异,以及物......
  • 【AI绘画】Midjourney光影控制详解
    博客主页:[小ᶻZ࿆]本文专栏:AI绘画|Midjourney文章目录......
  • select函数详解:IO复用
    select函数概述select函数是一种用于实现I/O复用的方法,它可以让程序在多个文件描述符(例如套接字)之间进行选择,以便在其中任何一个或多个可用时执行I/O操作。这种机制使得程序能够更高效地处理多个I/O操作。下面将对select的原理和工作机制进行详细介绍,并分析select函数的优势和......
  • Linux命令实践
    Linux命令实践学习Linux核心命令1.ls列出当前目录中的文件和目录djy666@ubuntu:~$ls20221307公共的模板视频图片文档下载音乐桌面Desktopdjyhellohello.chello.osnapls-l列出详细信息使用长格式列出目录内容,包括文件权限、所有者、文件......
  • linux中vim常用命令大全
    前言Linux有大量的配置文件,所以Linux的文本处理工具也是比较多的,其中编辑一些配置文件时,常用的工具就是vim。在Linux中,Vim编辑器是一个非常强大的文本编辑工具,它提供了多种模式和命令来满足不同的编辑需求。以下是Vim的一些常用命令和操作:一、启动与退出Vim‌直接启动Vim......
  • 【Linux】命令行参数和环境变量
         ......
  • 【Linux】kill与kill -9
    kill命令格式kill-signalpidsignalpid是进程号,ps命令可以查看默认情况下使用kill,系统发送给进程的是SIGTERM(15)信号,告诉进程“你需要被关闭,请自行停止并退出”。kill-9fasongSIGKILL(9)信号,告诉进程“你被终结了,请立刻退出”。kill-9表示强制杀死进程,与SIGTERM......
  • Linux命令实践
    目录课上测试作业题目:Linux命令实践学习Linux核心命令作业要求(9‘)作业内容命令演示AI提问及验证(重复命令未列出)总结高频命令自主学习重要命令推荐深入学习命令示例echoodbc总结作业提交要求(1')课上测试作业题目:Linux命令实践学习Linux核心命令lswhopwdcdmanwhereis......
  • C++ 多线程详解:从基础到应用
    目录一、什么是多线程?二、C++中的多线程支持三、总结在现代应用中,多线程成为了提升程序性能的重要工具。特别是当我们希望充分利用多核CPU的计算能力时,C++提供了强大的多线程支持,可以并发地执行多个任务。今天,我们将通过易懂的讲解与实际的代码示例,帮助你掌握C+......
  • 【系统架构设计师-2024年真题】案例分析-答案及详解
    更多内容请见:备考系统架构设计师-核心总结索引文章目录【材料1】(软件架构设计与评估)问题1问题2问题3【材料2】(系统设计与建模)问题1问题2问题3问题4【材料3】(嵌入式)问题1问题2问题3【材料4】(数据库缓存)问题1问题2问题3【材料5】(W......