Linux程序地址空间

news2024/9/25 7:19:35

在这里插入图片描述

Tips: 之后的博客以记录笔记为主了

文章目录

    • 0.前言
      • 历史遗留
      • 进程独立性
    • 2. 地址空间是什么
      • 2.1 地址空间
      • 2.2 地址空间的区域划分
    • 3. 页表
    • 4. 为什么要有进程地址空间

0.前言

历史遗留

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

int g_val1;
int g_val2 = 10;

int main()
{
  printf("                  code addr:%p\n",main);
  const char*str = "hello world";
  printf("read only string value addr:%p\n",str);
  printf("     init global value addr:%p\n",&g_val1);
  printf("   uninit global value addr:%p\n",&g_val2);
  char*mem = (char*)malloc(100);
  printf("                  heap addr:%p\n",mem);
  printf("                 stack addr:%p\n",&str);                                    

  return 0;
}

image-20231023142836939

栈区的地址是高地址向低地址方向增长,堆区的地址是低地址向高地址方向增长

image-20231023145418275

对于static修饰的静态变量,我们以为是是存储在栈区,其实在编译的时候,已经被编译到全局去了,所以在函数调用结束之后,并不会被释放,只不过它的作用域只是在这块函数里面(如下图验证,地址与全局数据相近)

image-20231023145925479

进程独立性

接下来看这段代码

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

int g_val = 100;

int main()
{
  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_va    l,&g_val);
      sleep(1);
      if(cnt--== 0)
      {
        g_val = 200;                                                                  
        printf("子进程:g_val:100->200\n");
      }
    }
  }
  else
  {
    while(1)
    {
      printf("I am parent,pid:%d ppid:%d ,g_val:%d,&g_val:%p\n",getpid(),getppid(),g_v    al,&g_val);
      sleep(1);
    }
  }
  return 0;
}

运行之后发现,子进程将全局的g_val修改后,并不会影响到父进程访问,但是子进程父进程访问的地址都是一样的。

这就说明他们访问的并不是直接的物理地址,这个地址叫做线性地址或者虚拟地址

所以我们平时写的C/C++的指针,并不是物理地址

image-20231024135144215

前面我们提到过,进程就是PCB+内核数据结构,但实际并没有这么简单。内核除了要创建PCB这样的结构,还要创建进程地址空间

这些地址空间就是指向的虚拟地址,这个虚拟地址存在一张页表当中,是一个K_V结构,映射一个物理地址。

每个进程都有自己的页表,上面的子进程,拷贝了一份父进程的页表,当子进程的数据要修改的时候,就会进行写时拷贝,重新分配映射物理地址,所以这就有了_为什么这个子进程将g_val_修改之后,不影响父进程访问

image-20231024145451368

2. 地址空间是什么

2.1 地址空间

在32位计算机中,有32位的地址和数据总线,CPU和内存通过总线连接起来。我们常说,计算机只认识二进制,再往深了说,计算机其实只认识高低电频,我们对内存中的寄存器进行充放电,就是数据的访问过程,由于只有高低电频,所以我们就将高电频定义为1,低电频定义为0,这就是我们所说的二进制。然后将这些高低电频组合起来,就是向内存寻址,也就是我们的物理地址

每根地址总线只有0、1,32根就是232总,寻址的单位是字节,所以这就注定了我们32位机器能够装载内存的内存空间为232 * 1byte = 4GB

2.2 地址空间的区域划分

我们地址空间的范围是[0,232],在这个区间内,又被划分成了多个范围,例如我们的栈区、堆区什么的,通过这些区间的划分来管理好我们各个区域,我们需要什么区间的数据,直接定位在这个区间即可

struct mm_struct
{
    long code_start,code_end;
    long readonly_start,readonly_endl;
    long heap_start,heap_end;
    //...
}

3. 页表

对于页表,目前可以理解为一个映射表,每个虚拟地址都映射着自己的物理地址,另外也存储着读写的标识符rworr,这个标识符就代表着是否可以读写操作(只读常量)。

对于物理内存,并不知道是否可以读写,它没有权限这个概念,所以需要我们在中途进行标识。如果我们页表的标识符是只读,那么在中途就拦截下来了,并不会写入内存。这就是为什么有只读常量区。

我们玩的一些游戏,例如英雄联盟,下载就是十几个G,可是物理内存只有4个G或者8个G,但这个游戏还是能跑,这就能够说明,我们的操作系统对大文件,可以实现分批加载

例如我们要加载一个500MB的空间,但我们的代码却是一行一行的执行,在短期之内并不需要这么多空间,可能只用到了10MB,那这剩余的490MB,需要全部加载到内存么?

这里我们要有一个共识,操作系统并不会做浪费时间和浪费空间的事情

所以,这490MB,并不会全部加载到内存当中,操作系统采用的是一种惰性加载的策略

在页表当中其实还有一个字段,这个标识符表示对应的代码和数据是否已经加载到内存当中。我们的虚拟地址都在这个也页表当中,但有些还没有给它加载到内存。当我们进行访问时,操作系统识别到页表中的这个标识符为未加载,那么在会申请一份物理内存,把这个可执行程序的代码和数据加载内存,然后把这个地址填到页表当中,这个过程叫做缺页中断,然后再进行访问就能访问了。

在进程创建的时候,一定是要先创建内核的数据结构,即对该进程维护的PCB、地址空间、页表这些对应关系处理好,然后再加载对应的可执行程序

4. 为什么要有进程地址空间

进程 = 内核数据结构(task_struct && mm_struct && 页表)+ 程序的代码和数据

  • 每个进程启动时,操作系统都会给进程构建地址空间,依次来表征进程能看到的空间范围,这样就能让进程以统一的视角区看待内存结构,这样内存就不需要自己去维护了

    但这其实是一个大饼,虽然能看到整个内存的空间范围,但是并不会全部给这个进程使用,例如我们C语言申请内存的时候,申请太大的时候,会申请失败

  • 有了虚拟地址,这样进程访问内存的时候,会经过一个中间的转换,如果是一个非法的访问操作,在这个转换的过程中,就能对这个非法操作进行拦截,这样就能保护我们的物理内存

  • 有了地址空间和页表的存在,进程的管理并不用关系内存管理,进程需要的内存,如果没有了,会自动缺页中断,操作系统调用内存管理的功能。这样就将进程管理模块和内存管理模块进行解耦。
    ,如果是一个非法的访问操作,在这个转换的过程中,就能对这个非法操作进行拦截,这样就能保护我们的物理内存

  • 有了地址空间和页表的存在,进程的管理并不用关系内存管理,进程需要的内存,如果没有了,会自动缺页中断,操作系统调用内存管理的功能。这样就将进程管理模块和内存管理模块进行解耦。

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

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

相关文章

如何提高app的广告变现能力?

对于中小型app&#xff0c;开发者在开发app之前&#xff0c;更应该考虑清楚app商业化的方向。 广告总收入A广告位收入B广告位收入C广告位收入...n广告位收入 单个广告位收入广告请求*广告填充率*广告展示率*eCPM/1000 1、找准用户质量 广告投放核心的指标是roi&#xff0c;…

【用一张动图解释 8 种常用网络协议】

网络协议就是计算机之间沟通的语言&#xff0c;为了有效地交流&#xff0c;计算机之间需要一种共同的规则或协议&#xff0c;就像我们和老外沟通之前&#xff0c;要先商量好用哪种语言&#xff0c;要么大家都说中文&#xff0c;要么大家都说英语&#xff0c;这才能有效地沟通。…

【CSS】伪类和伪元素

伪类 :hover&#xff1a;悬停active&#xff1a;激活focus&#xff1a;获取焦点:link&#xff1a;未访问&#xff08;链接&#xff09;:checked&#xff1a;勾选&#xff08;表单&#xff09;first-child&#xff1a;第一个子元素nth-child()&#xff1a;指定索引的子元素&…

40.查找练习题(王道2023数据结构第7章)

试题1&#xff08;王道7.2.4节综合练习5&#xff09;&#xff1a; 写出折半查找的递归算法。 #include<stdio.h> #include<stdlib.h> #include<string.h>#define MAXSIZE 10 #define ElemType int #define Status inttypedef struct{int data[MAXSIZE]; /…

单例模式详解【2023年最新】

一、单例模式概念 单例模式是一种创建型设计模式&#xff0c;它确保一个类只有一个实例&#xff0c;并提供一个全局访问点来访问该实例。它的目的是限制一个类只能创建一个对象&#xff0c;以确保在整个应用程序中只有一个共享的实例。 单例模式通常用于以下情况&#xff1a;…

服务容错框架Sentinel入门

概述 Sentinel&#xff0c;阿里开源的一套用于服务容错的综合性解决方案。它以流量为切入点&#xff0c;从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。分布式系统的流量防卫兵。 特征: 丰富的应用场景&#xff1a;秒杀&#xff08;即突发流量控制在系统…

物联网AI MicroPython传感器学习 之 GP2Y10粉尘传感器

学物联网&#xff0c;来万物简单IoT物联网&#xff01;&#xff01; 一、产品简介 GP2Y10粉尘传感器用于检测非常细的空气漂浮颗粒物&#xff0c;主要用于空气净化系统中&#xff0c;传感器外观引脚如下图所示。 引脚定义 GND&#xff1a;地VCC&#xff1a;5VLED&#xff1a…

Dos和DDos攻击

Dos Denialof Service拒绝服务,利用程序漏洞或一对一资源耗尽的&#xff0c;攻击有计算机网络宽带攻击和连通性攻击 分类&#xff1a; D网络&#xff1a;耗尽目标网络带宽资源 如&#xff1a;ICMP Flood, UDP Flood D协议&#xff1a;攻击协议漏洞发起的拒绝服务攻击 如…

Leetcode—17.电话号码的字母组合【中等】

2023每日刷题&#xff08;九&#xff09; Leetcode—17.电话号码的字母组合 回溯法解题 实现代码 class Solution { public:vector<string> table {"","","abc","def","ghi","jkl","mno",&quo…

leetCode 283 移动零

//right指向待处理序列的首部 //left指向处理后序列的尾部class Solution {public void swap(int[] nums, int left, int right){int tmp nums[left];nums[left] nums[right];nums[right] tmp;}public void moveZeroes(int[] nums) {int n nums.length;int left 0;int rig…

Oracle(6) Control File

一、oracle控制文件介绍 1、ORACLE控制文件概念 Oracle控制文件是Oracle数据库的一个重要元素&#xff0c;用于记录数据库的结构信息和元数据。控制文件包含了数据库的物理结构信息、数据字典信息、表空间和数据文件的信息等。在Oracle数据库启动时&#xff0c;控制文件会被读…

【c#】2022创建WEB API接口教程demo

c#创建WEB API接口 创建WEB API接口结果图涉及到的技术设计流程创建WEB API接口 结果图 涉及到的技术 设计流程 1、创建WEB api项目,使用控制器和penapi勾选上,第一次创建项目时没有勾选,因为感觉没啥用。后面跑项目的时候,要把接口用swagger去直接生成的时候,还是需要…

新的抓包神器,完全免费,支持多平台!

目前IOS端的抓包软件&#xff1a; HTTP Catcher&#xff08;网球&#xff09;&#xff0c;免费下载&#xff0c;完整版需要内购。 Storm Sniffer&#xff08;螃蟹&#xff09;&#xff0c;免费下载&#xff0c;完整版需要内购。 Thor&#xff08;锤子&#xff09;&#xff0…

安全设备

一.防火墙 5层应用层 防火墙 4层 udp tcp 协议 华为 厂商 华为 h3 1.区域划分 Dmz 停火区 Untrust 不安全区域 Trust 安全区域 防火墙 默认禁止所有 二.Waf Web 应用防火墙 放到web前面 产品 雷池 绿盟 软件 安…

proxy解决跨域问题

在使用vite工具创建的项目中解决跨域问题&#xff1a; 在vue.config.js中配置如下代码&#xff1a; const { defineConfig } require(‘vue/cli-service’) module.exports defineConfig({ transpileDependencies: true, server:{ proxy:{ ‘/path’:{ target:‘https://i.…

21 行为型模式-职责链模式

1 职责链模式介绍 2 职责链模式原理 3 职责链模式实现 责任链模式的实现非常简单&#xff0c;每一个具体的处理类都会保存在它之后的下一个处理类。当处理完成后&#xff0c;就会调用设置好的下一个处理类&#xff0c;直到最后一个处理类不再设置下一个处理类&#xff0c;这时…

美团悄悄上线社群团购“团买买”

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 之前有社交电商&#xff1a;群买买、快团团、群团团、京喜团&#xff0c;而最近美团也上线了一款社群团购工具“团买买”的小程序。看来今年社群团购赛道非常热门啊&#xff0c;美团的“团买买”对…

【JAVA学习笔记】44 - 注解,元注解

项目代码 一、注解的引入 1)注解(Annotation)也被称为元数据(Metadata),用于修饰解释包、类、方法、属性、构造器、局部变量等数据信息。 2)和注释一样&#xff0c;注解不影响程序逻辑&#xff0c;但注解可以被编译或运行&#xff0c;相当于嵌入在代码中的补充信息。 3)在Ja…

回归预测 | MATLAB实现BO-BiLSTM贝叶斯优化双向长短期神经网络多输入单输出回归预测

回归预测 | MATLAB实现BO-BiLSTM贝叶斯优化双向长短期神经网络多输入单输出回归预测 目录 回归预测 | MATLAB实现BO-BiLSTM贝叶斯优化双向长短期神经网络多输入单输出回归预测效果一览基本介绍模型搭建程序设计参考资料 效果一览 基本介绍 MATLAB实现BO-BiLSTM贝叶斯优化双向长…

让MyEclispe中英文切换详解(附源码)

找到根目录 打开图中红框圈起来的myeclipse.ini文件&#xff08;或者选择使用记事本打开&#xff09; 在末尾添加 -Duser.languageen 这是设置成英文版 设置成中文则将 en 更改为 zh_cn就行了 -Duser.languageen-US -Duser.languagezh_cn