程序地址空间

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)算法

相关推荐
海棠蚀omo16 分钟前
解读Linux进程的“摩尔斯电码”:信号产生的原理与实践,掌控进程的生死时速
linux·操作系统
前端炒粉2 小时前
35.LRU 缓存
开发语言·javascript·数据结构·算法·缓存·js
断剑zou天涯4 小时前
【算法笔记】窗口内最大值或最小值的更新结构
java·笔记·算法
smj2302_796826524 小时前
解决leetcode第3753题范围内总波动值II
python·算法·leetcode
YouEmbedded6 小时前
解码UDP
linux·udp
骑着猪去兜风.6 小时前
线段树(二)
数据结构·算法
w***48826 小时前
Linux安装redis
linux·运维·redis
fengfuyao9857 小时前
竞争性自适应重加权算法(CARS)的MATLAB实现
算法
散峰而望7 小时前
C++数组(二)(算法竞赛)
开发语言·c++·算法·github
leoufung7 小时前
LeetCode 92 反转链表 II 全流程详解
算法·leetcode·链表