热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

LinuxX86_64位虚拟地址空间布局与试验

LinuxX86_64位虚拟地址空间布局与试验,Go语言社区,Golang程序员人脉社

Linux虚拟地址布局

x64 layout

在x86_64下面,其实虚拟地址只使用了48位。所以C程序里,打印的地址都是只有12位16进制。48位地址长度也就是对应了256TB的地址空间。

而在Linux下有效的地址区间是从 0x00000000 00000000 ~ 0x00007FFF FFFFFFFF 还有 0xFFFF8000 00000000 ~ 0xFFFFFFFF FFFFFFFF 两个地址区间。而每个地址区间都有128TB的地址空间可以使用,所以总共是256TB的可用空间。地址空间的划分就如下所示

    ffffffff`ffffffff     _____________   
                          |            |   
                          |   内核空间  |   
    ffff8000`00000000     |____________|   
                          |            |   
                          |   未使用    |   
                          |   的空间    |   
                          |            |   
    00007fff`ffffffff     |____________|   
                          |            |   
                          |   用户空间  |   
    00000000`00000000     |____________|

下面分析用户空间的地址布局。一个程序的内存布局,可以通过

cat /proc/<pid>/maps
pmap <pid> -X

这里写图片描述

这是一个简单的helloworld程序的示例布局

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

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

具体的x64的内存布局如下图所示:

这里写图片描述

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

brk尝试

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

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

初始状态

这里写图片描述

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

这里写图片描述

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

这里写图片描述

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

mmap尝试

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

#include 
#include 
#include 
#include 
#include 

int main()
{
    void * addr;
    printf("Welcome to sbrk example:%dn", 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 :%pn", 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大。


推荐阅读
  • 数字图书馆近期展出了一批精选的Linux经典著作,这些书籍虽然部分较为陈旧,但依然具有重要的参考价值。如需转载相关内容,请务必注明来源:小文论坛(http://www.xiaowenbbs.com)。 ... [详细]
  • 深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案
    深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案 ... [详细]
  • 线程能否先以安全方式获取对象,再进行非安全发布? ... [详细]
  • 分享一款基于Java开发的经典贪吃蛇游戏实现
    本文介绍了一款使用Java语言开发的经典贪吃蛇游戏的实现。游戏主要由两个核心类组成:`GameFrame` 和 `GamePanel`。`GameFrame` 类负责设置游戏窗口的标题、关闭按钮以及是否允许调整窗口大小,并初始化数据模型以支持绘制操作。`GamePanel` 类则负责管理游戏中的蛇和苹果的逻辑与渲染,确保游戏的流畅运行和良好的用户体验。 ... [详细]
  • Unity与MySQL连接过程中出现的新挑战及解决方案探析 ... [详细]
  • 在对WordPress Duplicator插件0.4.4版本的安全评估中,发现其存在跨站脚本(XSS)攻击漏洞。此漏洞可能被利用进行恶意操作,建议用户及时更新至最新版本以确保系统安全。测试方法仅限于安全研究和教学目的,使用时需自行承担风险。漏洞编号:HTB23162。 ... [详细]
  • 利用 Python Socket 实现 ICMP 协议下的网络通信
    在计算机网络课程的2.1实验中,学生需要通过Python Socket编程实现一种基于ICMP协议的网络通信功能。与操作系统自带的Ping命令类似,该实验要求学生开发一个简化的、非标准的ICMP通信程序,以加深对ICMP协议及其在网络通信中的应用的理解。通过这一实验,学生将掌握如何使用Python Socket库来构建和解析ICMP数据包,并实现基本的网络探测功能。 ... [详细]
  • 在Java基础中,私有静态内部类是一种常见的设计模式,主要用于防止外部类的直接调用或实例化。这种内部类仅服务于其所属的外部类,确保了代码的封装性和安全性。通过分析JDK源码,我们可以发现许多常用类中都包含了私有静态内部类,这些内部类虽然功能强大,但其复杂性往往让人感到困惑。本文将深入探讨私有静态内部类的作用、实现方式及其在实际开发中的应用,帮助读者更好地理解和使用这一重要的编程技巧。 ... [详细]
  • 本文详细探讨了使用纯JavaScript开发经典贪吃蛇游戏的技术细节和实现方法。通过具体的代码示例,深入解析了游戏逻辑、动画效果及用户交互的实现过程,为开发者提供了宝贵的参考和实践经验。 ... [详细]
  • 在 CentOS 7 中,为了扩展可用软件包的数量,通常需要配置多个第三方软件源。这些第三方源包括 EPEL、Nux Dextop 和 ELRepo 等,它们提供了大量官方源中未包含的软件包,从而增强了系统的功能性和灵活性。通过正确配置这些源,用户可以轻松安装和管理更多种类的软件,满足不同的需求。 ... [详细]
  • 深入解析Postman内置变量的实用技巧与示例代码
    本文详细探讨了Postman内置变量的实用技巧和应用案例,通过具体的示例代码,全面解析了这些变量在实际开发和测试中的使用方法,为读者提供了宝贵的学习和参考资源。 ... [详细]
  • HTML 页面中调用 JavaScript 函数生成随机数值并自动展示
    在HTML页面中,通过调用JavaScript函数生成随机数值,并将其自动展示在页面上。具体实现包括构建HTML页面结构,定义JavaScript函数以生成随机数,以及在页面加载时自动调用该函数并将结果呈现给用户。 ... [详细]
  • 本文介绍了UUID(通用唯一标识符)的概念及其在JavaScript中生成Java兼容UUID的代码实现与优化技巧。UUID是一个128位的唯一标识符,广泛应用于分布式系统中以确保唯一性。文章详细探讨了如何利用JavaScript生成符合Java标准的UUID,并提供了多种优化方法,以提高生成效率和兼容性。 ... [详细]
  • 通过使用CIFAR-10数据集,本文详细介绍了如何快速掌握Mixup数据增强技术,并展示了该方法在图像分类任务中的显著效果。实验结果表明,Mixup能够有效提高模型的泛化能力和分类精度,为图像识别领域的研究提供了有价值的参考。 ... [详细]
  • 哈希表(Hash Table)是一种高效的查找算法,与传统的链表和树结构相比,其在查找过程中无需进行逐个元素的比较。本文将深入探讨哈希表的基本原理、应用场景以及优化策略,帮助读者全面理解其在实际开发中的优势和局限性。通过实例分析和代码示例,我们将展示如何有效利用哈希表提高数据处理效率,并解决常见的冲突问题。 ... [详细]
author-avatar
可可伦-惊叹号
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有