简体   繁体   English

如何以编程方式获取 Linux 上的堆地址

[英]How to programmatically get the address of the heap on Linux

I can get the address of the end of the heap with sbrk(0) , but is there any way to programmatically get the address of the start of the heap, other than by parsing the contents of /proc/self/maps ?我可以使用sbrk(0)获取堆末尾的地址,但是除了解析/proc/self/maps的内容之外,有没有办法以编程方式获取堆的开头地址?

I think parsing /proc/self/maps is the only reliable way on the Linux to find the heap segment. 我认为解析/proc/self/maps是Linux上唯一可靠的查找堆段的方法。 And do not forget that some allocators (including one in my SLES) do use for large blocks mmap() thus the memory isn't part of the heap anymore and can be at any random location. 并且不要忘记一些分配器(包括我的SLES中的一个)确实用于大块mmap()因此内存不再是堆的一部分,并且可以在任何随机位置。

Otherwise, normally ld adds a symbol which marks the end of all segments in elf and the symbol is called _end . 否则,通常ld添加一个符号,该符号标记elf中所有段的结尾,符号称为_end Eg: 例如:

extern void *_end;
printf( "%p\n", &_end );

It matches the end of the .bss , traditionally the last segment of elf. 它匹配.bss的末尾,传统上是精灵的最后一段。 After the address, with some alignment, normally follows the heap. 在地址之后,通过一些对齐,通常在堆之后。 Stack(s) and mmap()s (including the shared libraries) are at the higher addresses of the address space. Stack(s)和mmap()s(包括共享库)位于地址空间的较高地址。

I'm not sure how portable it is, but apparently it works same way on the Solaris 10. On HP-UX 11 the map looks different and heap appears to be merged with data segment, but allocations do happen after the _end . 我不确定它是多么可移植,但显然它在Solaris 10上的工作方式相同。在HP-UX 11上,映射看起来不同,堆似乎与数据段合并,但分配确实发生在_end On AIX, procmap doesn't show heap/data segment at all, but allocations too get the addresses past the _end symbol. 在AIX上, procmap根本不显示堆/数据段,但是分配也会获得超过_end符号的地址。 So it seems to be at the moment quite portable. 所以它似乎目前非常便携。

Though, all considered, I'm not sure how useful that is. 虽然,所有人都考虑过,但我不确定它有多大用处。

PS The test program: PS测试程序:

#include <stdio.h>
#include <stdlib.h>

char *ppp1 = "hello world";
char ppp0[] = "hello world";
extern void *_end; /* any type would do, only its address is important */

int main()
{
    void *p = calloc(10000,1);
    printf( "end:%p heap:%p rodata:%p data:%p\n", &_end, p, ppp1, ppp0 );
    sleep(10000); /* sleep to give chance to look at the process memory map */
    return 0;
}

You may call sbrk(0) to get the start of the heap, but you have to make sure no memory has been allocated yet.您可以调用sbrk(0)来获取堆的开始,但您必须确保尚未分配 memory。

The best way to do this is to assign the return value at the very beginning of main() .最好的方法是在main()的最开始分配返回值。 Note that many functions do allocate memory under the hood, so a call to sbrk(0) after a printf , a memory utility like mtrace or even a call to putenv will already return an offset value.请注意,许多函数确实在后台分配了 memory,因此在printf之后对sbrk(0)的调用, putenv实用程序已经返回一个偏移值mtrace甚至调用。

Although much of what we can find say that the heap is right next to.bss, I am not sure what is in the difference between end and the first break.尽管我们可以找到的大部分内容都说堆就在.bss 旁边,但我不确定end和 first break 之间的区别是什么。 Reading there seems to results in a segmentation fault.在那里阅读似乎会导致分段错误。 The difference between the first break and the first address returned by malloc is, among (probably) other thing: malloc返回的第一个中断和第一个地址之间的区别是(可能)其他事情:

  • the head of the memory double-linked-list, including the next free block memory 双链表的头部,包括下一个空闲块
  • a structure prefixed to the malloc ed block incuding:malloc ed 块为前缀的结构,包括:
    • the length of this block这个块的长度
    • the address of the previous free block前一个空闲块的地址
    • the address of the next free block下一个空闲块的地址
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>


void print_heap_line();

int main(int argc, char const *argv[])
{
    char* startbreak = sbrk(0);

    printf("pid: %d\n", getpid()); // printf is allocating memory
    char* lastbreak = sbrk(0);
    printf("heap: [%p - %p]\n", startbreak, lastbreak);

    long pagesize = sysconf(_SC_PAGESIZE);
    long diff = lastbreak - startbreak;
    printf("diff: %ld (%ld pages of %ld bytes)\n", diff, diff/pagesize, pagesize);

    print_heap_line();

    printf("\n\npress a key to finish...");
    getchar(); // gives you a chance to inspect /proc/pid/maps yourself
    return 0;
}

void print_heap_line() {
    int mapsfd = open("/proc/self/maps", O_RDONLY);
    if(mapsfd == -1) {
        fprintf(stderr, "open() failed: %s.\n", strerror(errno));
        exit(1);
    }
    char maps[BUFSIZ] = "";
    if(read(mapsfd, maps, BUFSIZ) == -1){
        fprintf(stderr, "read() failed: %s.\n", strerror(errno));
        exit(1);
    }
    if(close(mapsfd) == -1){
        fprintf(stderr, "close() failed: %s.\n", strerror(errno));
        exit(1);
    }

    char*  line = strtok(maps, "\n");
    while((line = strtok(NULL, "\n")) != NULL) {
        if(strstr(line, "heap") != NULL) {
            printf("\n\nfrom /proc/self/maps:\n%s\n", line);
            return;
        }
    }
}
pid: 29825
heap: [0x55fe05739000 - 0x55fe0575a000]
diff: 135168 (33 pages of 4096 bytes)


from /proc/self/maps:
55fe05739000-55fe0575a000 rw-p 00000000 00:00 0                          [heap]


press a key to finish...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM