当前位置: 首页 > news >正文

【Linux】进程概念(下篇) —— 程序地址空间详解

🎇Linux:进程概念


  • 博客主页:一起去看日落吗
  • 分享博主的在Linux中学习到的知识和遇到的问题
  • 博主的能力有限,出现错误希望大家不吝赐教
  • 分享给大家一句我很喜欢的话: 看似不起波澜的日复一日,一定会在某一天让你看见坚持的意义,祝我们都能在鸡零狗碎里找到闪闪的快乐🌿🌞🐾。

在这里插入图片描述

目录

  • 🎃程序地址空间回顾
  • 🎃验证进程地址空间的基本排布
  • 🎃什么是进程地址空间
  • 🎃理解进程地址空间
  • 🎃Linux2.6内核进程调度队列 —— 了解
    • 👻进程队列数据结构图
    • 👻一个CPU拥有一个runqueue
    • 👻优先级
    • 👻活动队列
    • 👻过期队列
    • 👻active指针和expired指针

🎃程序地址空间回顾

我们在学C语言的时候,老师应该给大家画过这样的空间布局图

在这里插入图片描述

这张图到底有什么含义呢?我们今天来详细学习一下,C/C++ 进程地址空间是一个很重要的概念,关系到我们能否学好编程语言


🎃验证进程地址空间的基本排布

话不多说,我们直接用一段代码来验证上面那张图是否正确。

#include<stdio.h>
#include<stdlib.h>
  
int g_unval;
int g_val = 100;
   
int main(int argc, char* argv[], char* env[])
{
	printf("code addr:         %p\n", main);
    const char* p = "hello bit!";
    printf("read only:         %p\n", p);
    static int a = 5;
    printf("static global val: %p\n", &a);
    printf("global val:        %p\n", &g_val);
    printf("global uninit val: %p\n", &g_unval);
    char* q1 = (char*)malloc(10);
    char* q2 = (char*)malloc(10);
    printf("heap addr:         %p\n", q1); 
    printf("heap addr:         %p\n", q2); 
    printf("p stack addr:      %p\n", &p);
    printf("q1 stack addr:     %p\n", &q1);
    printf("args addr:         %p\n", argv[0]);
    printf("args addr:         %p\n", argv[argc - 1]);
    printf("env addr:          %p\n", env[0]);
    
    return 0;                                                                                                                                                                           
}

在这里插入图片描述

从结果我们得知:

  • 栈区地址是由高到低增长的
  • 堆区地址是由低到高增长的

细心的小伙伴就会发现,两个args addr的地址为什么是一样的呢?那我们再进一步验证一下

加上选项

-a -b

请添加图片描述

根本原因是原本只有一行命令行参数,如果加上了选项就不一样了


🎃什么是进程地址空间

直接上代码观察

#include<stdio.h>  
#include<unistd.h>  
#include<sys/types.h>  
   
int g_val = 0;  
  
int main()  
{  
	printf("begin......%d\n", g_val);                                                                                                                                                   
	pid_t id = fork();  
	if(id == 0)  
	{   
		int count = 0;  
		while(1)  
		{  
			printf("child pid: %d, ppid: %d, [g_val: %d][&g_val: %p]\n", getpid(), getppid(), g_val, &g_val);  
			sleep(1);  
			count++;  
			if(count == 5)  
			{  
				g_val = 100;  
			}  
		}  
	}  
	else if(id > 0)  
	{  
		while(1)
		{
			printf("father pid: %d, ppid: %d, [g_val: %d][&g_val: %p]\n", getpid(), getppid(), g_val, &g_val);  
			sleep(1);   		
		}                                                                                    
	}                                                                                                  
	else                                                                                               
	{                                                                                                  
		//TODO                                                                                         
	}                                                                                                  
	return 0;                                                                                          
}                            

在这里插入图片描述

我们发现,父子进程,输出地址是一致的,但是变量内容不一样!能得出如下结论:

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

可以知道&g_val一定不是物理地址(真正在内存中的地址),因为同一个物理地址处怎么可能读取到的是不同的值。所以我们断言曾经所看到的任何地址都不是物理地址,而这种地址本质是虚拟地址,它是由操作系统提供的,那么操作系统一定要有一种方式帮我们把虚拟地址转换为物理地址,因为数据和代码一定在物理内存上存储,这是由冯 • 诺依曼体系结构规定的。


🎃理解进程地址空间

地址空间在 Linux 内核中是一个mm_struct结构体,这个结构体没有告诉我们空间大小,但是它告诉我们空间排布情况,比如[code_start(0x1000), code_end(0x2000)],其中就会有若干虚拟地址,这是因为操作系统为了把物理内存包裹起来,给每个进程画的一把尺子,这把尺子我们叫进程地址空间。进程地址空间是在进程和物理内存之间的一个软件层,它通过mm_struct这样的结构体来模拟,让操作系统给进程画大饼,每一个进程可以根据地址空间来划分自己的代码。进程地址空间本质是进程看待物理内存的方式,它是抽象出来的概念,其中 Linux 内核中是用mm_struct数据结构来表示的。这样的话每个进程都认为自己独占系统内存资源(好比每个老婆都认为自己独占10亿);区域划分的本质是将线性地址空间划分成为一个一个的区域[start, end];而所谓的虚拟地址本质是在[start, end]之间的各个地址。

在这里插入图片描述


🎃Linux2.6内核进程调度队列 —— 了解

👻进程队列数据结构图

在这里插入图片描述


👻一个CPU拥有一个runqueue

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

👻优先级

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

👻活动队列

  • 时间片还没有结束的所有进程都按照优先级放在该队列
  • nr_active: 总共有多少个运行状态的进程
  • queue[140]: 一个元素就是一个进程队列,相同优先级的进程按照FIFO规则进行排队调度,所以,数组下标就是优先级!

从该结构中,选择一个最合适的进程,过程是怎么的呢?

  • 从0下表开始遍历queue[140]
  • 找到第一个非空队列,该队列必定为优先级最高的队列
  • 拿到选中队列的第一个进程,开始运行,调度完成!
  • 遍历queue[140]时间复杂度是常数!但还是太低效了!
  • bitmap[5]:一共140个优先级,一共140个进程队列,为了提高查找非空队列的效率,就可以用5*32个比特位表示队列是否为空,这样,便可以大大提高查找效率!

👻过期队列

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

👻active指针和expired指针

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

相关文章:

  • Windows上部署Discuz论坛
  • 猿创征文|我的C/C++技术成长之路
  • 微信小程序在线考试项目开发-用户信息注册登录功能
  • 微服务项目:尚融宝(36)(核心业务流程:用户绑定(1))
  • 2023秋招的第一个意向书
  • 正点原子Linux MINI板系统固化(烧录uboot、linux kernel、.dtb(设备树)和 rootfs)
  • 核酸检测小程序实战教程
  • MATLAB | 绘制博士学位的图解指南
  • 单词相似性查询易语言代码
  • 【Vue】Vue项目需求--实现搜索框输入防抖处理
  • 03Python数据类型
  • CSS常见选择器
  • React-函数组件的特性与闭包
  • MySQL的EXPLAIN执行计划深入分析
  • 【MySQL基础篇】MySQL数据库安装教程
  • 11111111
  • Angular 2 DI - IoC DI - 1
  • Apache Spark Streaming 使用实例
  • Electron入门介绍
  • ES10 特性的完整指南
  • JavaScript中的对象个人分享
  • Laravel深入学习6 - 应用体系结构:解耦事件处理器
  • mysql innodb 索引使用指南
  • Python爬虫--- 1.3 BS4库的解析器
  • Redux 中间件分析
  • spark本地环境的搭建到运行第一个spark程序
  • springMvc学习笔记(2)
  • TCP拥塞控制
  • tweak 支持第三方库
  • vue从创建到完整的饿了么(11)组件的使用(svg图标及watch的简单使用)
  • 从PHP迁移至Golang - 基础篇
  • 多线程事务回滚
  • 将回调地狱按在地上摩擦的Promise
  • 区块链将重新定义世界
  • 使用 Docker 部署 Spring Boot项目
  • 使用common-codec进行md5加密
  • 手机端车牌号码键盘的vue组件
  • 通过几道题目学习二叉搜索树
  • 职业生涯 一个六年开发经验的女程序员的心声。
  • Java性能优化之JVM GC(垃圾回收机制)
  • MyCAT水平分库
  • ​低代码平台的核心价值与优势
  • #我与Java虚拟机的故事#连载16:打开Java世界大门的钥匙
  • (arch)linux 转换文件编码格式
  • (LNMP) How To Install Linux, nginx, MySQL, PHP
  • (Redis使用系列) Springboot 实现Redis消息的订阅与分布 四
  • (附源码)ssm航空客运订票系统 毕业设计 141612
  • (附源码)基于SSM多源异构数据关联技术构建智能校园-计算机毕设 64366
  • (附源码)计算机毕业设计大学生兼职系统
  • (每日持续更新)jdk api之FileFilter基础、应用、实战
  • (十一)手动添加用户和文件的特殊权限
  • (推荐)叮当——中文语音对话机器人
  • (学习日记)2024.02.29:UCOSIII第二节
  • .Net 6.0 处理跨域的方式
  • .NET CF命令行调试器MDbg入门(二) 设备模拟器