首页 > 解决方案 > 为什么 brk(void *end_data_segment) 的参数不向上舍入到下一页边界?

问题描述

Linux 编程接口

int brk(void * end_data_segment );

系统调用将brk()程序中断设置为由 指定的位置end_data_segment。由于虚拟内存是以页面为单位分配的,end_data_segment因此有效地向上舍入到下一个页面边界。

所以对于这个演示:

#include <stdio.h>
#include <unistd.h>


int
main(int argc, char *argv[])
{
    long int page_size = sysconf(_SC_PAGESIZE);
    printf("My page size: %ld\n", page_size);
    void* c1 = sbrk(0);
    printf("program break address: %p\n", c1);
    printf("sizeof char: %lu\n", sizeof(char));
    c1 = (void*) ((char*) c1 + 1);
    printf("c1: %p\n", c1);
    brk(c1);
    void* c2 = sbrk(0);
    printf("program break address: %p\n", c2); 

}

输出:

My page size: 4096
program break address: 0x55b0bc104000
sizeof char: 1
c1: 0x55b0bc104001
program break address: 0x55b0bc104001

我希望新的程序中断地址是:0x55b0bc104000 + 0x1000(4096 in HEX) == 0x55b0bc105000

为什么我没有得到0x55b0bc105000,而是0x55b0bc104001

标签: clinuxbrk

解决方案


将其视为两种可能性:

  • end_data_segment与页面边界对齐;并确保虚拟地址空间底层区域的大小与end_data_segment值完全匹配

  • 不要将 对齐end_data_segment到页面边界;并确保虚拟地址空间底层区域的大小与页面大小对齐(向上舍入)

对于第一种可能性,便携式软件(不知道页面大小将是多少)可以(例如)将页面增加end_data_segment1/8 并分别执行 8 次,而不是最终得到一个额外的页面(结果你自然会期望)它最终会多出 8 页(比它想要的多 7 页,比预期的多 7 页)。更差; 软件可以减少end_data_segment不到一页的大小,它什么也不做(将四舍五入到原始值);这也可以多次执行,导致软件试图摆脱但仍然存在的大面积区域。当然这些可以组合 - 软件可以增加end_data_segment1 个字节然后减少end_data_segment在循环中间减少一个字节,导致意外的内存(空间)泄漏,当人们期望它不会浪费任何东西时,它可能会迅速吞噬所有可用的虚拟地址空间。当然,软件可以通过在各处添加(非标准/不可移植)修复来明确解决所有问题,但这将非常难看。

第二种可能性(不对齐end_data_segment)更直观、更方便且不易出错。


推荐阅读