程序地址空间

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都有一个运行队列,运行队列中有array0和array1两个数组,

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

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

我们一般把array0叫做活跃队列,array1叫做过期队列

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

相关推荐
道尔柯南2 小时前
【Linux】线程概念与控制
linux
weixin_307779132 小时前
C++代码实现MATLAB中的ode23t函数功能
开发语言·c++·算法·matlab
wuminyu2 小时前
Markword在紧凑对象头上的实现原理剖析
java·linux·c语言·jvm·c++
萧西待水2 小时前
奥赛一本通 1451 棋盘游戏
算法·宽度优先
Niuguangshuo2 小时前
论文解读:Paraformer,非自回归中文 ASR 的并行 Transformer
算法·音视频·语音识别
千舟软件2 小时前
【宿舍管理小程序】新生入住不靠纸质表
大数据·运维·服务器·科技·业界资讯
鹿角片ljp2 小时前
LeetCode 78:子集|回溯、选与不选、递归和path快照
java·数据结构·算法
酸菜。2 小时前
DW_apb_wdt总结
linux
虚无的纽扣2 小时前
【Linux】一篇文章带你吃透进程控制:fork、wait、exit到底在做什么
linux·ubuntu
raindayinrain2 小时前
Linux 网络编程--套接字选项
linux·网络·套接字选项