程序地址空间

1.进程的地址空间

我们看一段代码

复制代码
#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 == 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);
        }
    }
}

我们可以看到,我们创建的父进程和子进程,都指向g_val,当子进程改变g_val的值,但是父进程指向的g_val的值还是没有变的,而且二者的地址都是一样的,这是为什么呢?

1.地址空间

由于父子进程输出变量的内容不一样,而且进程具有独立性,所以我们可以推断,这绝对不是同一个变量,那么相同的地址又是怎么回事呢?这就有了地址空间的概念了

在操作系统内部,会有虚拟地址空间,通过页表的映射,访问物理内存空间,由于子进程拷贝的是父进程的数据,所以同一份代码,父子进程是共享的,当子进程要修改g_val的值时,又为了不影响父进程对g_val的使用,就有了写时拷贝,子进程会把父进程的很多数据结构都拷贝一份,包括虚拟地址空间,本质上虚拟地址空间也是一个结构体,当操作系统发现子进程的要更改的数据和父进程指向的数据冲突了,才会进行写时拷贝,在物理内存上开辟g_val的空间,子进程通过页表映射的物理内存地址也就和父进程页表映射的g_val的地址不一样了,

写时拷贝的意义?

因为每次创建子进程,都会拷贝父进程的代码和数据,消耗比较大,所以对于一些能够共享的代码,就尽量不拷贝,父子进程共享代码,当子进程需要修改代码而且影响父进程时,才会发生写时拷贝,达到有效节省空间的目的

2.地址空间的本质

地址空间其实时一个结构体,在linux中以mm_struct命名,地址空间其实是一个虚拟的空间,他通过比较有序的管理地址的方式,创造了遍历,因为在真实物理地址空间里,数据的分布是杂乱的,堆空间,栈空间,还有代码段等等,不好管理,所以通过虚拟地址空间,然后经过页表的映射,将无序变成有序,让进程以统一的视角看待物理内存以及自己运行的各个区域,

还可以拦截非法请求,当我i们访问的虚拟地址空间中的一个地址,在页表中没有对应的物理内存地址,就会拦截请求,所以地址空间还是比较好的

2.linux O(1)算法

linux系统中每一个cpu都有一个运行队列,运行队列中有array[0]和array[1]两个数组,

nr_active表示有多少个运行状态的进程,

bitmap[5]是一个位图,在32位平台下,32*5能表示160个数据单位,而我们的优先级只有140个,对于优先级,有实时优先级:0-99,我们先不考虑,100-139是普通优先级,所以bitmap[ 5]可以快速查找非空队列,queue[140]:一个元素就是一个进程队列,相同优先级的队列,按照FIFO(先进先出)规则进行排队调度,不同优先级按照下标对号入座,

我们一般把array[0]叫做活跃队列,array[1]叫做过期队列

active指针指向活动队列,expired指针指向过期队列,活动队列包含了当前时间片尚未运行完的进程,当cpu执行进程时,会先把活动队列上的进程执行完,执行完的进程转移到过期队列中,当active队列通过biemap[5]发现队列执行完了,就会交换active和expired指针,然后过期队列就变成了活跃队列,这个操作时O(1)的,效率很高,所以叫做进程调度O(1)算法

相关推荐
jyyyx的算法博客几秒前
Leetcode 2337 -- 双指针 | 脑筋急转弯
算法·leetcode
SweetCode12 分钟前
裴蜀定理:整数解的奥秘
数据结构·python·线性代数·算法·机器学习
ゞ 正在缓冲99%…25 分钟前
leetcode76.最小覆盖子串
java·算法·leetcode·字符串·双指针·滑动窗口
xuanjiong26 分钟前
纯个人整理,蓝桥杯使用的算法模板day2(0-1背包问题),手打个人理解注释,超全面,且均已验证成功(附带详细手写“模拟流程图”,全网首个
算法·蓝桥杯·动态规划
go_bai36 分钟前
Linux环境基础开发工具——(2)vim
linux·开发语言·经验分享·笔记·vim·学习方法
ghostwritten36 分钟前
Docker Registry Clean
运维·docker·容器
惊鸿.Jh1 小时前
【滑动窗口】3254. 长度为 K 的子数组的能量值 I
数据结构·算法·leetcode
明灯L1 小时前
《函数基础与内存机制深度剖析:从 return 语句到各类经典编程题详解》
经验分享·python·算法·链表·经典例题
碳基学AI1 小时前
哈尔滨工业大学DeepSeek公开课:探索大模型原理、技术与应用从GPT到DeepSeek|附视频与讲义免费下载方法
大数据·人工智能·python·gpt·算法·语言模型·集成学习
niuniu_6661 小时前
简单的自动化场景(以 Chrome 浏览器 为例)
运维·chrome·python·selenium·测试工具·自动化·安全性测试