• 【Linux进程篇】进程地址空间


    Linux进程篇】进程地址空间(1)

    作者:爱写代码的刚子
    时间:2023.10.20
    前言:本篇博客是对【Linux进程篇】的收尾,介绍进程地址空间的相关内容。本篇博客研究的背景:kernel 2.6.32 32位平台


    再次认识空间布局

    在这里插入图片描述

    其中:未初始化数据和初始化数据属于静态全局区。

    命令行参数比环境变量更靠近高地址

    注意:不同芯片平台栈的增长方向可能不同,栈顶指针用esp寄存器存储,同时不管数组在栈区还是堆区,数组的生长方向都是从下往上的(也就是从低地址到高地址的)

    • 验证一(同时不管数组在栈区还是堆区,数组的生长方向都是从低地址到高地址的):

    在这里插入图片描述

    在这里插入图片描述

    • 验证二(验证空间布局):

    在这里插入图片描述

    在这里插入图片描述

    观察一个现象

    在这里插入图片描述

    在这里插入图片描述

    在父子进程中分别对a进行修改,但是我们发现a的地址都为同一个,说明同一个变量在同样的地址有不同的值,如何解释?

    • 变量内容不一样,所以父子进程输出的变量绝对不是同一个变量

    • 但地址值是一样的,说明该地址绝对不是物理地址!

    • 在Linux地址下,这种地址叫做虚拟地址

    • 我们在用C/C++语言(或者其他语言)所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理,OS必须负责将虚拟地址转化为物理地址。

    进程地址空间

    什么叫做地址空间?

    所谓的进程地址空间,本质是一个描述进程可视范围的大小,地址空间内一定要存在各种区域划分,对线性地址进行start,和end即可,在32位计算机中,有32位的地址和数据总线,所有地址总线排列组合形成地址范围[0,232],232 * 1byte也就是4GB大小

    • cpu内存在地址寄存器(AR),保存当前CPU所访问内存单元地址
    • 虚拟地址和物理地址通过页表进行映射(页表是一种软件结构)
    • 当子进程创建时,子进程会继承父进程的虚拟地址空间,也就是说子进程和父进程指向同一块物理空间,当子进程需要对数据进行修改时,物理空间会为子进程新开辟一块空间,存储子进程修改的数据(类似于写时拷贝)

    进程地址空间图示及笔记

    在这里插入图片描述

    从以上图可以知道,同一个变量,地址相同,其实是虚拟地址相同,内容不同其实是被映射到了不同的物理地址。

    • 所以,什么叫进程?
      进程 = 内核数据结构(task_struct && mm_struct && 页表)+ 程序的代码和数据

    所以进程在切换时task_struct && mm_struct && 页表都要进行切换

    • 进程具有独立性,怎么做到的?
    1. 内核数据结构上,进程之间是互相独立的
    2. 不同进程通过页表进行映射,将虚拟地址空间和物理地址空间进行解耦合,即便是父子进程关系只需要在页表层面上,代码区指向一样,数据区指向不一样,在数据层面上解耦合。进程若触发异常,只需要处理各自页表映射的数据即可
    • 因为有页表的存在,数据加载在物理内存的位置我们并不关心,我们只关心页表的映射关系即可。以统一的视角看待内存,让无序变有序
    • 环境变量是全局属性,因为子进程会根据父进程页表的映射关系,建立自己的页表

    Linux2.6内核进程调度队列(Linxu进程篇2有涉及)

    【Linux进程篇】进程概念2

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

    一个CPU拥有一个runqueue(运行队列)

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

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

    bitmap[5]:一共140个优先级,一共140个进程队列,为了提高查找非空队列的效率,就可以用5*32个 比特位表示队列是否为空,这样,便可以大大提高查找效率!

    过期队列
    • 过期队列和活动队列结构一模一样
    • 过期队列上放置的进程,都是时间片耗尽的进程
    • 当活动队列上的进程都被处理完毕之后,对过期队列的进程进行时间片重新计算
    active指针和expired指针
    • active指针永远指向活动队列
    • expired指针永远指向过期队列
    • 可是活动队列上的进程会越来越少,过期队列上的进程会越来越多,因为进程时间片到期时一直都存在的。
    • 在合适的时候,只要能够交换active指针和expired指针的内容,就相当于有具有了一批新的活动进程!
    总结
    • 在系统当中查找一个最合适调度的进程的时间复杂度是一个常数,不随着进程增多而导致时间成本增加,我们称之为进程调度O(1)算法。

  • 相关阅读:
    CobalStrike(CS)流量分析
    Spring的创建和使用
    代码随想录训练营Day56:Leetcode647、516
    不是单例的单例——巧用ClassLoader
    【微信小程序】前端+后端 :第一篇(基于javaweb 案例)
    cobol数据类型
    扫描车牌是什么神经网络,卷积神经网络车牌识别
    kubernetes kubelet Overiview
    简洁高效的微信小程序分页器封装实践
    18.1 new、delete进一步认识
  • 原文地址:https://blog.csdn.net/m0_74215144/article/details/134015881