Linux--地址空间

news2025/1/11 4:28:32

目录

看一个现象

基本概念

 细节问题--理解它

1.如何理解地址空间?

2.为什么要有地址空间?

3. 进一步了解页表和写时拷贝

 4.如何理解虚拟地址?


看一个现象

 先通过一段代码,看一看现象

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);
        }
    }
}

运行结果:我们都知道父子进程是具有独立性的。进程=内核数据结构+代码和数据。

这里就能解释:程序开始运行,5s后子进程将g_val修改成300,但并没有影响到父进程。

        我们知道程序进入编译阶段,将c语言文件转成二进制文件时,函数名和变量名都已经不存在了,而是被替换成地址了。

但让人不理解的地方:父子进程的g_val地址竟然是一样的,内容却不一样!!!

我们虽然不知道这个地址是怎么回事,但我们可以确定的是,这个地址绝对不是物理地址,否则不可能会有两种值。我们在系统层面上将这个地址称为:虚拟地址


基本概念

        每个进程在Linux中都拥有自己独立的地址空间。这个地址空间是一个虚拟的、连续的内存区域,包含了程序可以访问的所有内存地址。地址空间的大小通常远大于实际的物理内存大小,这是通过虚拟内存技术实现的。(地址空间本质就是内核中的一个结构体对象)

        页表是Linux实现虚拟内存到物理内存映射的关键数据结构。在Linux中,内存是以页为单位进行管理的,每个页的大小通常是固定的(例如4KB)。页表是一个表结构,其中每个条目都对应于地址空间中的一个页,并包含了该页在物理内存中的地址或其在交换空间中的位置信息。

以上面的情况为例:

        我们在父进程中创建了g_val,接着我们创建了子进程,子进程会把父进程的很多内核数据结构全拷贝一份(这里就包括了地址空间和页表)。父进程创建了变量g_val,100会存在于物理内存里(真实地址),但在这之前地址空间会给g_val一个虚拟地址,虚拟地址和真实地址都会被放在页表中,父进程通过页表使用虚拟地址就可以读取到g_val的值了

        子进程中包含了父进程的大部分信息,子进程也可以通过这个虚拟地址读取到g_val的值,这时子进程想通过虚拟地址修改g_val的值(300),由于进程之间是由独立性的,为了维护这种独立性,操作系统会自主完成写时拷贝,重新在物理内存中开辟一块空间存放300,并且将子进程页表中虚拟地址对应的真实地址修改成新开辟空间的地址。

        此时父子进程中g_val的虚拟地址是一样的但被页表映射到了物理空间不同的区域。

关于写时拷贝

        为了保证进程的独立性,可不可以把数据在创建子进程的时候,全部给子进程拷贝一份?可以,但不好,因为浪费空间了。

        在Linux内核中,写时拷贝技术被广泛应用在进程创建、文件映射等场景中。例如,当父进程创建子进程时,子进程会继承父进程的代码段、数据段、堆和栈等内容。此时,为了避免立即复制这些资源造成的性能损耗及浪费问题,Linux内核采用了写时拷贝技术。也就是说,子进程在开始时并不拥有这些资源的物理副本,而是与父进程共享这些资源。只有当子进程或者父进程试图修改这些资源时,Linux内核才会进行实际的复制操作,以确保修改不会影响到另一个进程。


 细节问题--理解它


1.如何理解地址空间?

a.什么是划分区域?

实际问题中:画38线。

在计算机中:地址空间本质是内核的一个struct结构体,内部有很多的属性都是表示start(起始),end(结束)的范围

b.对地址空间的理解

        进程空间实际上就是操作系统给给每个进程画的饼,操作系统给每个进程的地址空间的大小都是真实物理内存的大小,比如真实的物理内存大小有100G,OS就会告诉每个进程,我会给你100G的大小供你使用, 这里给进程的100G就是地址空间,但这是虚拟内存,实际并没有那么大。


2.为什么要有地址空间?

        在Linux中,地址空间的存在具有多重重要性。首先,它确保了不同进程之间无法直接访问对方的内存空间,从而极大地保护了内存中代码和数据的安全性。想象一下,如果没有地址空间的隔离,多个进程可能会相互干扰,导致数据混乱和系统崩溃。
        其次,地址空间有助于实现进程间的独立性。每个进程都有其自己的地址空间,这使得它们的数据和代码相互隔离,避免了潜在的冲突。这种独立性是操作系统能够同时管理多个进程的关键。
        此外,地址空间还使得进程能够以统一的视角看待自己的代码和数据。通过虚拟地址空间,操作系统为每个进程创建了一个独立的环境,使得进程能够方便地访问自己的资源和执行操作,而无需关心底层的物理内存布局。
        在更底层的技术实现上,地址空间结合页表实现了虚拟地址到物理地址的映射。这种映射机制不仅提供了内存管理的灵活性,还允许操作系统在必要时进行内存保护,防止进程访问未授权的内存区域。

        Linux通过隔离机制、页表和MMU、内存保护机制以及高级安全特性等多种手段来拦截和处理非法请求,确保地址空间的安全性和稳定性。这些机制共同构成了一个强大的安全防线,保护着Linux系统的正常运行和数据的完整性。
 


3. 进一步了解页表和写时拷贝

关于页表

        每个进程都有自己的页表,这使得每个进程可以认为自己拥有整个虚拟地址空间,而不会干扰到其他进程。当进程访问某个虚拟地址时,CPU会查找页表,找到对应的物理地址,然后完成访问。
        页表的一个重要特性是它可以实现虚拟内存的一些高级特性,比如内存保护(通过设置页表的访问权限位来防止非法访问)、内存共享(多个进程可以共享同一物理页,通过设置页表的共享位来实现)以及内存映射(将文件或其他对象映射到虚拟地址空间)。

看下面这段C语言代码,是否可行:

char*str = "hello";
*str = 'H'

答案是不可行的。*str储存在常量区不能被修改,因为常量区有代码没有写权限(通过设置页表的访问权限位来防止非法访问)                                                                                           


写时拷贝       

      在写时拷贝技术中,当一个进程试图复制另一个进程的内存空间时(例如,通过fork()创建一个新进程),操作系统并不会立即复制所有的内存页。相反,它只会复制页表,并标记这些页为“写时拷贝”。这意味着新进程和老进程现在共享相同的物理内存页。
        只有当新进程试图写入某个共享页时,操作系统才会触发写时拷贝操作。这时,操作系统会为该页分配一个新的物理页,并将新进程页表中的对应项更新为指向这个新页。然后,操作系统会将老进程和新进程对共享页的修改隔离开来,确保每个进程都有自己的数据副本。
        这种写时拷贝的策略可以极大地提高fork()的效率,因为大多数情况下,新进程并不会立即修改它的内存空间。因此,只有在必要时才进行实际的内存复制,这可以节省大量的时间和资源。              


 4.如何理解虚拟地址?

在最开始的时候,虚拟地址和页表里面的地址从哪里来?

        程序里面本身就有地址!!!这里的地址是虚拟地址(逻辑地址)。 因此,虚拟地址和页表里面的地址直接从程序里面读就行了。程序一旦加载到物理内存的时候,它就会有新的物理地址,接着根据虚拟到物理建立映射关系。这种编译模式叫做平坦模式。

        这些地址在程序运行时被使用,与程序的实际物理内存位置无直接关联。虚拟地址空间是从0开始的,每个进程都有自己独立的虚拟地址空间。
        虚拟地址的主要特点是与实际的物理内存容量无关,它使得程序在编写时无需关心实际的物理内存布局和大小,从而提高了程序的可移植性和灵活性。此外,虚拟地址空间通常被分为用户空间和内核空间两部分,这种分离机制有助于保护系统内核不受用户程序的影响,提高了系统的安全性。
        在Linux系统中,虚拟地址到物理地址的映射是通过页表实现的。页表是操作系统为了支持虚拟内存而创建的数据结构,它记录了虚拟地址到物理地址的映射关系。当进程访问某个虚拟地址时,CPU会查找页表,找到对应的物理地址,然后完成访问。这种映射关系可以动态改变,操作系统可以根据需要添加、删除或修改页表中的条目,以支持内存的动态分配、释放和共享等操作。
        总的来说,虚拟地址在Linux中是一种重要的内存管理机制,它使得程序能够以一种抽象、统一的方式访问内存,提高了程序的可靠性和可维护性。同时,通过页表等机制,操作系统能够实现对内存的高效管理和优化。

 

 

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

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

相关文章

Linux 认识与学习Bash——2

1 read 从键盘读取变量的值 read 后面不带变量,那么默认会给REPLY变量赋值 #!/bin/bash echo -n "请输入你的名字:" read name echo "欢迎您 $name" echo "----------------"echo -n "请输入你的名字2:&q…

我与深拷贝

前言 最近在掘金读到了一篇文章《Radash 能取代 Lodash???真幽默 - 掘金》,文章的评论区讨论起了深拷贝。"深拷贝" 我的"老朋友",还记得在学习我人生中的第二道面试题的时候认识了它,…

CSS 画一个三角形

一、前言 在前端开发的时候,我们有时候会需要用到一个三角形的形状,比如地址选择或者播放器里面播放按钮 通常情况下,我们会使用图片或者svg去完成三角形效果图,但如果单纯使用css如何完成一个三角形呢? 实现过程似…

物理学视角讲解diffusion生成模型——隐扩散模型

https://zhuanlan.zhihu.com/p/692996885 https://zhuanlan.zhihu.com/p/693255617 前面两篇文章介绍了扩散过程,同时实现了1维、2维混合高斯扩散、逆扩散,通过模型预测得分函数来实现逆扩散推理。这个章节介绍工业界使用的文本生成图扩撒模型&#xff1…

基于SSM+Jsp+Mysql的多人命题系统

开发语言:Java框架:ssm技术:JSPJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包…

frp改造Windows笔记本实现家庭版免费内网穿透

文章目录 前言frp原理Windows服务端IP检验IP固定软件下载端口放行端口映射开机启动 NAS客户端端口查询软件下载端口检验穿透测试自启设置 Ubuntu客户端软件下载后台启动 后记 前言 之前一直用花生壳远程控制一个服务器,但最近内网的网络策略似乎发生了变化&#xf…

InfiniGate自研网关实现思路二

5.HTTP请求参数解析 解析 HTTP 网络请求的参数信息,包括;GET/POST,以及应对不同 Content-Type 类型的处理。 HTTP 接口请求的参数需要解析成可以匹配到 RPC 接口的入参信息,所以通常为了方便控制一般只支持 RPC 接口单个对象入参…

「sentinel」流量控制组件的应用

「sentinel」流量控制组件的应用 Sentinel版本QPS 一、初识Sentinel1、Sentinel2、Sentinel 和 Hystrix对比3、雪崩问题 二、环境搭建1、下载安装Sentinel2、微服务整合Sentinel 三、流量控制1、簇点链路2、流控设置3、流控模式直接关联链路 4、流控效果流控效果解释 四、热点限…

C#通用类库封装实战

数据库查询 特性方式获取数据库列的别名 数据库更新 使用简单工厂配置的方式

C++ stl容器stack,queue,priority_queue的底层模拟实现

目录 前言: 文档借鉴:Reference - C Reference 1.deque a.deque的结构特点: b.deque的迭代器结构: c.面试题: 2.stack 3.queue 4.仿函数 5.priority_queue 总结: 前言: 本篇一共简单…

【QT学习】8.qt事件处理机制,事件过滤器,自定义事件

1.qt事件处理机制 事件处理: 当用户移动鼠标的时候 ,创建一个 鼠标移动事件对象 然后把这个对象放到 事件队列里面去,事件管理器 从队列中 取出事件,然后 调用其对应的事件处理函数。 多态机制: &#x…

靠谱的婚恋平台有哪些?青藤之恋、二狗、百合网、珍爱网等深度测评

哇塞,恋爱和结婚对于年轻人来讲可是超级重要的大事呢!不过呀,找到一个稳稳当当的婚恋平台可不简单哟!那么,到底哪个婚恋平台最靠得住呢? 丛丛: 这可是我用了好久好久的脱单交友小程序嘞&#xf…

MySQL中explain的用法

执行结果各字段的含义 EXPLAIN SQL语句 如: EXPLAIN SELECT * FROM test 执行结果: 列名描述id在一个大的查询语句中每个SELECT关键字都对应一个 唯一的idselect_typeSELECT关键字对应的那个查询的类型table表名partitions匹配的分区信息type针对单表…

机器学习预测汽车油耗效率 MPG

流程 数据获取导入需要的包引入文件,查看内容划分训练集和测试集调用模型查看准确率 数据获取 链接:https://pan.baidu.com/s/1KeIJykbcVpsfEk0xjhiICA?pwd30oe 提取码:30oe --来自百度网盘超级会员V1的分享导入需要的包 import pandas as pd imp…

【Spring Boot】掌握Spring Boot:深入解析配置文件的使用与管理

💓 博客主页:从零开始的-CodeNinja之路 ⏩ 收录文章:【Spring Boot】掌握Spring Boot:深入解析配置文件的使用与管理 🎉欢迎大家点赞👍评论📝收藏⭐文章 目录 Spring Boot 配置文件一. 配置文…

重新总结一下以前写过的“波特率”!单片机常见的通信速率分析!

文章目录 如题以前文章新的总结如题 波特率是单片机中描述通信速率的一个单位,比如串口通信、SPI通信、IIC通信、LIN通信、CAN通信等等,现在重新总结一下涉及到波特率的一些知识点。 以前文章 上面是存储的单位换算方式 这是通信速率的换算方式 新的总结 波特率的英文是…

画家-qt-surce

void GraphicView::paintEvent(QPaintEvent *pe) { QPainter painter(viewport()); painter.setRenderHint(QPainter::SmoothPixmapTransform);//升级画家 painter.drawImage(rect(),musicImage); } 分析: 这段代码是用于绘制图形视图的部分。 1. void GraphicV…

JavaCard学习笔记: CAP Component 之 Class Component

文章目录 整体结构tag和size字段signature_pool_length和signature_pooltype_descriptor结构导入类型编码导入项签名示例导入类导入数组导入远程方法 interfaces[]interface_info结构flagsinteface_countsuperinterfacesinterface_name class_info_compact classes[]结构flagsi…

mapreduce中的ReduceTask工作机制(Hadoop)

ReduceTask 是 Hadoop 中的一个重要组件,负责对 MapTask 的输出进行合并、排序和归并,最终生成最终的输出结果。 ReduceTask 的工作机制 1. 分组(Shuffle)阶段: 在分组阶段,ReduceTask 会从多个 Mapper …

【问题处理】银河麒麟操作系统实例分享,服务器操作系统VNC远程问题分析

1.服务器环境以及配置 【内核版本】 4.19.90-23.8.v2101.ky10.aarch64 【OS镜像版本】 0518-server 2.问题现象描述 服务器通过vncserver:1.service服务启动的vnc服务后,普通用户用vnc连接时,锁屏后,然后输入登陆密码会报密码错误&…