0
点赞
收藏
分享

微信扫一扫

【Linux】解密操作系统中的进程地址空间与页表管理

elvinyang 04-06 12:00 阅读 1

文章目录

介绍

在操作系统中,每个进程都有自己的地址空间,用于存放代码、数据和堆栈等信息。同时,操作系统通过页表管理着地址空间与物理内存之间的映射关系。本文将深入探讨进程地址空间的布局和页表的作用。

程序地址的空间布局图:

进程地址空间是指进程可用地址范围的集合,通常是32位或64位的虚拟地址空间。
在这里插入图片描述

#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);
    printf("init data addr: %p\n", &g_val);
    printf("uninit data addr: %p\n", &g_unval);
    
    char *heap = (char*)malloc(20);
    char *heap1 = (char*)malloc(20);
    char *heap2 = (char*)malloc(20);
    char *heap3 = (char*)malloc(20);
    printf("heap addr: %p\n", heap);
    printf("heap1 addr: %p\n", heap1);
    printf("heap2 addr: %p\n", heap2);
    printf("heap3 addr: %p\n", heap3);

    printf("stack addr: %p\n", &heap);
    printf("stack1 addr: %p\n", &heap1);
    printf("stack2 addr: %p\n", &heap2);
    printf("stack3 addr: %p\n", &heap3);
    
    for(int i = 0; argv[i]; ++i) {
        printf("&argv[%d]=%p\n", i, argv+i);
    }
    for(int i = 0; env[i]; ++i) {
        printf("&env[%d]=%p\n", i, env+i);
    }

    return 0;
}

堆、栈的的地址方向相对而升。

在这里插入图片描述

虚拟地址/线性地址

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

int main() 
{
    pid_t id = fork();
    if(id == 0) 
    {
        int cnt = 0;
        while(1) {
            printf("child, pid: %d, ppid: %d, g_val: %d, &g_val:%p\n", getpid(), getpid(), g_val, &g_val);
            sleep(2);
            cnt++;
            if (cnt == 3) {
                g_val = 200;
                printf("child change g_val: 100->200\n");
            }
        } 
    }
    else
    {
        while(1) {
            printf("father, pid: %d, ppid: %d, g_val: %d, &g_val:%p\n", getpid(), getpid(), g_val, &g_val);
            sleep(2);
        }
    }
    return 0;
}

通过代码可以看出父子进程的地址都是相同的!修改后值不同但是地址是一样的。
在这里插入图片描述
所以,这个地址绝对不是物理地址!这个地址,叫做:虚拟地址/线性地址
我们所用到的所有地址,全部都不是物理地址!
因此,上述的程序地址的空间布局图,不是物理地址分布而是进程地址空间!

进程地址空间与页表

在操作系统中,进程地址空间和页表是重要的概念,它们为进程的内存管理提供了关键的支持。本文将深入探讨地址空间的概念、页表的作用以及为什么它们对于操作系统和应用程序至关重要。
在这里插入图片描述

1. 什么是地址空间?

地址空间是指进程可用于寻址的内存范围,每个进程都有自己独立的地址空间。在32位系统中,通常的地址空间范围为0到4GB。地址空间中通常包含以下字段:

struct XXX
{
	int code_start, code_end; // 代码段的起始和结束地址
	int init_start, init_end; // 已初始化的数据段的起始和结束地址
	int uninit_start, uninit_end;  // 未初始化的数据段(或称BSS段)的起始和结束地址
	int heap_start, XXX_end; // 堆的起始和结束地址(XXX_end可能是其他字段,例如全局变量区域)
	int statck_start; // 栈的起始地址
	...
}

在这里插入图片描述
这些字段划分了进程地址空间中不同数据段的范围,但值得注意的是,地址空间本身并不存储数据,而是提供了一种虚拟的视图,数据实际存储在物理内存中。为了管理这些虚拟地址与物理地址的映射关系,操作系统引入了页表的概念。

2. 为什么要有地址空间和页表?

a. 有序的内存管理

地址空间将物理内存从无序状态变为有序状态,使得进程可以以统一的视角看待内存。通过地址空间,进程可以将不同数据段组织在一起,简化了内存管理的复杂度。

b. 解耦合进程管理和内存管理

地址空间和页表的使用将进程管理和内存管理解耦合,使得系统更加灵活和可维护。进程可以独立于物理内存的变化而运行,从而提高了系统的稳定性和可靠性。

c. 内存安全的保护手段

地址空间和页表是保护内存安全的重要手段。通过控制页表的权限和访问规则,操作系统可以有效地防止进程越界访问内存或者修改其他进程的数据,保护了系统的安全性和稳定性。

3. 解释一些问题

在使用malloc或者new等动态内存分配函数时,实际上是在进程的虚拟地址空间中申请内存。这些函数并不直接操作物理内存,而是通过操作系统提供的内存管理机制来分配虚拟内存空间。当进程访问这些分配的内存时,如果页表中没有相应的映射,则会触发缺页中断,操作系统会根据需要将数据从物理内存加载到内存中,并更新页表的映射关系。

操作系统需要兼顾内存使用效率和资源利用率,确保内存不会空转,并提高动态内存分配函数的效率。这包括优化内存分配算法、提高内存分配速度以及减少内存碎片等方面的工作。

结论

进程地址空间和页表是操作系统中重要的概念,对于理解内存管理和进程间隔离具有重要意义。通过合理管理地址空间和页表,可以提高系统的稳定性和性能,同时有效保护内存安全。

举报

相关推荐

0 条评论