uc_03_虚拟地址_内存映射

目录

1  虚拟地址

2  虚拟地址空间

3  虚拟地址空间布局

3.*    const int a = 5;

3.*  递归函数栈溢出?;

4  内存映射的建立与解除

4.1  mmap()  内存映射的建立

4.2  munmap()  内存映射的解除


1  虚拟地址

        我们在程序中看到的或使用的地址,不是真实的物理内存的地址,而是经由系统内核的内存管理系统管理后所看到的虚拟地址。

        虚拟地址和物理地址之间存在映射对应关系。

        内存管理系统一方面保证物理内存的安全,避免物理内存被直接操作。

        同时降低了程序员的编码难度(不用手算物理内存地址了)。

其他概念:

        磁盘上用来缓存物理内存数据的部分,称为交换分区,windows叫虚拟内存

2  虚拟地址空间

        对于32位操作系统而言,每个进程所得到的虚拟地址都在一个固定的范围内,该范围称为虚拟地址空间。

        虚拟地址空间本质是一个地址范围,表示程序的寻址能力,和物理内存大小无关(32位系统有4G虚拟地址空间,可用在512M内存条)。我们所看到的虚拟地址,都在这个范围内。

        32位操作系统,虚拟地址空间范围从0x0000 0000到0xFFFF FFFF,共4G:

                其中,0~3G-1范围归用户使用,称为用户地址空间。

                其中,3G~4G-1范围归内核使用,称为内核地址空间。

        64位操作系统,目前应用程序的内存需求不大,所以不支持完全的64位虚拟地址:

                用户地址空间范围0x0000 0000 0000 0000~0x0000 FFFF FFFF FFFF(256T?128T?)

                内核地址空间范围0xFFFF 0000 0000 0000~0xFFFF FFFF FFFF FFFF(256T?128T?)

                内核地址空间和用户地址空间之间是不规范地址空间,不允许使用。

        用户地址空间的代码不能直接防范内核空间的代码和数据,但可以通过系统调用进入内核态,间接与系统内核交互。

3  虚拟地址空间布局

        程序中不同性质的数据,加载到内存后,其虚拟地址会被映射到虚拟地址空间中不同区域。

        用户地址空间,从低地址到高地址布局如图:(数据区和BSS区可统称为数据区)

        

//map.c  虚拟地址空间布局
#include<stdio.h>
#include<stdlib.h> // malloc()
const int const_global = 10;//常全局变量
int init_global = 20;//初始化全局变量
int uninit_global;//未初始化全局变量

int main(int argc,char* argv[],char* envp[]){
    static const int const_static = 30;//常静态变量
    static int init_static = 40;//初始化静态变量
    static int uninit_static;//未初始化静态变量
    const int const_local = 50;//常局部变量
    int local = 60;//局部变量
    char* string = "hello";//字面值常量
    int* heap = malloc(sizeof(int));//堆变量
    printf("---------参数和环境区---------\n");
    printf("        命令行参数:%p\n",argv);
    printf("          环境变量:%p\n",envp);
    printf("-------------栈区-------------\n");
    printf("        常局部变量:%p\n",&const_local);
    printf("          局部变量:%p\n",&local);
    printf("-------------堆区-------------\n");
    printf("            堆变量:%p\n",heap);
    printf("------------BSS区-------------\n");
    printf("  未初始化全局变量:%p\n",&uninit_global);
    printf("  未初始化静态变量:%p\n",&uninit_static);
    printf("------------数据区------------\n");
    printf("    初始化全局变量:%p\n",&init_global);
    printf("    初始化静态变量:%p\n",&init_static);
    printf("------------代码区------------\n");
    printf("        常全局变量:%p\n",&const_global);
    printf("        常静态变量:%p\n",&const_static);
    printf("              函数:%p\n",main);
    printf("        字面值常量:%p\n",string);
    printf("------------------------------\n");
    
    return 0;
}

编译执行,结果如下:

uc_03$gcc map.c -o map
uc_03$./map
---------参数和环境区---------
        命令行参数:0x7fff2fa44938
          环境变量:0x7fff2fa44948
-------------栈区-------------
        常局部变量:0x7fff2fa44830
          局部变量:0x7fff2fa44834
-------------堆区-------------
            堆变量:0x871010
------------BSS区-------------
  未初始化全局变量:0x601060
  未初始化静态变量:0x60105c
------------数据区------------
    初始化全局变量:0x601050
    初始化静态变量:0x601054
------------代码区------------
        常全局变量:0x400868
        常静态变量:0x400ae0
              函数:0x400626
        字面值常量:0x40086c
------------------------------
uc_03$

3.*    const int a = 5;

        该变量在函数外,就是常全局变量,地址被映射到虚拟地址空间-用户地址空间的代码区。

        该变量在函数内,就是非静态局部变量,地址被映射到虚拟地址空间-用户地址空间的栈区;

3.*  递归函数溢出?;

        问:函数地址在代码区,为什么递归函数不当,却显示溢出?

        答:函数的本质是代码块。

               调用函数 == 执行代码块,此时是通过函数地址(代码区)调用函数没错。

               但函数中局部变量的地址是在栈区中的,栈帧可下移,过量时栈溢出。

4  内存映射的建立与解除

4.1  mmap()  内存映射的建立

        malloc()底层调用的就是mmap()。

        没有与物理地址建立映射关系的虚拟地址,无法直接使用,我们可以通过系统调用mmap函数,来手动建立虚拟地址和物理地址之间的映射关系。

        #include <sys/mman.h>

        void* mmap(void* start,    size_t length,    int prot,    int flags,    int fd,    off_t offset);

                -功能:建立虚拟内存 到 物理内存磁盘文件的映射

                -参数:start  映射区虚拟内存的起始地址,NULL则系统自动选定后返回,推荐。

                            length  映射区字节数,自动按页圆4096取整。多页在虚拟内存空间中连续,

                                        在物理内存空间中不一定连续。(同一页圆内,一定连续)

                            prot  映射区操作权限,可取以下值:

                                    PROT_READ   映射区可读

                                    PROT_WRITE   映射区可写

                                    PROT_EXEC   映射区可执行

                                    PROT_NONE   映射区不可访问

                            flags  映射标志,可取以下值:

                                    MAP_ANONYMOUS  匿名映射,将虚拟内存映射到物理内存而非文件,

                                                                        忽略fd和offset函数

                                    MAP_PRIVATE  对映射区的写操作只反映到缓冲区,并不真正写入文件

                                    MAP_SHARED  对映射区的写操作直接反映到文杰中

                                    MAP_DENYWRITE  拒绝其它对文件的写操作

                                    MAP_FIXED  若在start上无法创建映射,则失败(无此标志系统会自动调整)

                            fd  文件描述符

                            offset  文件偏移量,自动按页(4K)对齐

                -返回值  成功返回映射区虚拟内存的起始地址,失败返回MAP_FAILED(-1)

//mmap.c  内存映射
#include<stdio.h>
#include<sys/mman.h>// mmap() munmap()
#include<string.h>
int main(void){
    //建立内存映射
    char* start = mmap(NULL,8192,PROT_READ | PROT_WRITE,MAP_PRIVATE | MAP_ANONYMOUS,0,0);
    if(start == MAP_FAILED){
        perror("mmap");
        return -1;
    }

    //使用映射得到的虚拟地址访问内存
    strcpy(start,"铁锅炖大鹅");
    printf("%s\n",start);
    //解除第一个页映射
    if(munmap(start,4096) == -1){
        perror("munmap");
        return -1;
    }
    //printf("%s\n",start); //段错误
    //第二个页的起始虚拟地址
    char* start2 = start + 4096;
    strcpy(start2,"小鸡炖蘑菇");
    printf("%s\n",start2);
    //解除第二个页的映射
    if(munmap(start2,4096) == -1){
        perror("munmap");
        return -1;
    }
    //printf("%s\n",start2); //段错误
    
    return 0;
}

4.2  munmap()  内存映射的解除

        #include<sys/mman.h>

        int munmap(void* start,    size_t length);

                -功能:解除虚拟内存到物理内存或磁盘文件的映射

                -参数:start  映射区虚拟内存的起始地址。

                            length  映射区字节数,自动按页圆取整。

                -返回值:成功返0,失败返-1

        munmap()允许对映射区的一部分解除映射,但必须按页处理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值