4. x86 - 64 的虚拟内存空间管理

前言:

之前有写过一篇博客,是关于进程从创建到加载的流程,并给出了一个示意图。今天就慢慢深入,从头再理一下,并给出一些关键的函数调用及代码。

一、文件从创建到加载

二、x86_64的用户空间布局

这一部分,网上看到一个很好的博客,用例子来讲解内容,推荐给大家,参考博客:https://blog.csdn.net/wu_roc/article/details/77203480

下图为一个简单的helloworld程序的实例布局:

  1. 前三行分别是Text Segment、Data Segment和BSS Segment,Text Segment其实就是存放二进制可执行代码的位置,所以它的权限是读与可执行,Data Segment存放的是静态常量,所以该地址段权限是只读,BSS Segment存放未初始化的静态变量,所以也就是可以随意读写。
  2. 接下来是Heap地址段,heap地址是往高地址增长的,是用来动态分配内存的区域。它跟栈相反,是往高地址增长的,对应的内存申请系统调用是brk()。
  3. 接下来的区域是Memory Mapping Segment。这块地址也是用来分配内存区域的,一般是用来把文件映射进内存用的,但是你也可以直接在这里申请内存空间来使用,对应的内存申请系统调用是mmap()。
  4. 再下面就是Stack地址段了。这个栈已经用了136KB了。栈的最大范围,我们可以通过prlimit命令看到,默认的情况下是8MB,和Linux-x86一样。
  5. 再下面就是vvar,vdso和vsyscall了。这三个东西都为了加速访问内核数据,比如读取时间gettimeofday,肯定不能频繁地进行系统调用陷入内核,所以就映射到用户空间了。所有程序都有这3个映射地址段。

关于vvar,vdso和vsyscall。先说vsyscall,这东西出现最早,比如读取时间gettimeofday,内核会把时间数据和gettimeofday的实现映射到这块区域,用户空间可以直接调用。但是vsyscall区域太小了,而且映射区域固定,有安全问题。后来又造出了vdso,之所以保留是为了兼容用户空间程序。vdso相当于加载一个linux-vd.so库文件一样(名字也由此而来),也就是把一些函数实现映射到这个区域,而vvar也就是存放数据的地方了,那么用户可以通过调用vdso里的函数,使用vvar里的数据,来获得自己想要的信息。而且地址是随机的,更安全。

三、x86 - 64内存空间布局图:

可以发现里面有不少Random xx offset,这是Linux里的ASLR策略。ASLR的话就是Address space layout randomization,是一种安全机制,主要防止缓冲区溢出攻击。

四、通过brk系统调用分配函数:

brk系统调用可以通过调整heap区域的brk指针,从而调整heap对的虚拟内存空间大小。实验代码如下:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
    void *curr_brk, *tmp_brk = NULL;
    printf("Welcome to sbrk example:%d\n", getpid());
    tmp_brk = curr_brk = sbrk(0);   //获得当然brk的地址
    printf("Program Break Location1:%p\n", curr_brk);
    getchar();
    brk(curr_brk+4096); //增加Heap段4KB空间
    curr_brk = sbrk(0);
    printf("Program break Location2:%p\n", curr_brk);
    int* a = (int *)tmp_brk+32; //尝试使用刚刚扩展的内存空间
    *a = 32;
    printf("%d\n", *a); //没有问题
    getchar();
    brk(tmp_brk);   //恢复到原来的brk大小
    curr_brk = sbrk(0);
    printf("Program Break Location3:%p\n", curr_brk);
    getchar();
    printf("%d\n", *a); //产生SIGSEGV,Segmentation Fault
    return 0;
}
  • 初始状态:

  • 按回车,调用brk(curr_brk+4096)之后:

  • 看到heap大小从132KB变成了136KB,而且地址空间可以正常使用。再次回车,调用brk(tmp_brk)

  • 发现heap堆大小变回去了。再回车发现会产生段错误,因为内存已经被回收,进程无法使用该内存地址。

五、通过mmap系统调用分配函数:

mmap系统调用是把一个文件映射到一段内存地址空间,如上面截图里的/lib/libc.so所做的一样。也可以匿名直接申请一段内存空间使用。实验代码如下:

#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <errno.h>

int main()
{
    void * addr;
    printf("Welcome to sbrk example:%d\n", getpid());
    getchar();
    //申请4096*2=8KB的空间,地址空间可读写,私有且匿名。
    addr = mmap(NULL, 4096 * 2, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
    if(addr < 0) {
        perror("mmap");
    }
    printf("Program Allocate :%p\n", addr);
    getchar();
    if(munmap(addr, 1024) != 0){
        perror("munmap");
    }
    return 0;
}
  • 运行结果:在mmap之前,有这么一条地址空间,大小是12KB:

  • 在mmap之后,发现大小变成了20KB,刚好是我们申请了8KB:

注意mmap申请内存的时候,如果申请地址长度小于一个PAGE_SIZE=2KB=4096字节=0x1000字节,那么会直接申请2KB。而PAGE_SIZE的话可以通过getconf PAGE_SIZE命令来查看。

其实mmap不一定要在Memory Mapping Segment进行内存申请,你可以指定任意的内存地址,当然只要不跟已有的冲突就好。这个地址也一定要是000结尾,才使得能页对齐。比如你可以申请0x100000的地址段,他的地址比Text Segment的地址更低。这没有任何问题。而mmap可申请的最低的地址由一个mmap_min_addr内核参数来控制,可以通过下面的命令读取。

sysctl vm.mmap_min_addr

在debian下,它的默认值是4096。也就是你申请的首地址必须比0x1000大。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AMD64/x86-64是一种处理器架构,它是x86架构的64位扩展。它由AMD公司于2003年推出,并在Intel的支持下成为主流的64位计算机架构。AMD64/x86-64架构具有以下特点[^1]: 1. 64位寻址空间:AMD64/x86-64架构支持64位寻址空间,可以处理更大的内存范围,最多可寻址2^64个字节的内存。 2. 兼容性:AMD64/x86-64架构是向后兼容的,可以运行32位和16位的x86软件。这意味着现有的x86软件可以无需修改直接在AMD64/x86-64架构上运行。 3. 扩展指令集:AMD64/x86-64架构引入了一些新的指令集扩展,如SSE(Streaming SIMD Extensions)和AVX(Advanced Vector Extensions),提供了更高的计算性能和并行处理能力。 4. 更多的通用寄存器:AMD64/x86-64架构增加了通用寄存器的数量,从8个增加到16个,这样可以提高程序的性能和效率。 5. 更大的整数和浮点数寄存器:AMD64/x86-64架构扩展了整数和浮点数寄存器的位数,使得处理更大的整数和浮点数数据更加高效。 6. 更好的内存管理:AMD64/x86-64架构引入了更高效的内存管理机制,如更大的虚拟地址空间和更灵活的分页机制,提高了系统的性能和可靠性。 总结起来,AMD64/x86-64架构是一种先进的64位处理器架构,具有更大的寻址空间、兼容性、扩展指令集、更多的寄存器和更好的内存管理等特点,为计算机系统提供了更高的性能和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值