【Linux系统编程】第二十一弹---进程的地址空间

news2024/11/13 10:31:06

个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】【Linux系统编程】

目录

1、进程空间的地址

1.1、基本概念

1.2、代码分析

1.3、如何理解地址空间

1.4、进一步理解页表和写时拷贝

1.5、进一步理解虚拟地址

2、内核进程调度队列 

2.1、一个CPU拥有一个runqueue

2.2、优先级

2.3、活动队列

2.4、过期队列

2.5、active指针和expired指针

2.6、总结


1、进程空间的地址

1.1、基本概念

  • 程序段(Text):程序代码在内存中的映射,存放函数体的二进制代码。
  • 初始化过的数据(Data):在程序运行初已经对变量进行初始化的数据。
  • 未初始化过的数据(BSS):在程序运行初未对变量进行初始化的数据。
  • 栈 (Stack):存储局部、临时变量,函数调用时,存储函数的返回指针,用于控制函数的调用和返回。在程序块开始时自动分配内存,结束时自动释放内存,其操作方式类似于数据结构中的栈。
  • 堆 (Heap):存储动态内存分配,需要程序员手工分配,手工释放.注意它与数据结构中的堆是两回事,分配方式类似于链表。

1.2、代码分析

地址空间图

讲解进程地址空间之前,我们先编写一段C语言程序。

#include<stdio.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 == 3)
      {
        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);
    }
  }
  return 0;
}

 测试结果

从上面的测试结果我们可以看到发现,父子进程,输出地址是一致的,但是变量内容不一样!能得出如下结论: 

  • 变量内容不一样,所以父子进程输出的变量绝对不是同一个变量。
  • 但地址值是一样的,说明,该地址绝对不是物理地址!
  • 在Linux地址下,这种地址叫做 虚拟地址
  • 我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理。

OS必须负责将 虚拟地址 转化成 物理地址 。

具体如下图:

上图分析:使用系统调用接口创建新的进程时,fork后的数据代码,父子进程将会同时执行,同时增加新的进程控制块(tast_struct),父子进程通过刚开始相同的页表指向相同的物理空间,其所使用的进程地址空间对应的位置也是相同的,父子进程指向同一个g_val,因此,父进程和子进程对应的g_val的地址是相同的,但是,当子进程尝试修改g_val变量时,为保证进程的独立性,操作系统识别到当前子进程通过页表找到g_val,想修改g_val,此时,操作系统会重新开辟一段空间,将上述值拷贝下来,修改映射关系,因此使用不同的物理内存地址,互不影响,互相独立。

为什么要写时拷贝呢?写时拷贝的效率会不会很低呢?

通过调整拷贝的时间顺序,达到有效节省空间的效果。

写时拷贝的效率并不会很低,因为如果不写时拷贝,需要将父进程的所以数据拷贝一份,而写时拷贝只需要将需要修改的数据拷贝一份,最坏情况也是跟不写时拷贝的效率一样。

可不可以直接将父进程的数据全部拷贝到新的空间呢?

可以,但是没有必要这么做。

因为子进程是能够访问父进程的数据的,大部分情况下,是不需要进行全部拷贝过来,那样太浪费空间了;我们通常是要进行写入的时候,OS才会要写入的变量复制一份,重新开一个大小一样的空间,在新开的空间内写入数据,再将新空间的地址交给页表。这是按需申请。通过调整拷贝的时间顺序,达到节省空间的目的。

1.3、如何理解地址空间

什么是划分区域?

举个例子,如果需要将桌子划分为两块该如何划分,假设桌子长度为100厘米。我们可以将桌子划分为左边区域和右边区域,左边区域为[1,50],右边区域为[50,100]。用计算机语言描述则可以通过两个结构体来描述,一个描述区域宽度,一个描述哪个区域。

struct area
{
    int start;
    int end;
}
struct desktop
{
    struct area left;
    struct area right;
}

struct desktop d;
//me
d.left.start=1;
d.left.end=70;
//同桌
d.right.start=70;
d.right.end=100;

源代码

地址空间本质就是内核中的一个结构体对象。 

为什么要有地址空间?

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

2.进程管理模块和内存管理模块进行解耦。

3.拦截非法请求---对物理内存的保护。

1.4、进一步理解页表和写时拷贝

页表还有一些其他的作用,1、判断该物理地址是否在内存中(进程挂起情况) 2、识别rwx权限(常量区不能修改值情况)

当操作系统判断出地址不在内存中时还会做进一步判断:

  • 1、是不是数据不在物理内存
  • 2、是不是数据需要写时拷贝
  • 3、如果都不是才能异常处理

写时拷贝

父子进程创建时使用相同的虚拟地址,而进行修改时,经操作系统识别,重新复制一份,并开辟新的空间,经过页表映射的是不同的物理地址,此时修改的是不同的物理地址的数据,其虚拟地址不受影响。

写时拷贝(Copy-on-write,简称COW)是一种计算机程序设计领域的优化策略。其核心思想是,如果有多个调用者(callers)同时请求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的调用者都是透明的。此作法主要的优点是如果调用者没有修改该资源,就不会有副本(private copy)被创建,因此多个调用者只是读取操作时可以共享同一份资源。

1.5、进一步理解虚拟地址

在最开始的时候,进程地址空间和页表里面的数据从哪里来的呢?

是从可执行程序内部来的。程序里面本身就有地址!!!这个地址就是虚拟地址(逻辑地址)。我们的可执行程序里面已经没有变量名和函数名,都变成了地址;

补充:

objdump -S 可执行程序  # 查看反汇编
objdump -S myprocess > test.s # 将反汇编内容重定向到test.s文件

测试结果 

结论:

创建一个进程,就会创建一个task_struct,地址空间,页表和物理内存。

2、内核进程调度队列 

上图是Linux2.6内核中进程队列的数据结构,之间关系也已经给uu们画出来,方便大家理解 。

2.1、一个CPU拥有一个runqueue

  • 如果有多个CPU就要考虑进程个数的负载均衡问题

2.2、优先级

  • 普通优先级:100~139(我们都是普通的优先级,想想nice值的取值范围,可与之对应!)
  • 实时优先级:0~99(不关心)

2.3、活动队列

  • 时间片还没有结束的所有进程都按照优先级放在该队列
  • nr_active: 总共有多少个运行状态的进程
  • queue[140]: 一个元素就是一个进程队列,相同优先级的进程按照FIFO规则进行排队调度,所以,数组下标就是优先级!
  • 从该结构中,选择一个最合适的进程,过程是怎么的呢?
    • 1. 从0下表开始遍历queue[140]
    • 2. 找到第一个非空队列,该队列必定为优先级最高的队列
    • 3. 拿到选中队列的第一个进程,开始运行,调度完成!
    • 4. 遍历queue[140]时间复杂度是常数!但还是太低效了!
  • bitmap[5]:一共140个优先级,一共140个进程队列,为了提高查找非空队列的效率,就可以用5*32个比特位表示队列是否为空,这样,便可以大大提高查找效率!

2.4、过期队列

  • 过期队列和活动队列结构一模一样
  • 过期队列上放置的进程,都是时间片耗尽的进程
  • 当活动队列上的进程都被处理完毕之后,对过期队列的进程进行时间片重新计算

2.5、active指针和expired指针

  • active指针永远指向活动队列
  • expired指针永远指向过期队列
  • 可是活动队列上的进程会越来越少,过期队列上的进程会越来越多,因为进程时间片到期时一直都存在的。
  • 没关系,在合适的时候,只要能够交换active指针和expired指针的内容,就相当于有具有了一批新的活动进程!


2.6、总结


在系统当中查找一个最合适调度的进程的时间复杂度是一个常数,不随着进程增多而导致时间成本增加,我们称之为进程调度O(1)算法!

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

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

相关文章

基于SpringBoot+Vue+MySQL的智能物流管理系统

系统展示 系统背景 随着信息技术的飞速发展和电子商务的蓬勃兴起&#xff0c;智能物流管理系统的需求日益迫切。传统的物流管理方式已难以满足高效、精准、实时的管理需求。因此&#xff0c;基于SpringBoot、Vue和MySQL的智能物流管理系统应运而生。该系统旨在通过现代化的技术…

vue项目引入比较独特的字体的方法

引入字体的步骤 前言&#xff08;步骤一&#xff09;引入的文件OPPOSans-M.ttf,TencentSans-W3.ttf,TencentSans-W7.ttf,YouSheBiaoTiHei.ttf (步骤二)font.css(步骤三) 全局引入在使用的地方的展示效果展示 前言 公司这边开发一个可视化大屏&#xff0c;UI小姐姐设置了很多比…

2024年超好用的公司加密软件分享|十款企业防泄密软件推荐

在数字化时代&#xff0c;企业数据的安全性变得尤为重要。随着网络攻击和数据泄露事件的频发&#xff0c;企业需要采取有效的措施来保护敏感信息。加密软件作为一种重要的数据保护工具&#xff0c;能够帮助企业防止数据泄露和未经授权的访问。本文将为您推荐十款2024年超好用的…

Dockerfile部署xxljob

使用Dockerfile部署xxljob 1. 背景 我们在使用定时任务调度时&#xff0c;通常会使用xxljob容器化部署xxljob&#xff0c;通常使用 docker pull xuxueli/xxl-job-admin:2.4.0 拉取镜像并启动容器。这种方式对于x86架构服务器来说&#xff0c;没有任何问题。但是在arm架构的服…

什么是 IP 地址信誉?5 种改进方法

IP 地址声誉是营销中广泛使用的概念。它衡量 IP 地址的质量&#xff0c;这意味着您的电子邮件进入垃圾邮件或被完全阻止发送的可能性。 由于每个人都使用专用电子邮件提供商而不是直接通过 IP 地址进行通信&#xff0c;因此&#xff0c;这些服务可以跟踪和衡量发件人的行为质量…

玩机进阶教程-----MTK芯片机型 回读 备份 导出分区来制作线刷包 其中MT****_Android_scatter.txt的修改 分区的写入与否

在与一些小品牌机型定制系统过程中。其中一些机型定制导出分区制作线刷包。默认分区的写入与否要了解清楚。有些分区导出后在写入有可能会导致机型不开机或者卡第一屏的故障。这方面最基本的就是涉及mtk分区写入地址引导MT****_Android_scatter.txt的修改 通过博文了解 1----…

一文了解高速工业相机

超高速相机是工业相机的一种&#xff0c;一般高速相机指的是数字工业相机&#xff0c;其一般安装在机器流水线上代替人眼来做测量和判断&#xff0c;通过数字图像摄取目标转换成图像信号&#xff0c;传送给专用的图像处理系统。 超高速工业相机的采集速率> 50Gb/s&#xff…

AI取代程序员? or 成为10倍效能工程师!

Manuel Odendahl 是一位知名的人工智能和机器学习专家&#xff0c;尤其在计算机视觉和自然语言处理领域有显著贡献。他的研究涉及深度学习、图像识别和人机交互等方面&#xff0c;且在相关领域发表了多篇学术论文。 在这个人工智能快速发展的时代&#xff0c;程序员的工作方式正…

SpringCloud 基于 web 的只会养老平台

摘要 首先,论文一开始便是清楚的论述了系统的研究内容。其次,剖析系统需求分析,弄明白“做什么”,分析包括业务分析和业务流程的分析以及用例分析,更进一步明确系统的需求。然后在明白了系统的需求基础上需要进一步地设计系统,主要包罗软件架构模式、整体功能模块、数据库设计…

23:SPI二:W25Q64存储器模块的使用

W25Q64存储器模块的使用 1、W25Q64的简介2、模块内部结构2.1&#xff1a;引脚结构2.2&#xff1a;内部存储结构2.3&#xff1a;此模块的注意事项 3、程序模拟SPI读写W25Q644、片上外设SPI读写W25Q64 1、W25Q64的简介 其中最主要的特点就是掉电不丢失。 由上图所示&#xff1a…

基于SpringBoot+Vue的宠物医院管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的…

Rust编程的作用域与所有权

【图书介绍】《Rust编程与项目实战》-CSDN博客 《Rust编程与项目实战》(朱文伟&#xff0c;李建英)【摘要 书评 试读】- 京东图书 (jd.com) Rust编程与项目实战_夏天又到了的博客-CSDN博客 3.8 作 用 域 Rust的所有权系统和作用域息息相关&#xff0c;因此有必要先理解R…

C++和OpenGL实现3D游戏编程【连载9】——纹理的镂空显示

1、本节实现的内容 前面的课程中,我们学会了加载纹理并显示纹理图案,但是纹理的图案都是长方形的图片,图片就会有白色或黑色背景,那么在游戏设计过程中,我们经常不需要显示图片的背景部分,那么这节课我们就来讨论一下如何实现剔除白色或黑色背景后的镂空图像,下图就是将…

算法打卡:第十一章 图论part01

今日收获&#xff1a;图论理论基础&#xff0c;深搜理论基础&#xff0c;所有可达路径&#xff0c;广搜理论基础&#xff08;理论来自代码随想录&#xff09; 1. 图论理论基础 &#xff08;1&#xff09;邻接矩阵 邻接矩阵存储图&#xff0c;x和y轴的坐标表示节点的个数 优点…

PFC理论基础与Matlab仿真模型学习笔记(1)--PFC电路概述

一、整流器滤波电路简介 整流器滤波电路的主要功能是将交流电&#xff08;AC&#xff09;转换为直流电&#xff08;DC&#xff09;&#xff0c;并通过滤波器减少波动以输出稳定的直流电。其工作原理主要分为两个部分&#xff1a; 1.整流部分 整流器的核心器件是二极管&#…

Spring Boot从0到1 -day02

目录 学习目标Spring Boot 的基本配置启动类与核心注解SpringBootApplicationSpring Boot 的全局配置文件1. application.properties2. application.ymlSpring 中Spring Boot Application注解的作用 自动配置原理1. 自动配置类2. 自动配置的发现示例3. 自定义自动配置 条件注解…

手把手教你用Ollama AnythingLLM搭建AI知识库,无需编程,跟着做就行!

在本地电脑上跑大语言模型&#xff08;LLM&#xff09;&#xff0c;已经不是什么高科技操作了。随着技术的迭代&#xff0c;现在利用Ollam和AnythingLLM就可以轻松构建自己的本地知识库&#xff0c;人人皆可上手&#xff0c;有手就行。过往要达成这一目标&#xff0c;可是需要有…

令人拍案叫绝的Python条件控制技巧

目录 1. 条件控制的重要性 2. 理解条件语句的基础 3. 使用 elif 增加更多选择 4. 利用 in 和 not in 进行集合匹配 5. 利用 and 与 or 连接条件 6. 高级技巧&#xff1a;列表推导式中的条件表达式 7. 实战案例&#xff1a;自动评分系统 8. 总结 文末福利 1. 条件控制的…

计算机网络34——Windows内存管理

1、计算机体系结构 2、内存管理 分为连续分配管理和非连续分配管理 在块内存在的未使用空间叫内部碎片&#xff0c;在块外存在的未使用空间叫外部碎片 固定分区分配可能出现内部碎片&#xff0c;动态分区分配可能出现外部碎片 3、逻辑地址和实际地址的互相转换 4、缺页中断 …

渗透测试常用工具(非常详细)从零基础入门到精通,看完这一篇就够了。

对于白帽子来说&#xff0c;在进行渗透测试、代码审计、逆向工程等一系列工作中&#xff0c;都离不开安全工具的支撑&#xff0c;这些工具像一把把利剑&#xff0c;可以大大提高渗透效率。 在本篇中&#xff0c;我总结了超多网络安全工具&#xff0c;涉及暴力破解、渗透字典、…