理解linux中的进程内存映射

理解linux中的进程内存映射,linux,memory,memory-management,x86-64,glibc,Linux,Memory,Memory Management,X86 64,Glibc,我试图理解linux进程内存布局的基本原理,我得到了这个程序: #include <stdio.h> // standard io #include <stdlib.h> // C standard library #include <pthread.h> // threading #include <unistd.h> // unix standard library #include <sys/types.h> // system

我试图理解linux进程内存布局的基本原理,我得到了这个程序:

#include <stdio.h> // standard io
#include <stdlib.h> // C standard library
#include <pthread.h> // threading
#include <unistd.h> // unix standard library
#include <sys/types.h> // system types for linux

// getchar basically is like "read"
// it prompts the user for input
// in this case, the input is thrown away
// which makes similar to a "pause" continuation primitive 
// but a pause that is resolved through user input, which we promptly throw away!
void * thread_func (void * arg) {

    printf("Before malloc in thread 1\n");
    getchar();
    char * addr = (char *) malloc(1000);
    printf("After malloc and before free in thread 1\n");
    getchar();
    free(addr);
    printf("After free in thread 1\n");
    getchar();

}

int main () {

    char * addr;
    printf("Welcome to per thread arena example::%d\n", getpid());
    printf("Before malloc in the main thread\n");
    getchar();
    addr = (char *) malloc(1000);
    printf("After malloc and before free in main thread\n");
    getchar();
    free(addr);
    printf("After free in main thread\n");
    getchar();

    // pointer to the thread 1
    pthread_t thread_1;
    // pthread_* functions return 0 upon succeeding, and other numbers upon failing
    int pthread_status;

    pthread_status = pthread_create(&thread_1, NULL, thread_func, NULL);

    if (pthread_status != 0) {
        printf("Thread creation error\n");
        return -1;
    }

    // returned status code from thread_1
    void * thread_1_status;

    pthread_status = pthread_join(thread_1, &thread_1_status);

    if (pthread_status != 0) {
        printf("Thread join error\n");
        return -1;
    }

    return 0;
}
这些记忆区域的用途是什么

7fcc37491000-7fcc37691000 ---p 001ba000 08:01 1053757                    /lib/x86_64-linux-gnu/libc-2.19.so
...
7fcc37abe000-7fcc37ac1000 rw-p 00000000 00:00 0 
7fcc37ad8000-7fcc37adc000 rw-p 00000000 00:00 0 

然后,在运行程序后,我按enter键几次。在打印“线程1中的malloc之前”之后。内存布局如下所示:

00400000-00401000 r-xp 00000000 08:01 1323314                            /home/oscp/xg/c/memory_layout/a.out
00600000-00601000 r--p 00000000 08:01 1323314                            /home/oscp/xg/c/memory_layout/a.out
00601000-00602000 rw-p 00001000 08:01 1323314                            /home/oscp/xg/c/memory_layout/a.out
00632000-00653000 rw-p 00000000 00:00 0                                  [heap]
7fcc36ad6000-7fcc36ad7000 ---p 00000000 00:00 0 
7fcc36ad7000-7fcc372d7000 rw-p 00000000 00:00 0 
7fcc372d7000-7fcc37491000 r-xp 00000000 08:01 1053757                    /lib/x86_64-linux-gnu/libc-2.19.so
7fcc37491000-7fcc37691000 ---p 001ba000 08:01 1053757                    /lib/x86_64-linux-gnu/libc-2.19.so
7fcc37691000-7fcc37695000 r--p 001ba000 08:01 1053757                    /lib/x86_64-linux-gnu/libc-2.19.so
7fcc37695000-7fcc37697000 rw-p 001be000 08:01 1053757                    /lib/x86_64-linux-gnu/libc-2.19.so
7fcc37697000-7fcc3769c000 rw-p 00000000 00:00 0 
7fcc3769c000-7fcc376b5000 r-xp 00000000 08:01 1053877                    /lib/x86_64-linux-gnu/libpthread-2.19.so
7fcc376b5000-7fcc378b4000 ---p 00019000 08:01 1053877                    /lib/x86_64-linux-gnu/libpthread-2.19.so
7fcc378b4000-7fcc378b5000 r--p 00018000 08:01 1053877                    /lib/x86_64-linux-gnu/libpthread-2.19.so
7fcc378b5000-7fcc378b6000 rw-p 00019000 08:01 1053877                    /lib/x86_64-linux-gnu/libpthread-2.19.so
7fcc378b6000-7fcc378ba000 rw-p 00000000 00:00 0 
7fcc378ba000-7fcc378dd000 r-xp 00000000 08:01 1053733                    /lib/x86_64-linux-gnu/ld-2.19.so
7fcc37abe000-7fcc37ac1000 rw-p 00000000 00:00 0 
7fcc37ad8000-7fcc37adc000 rw-p 00000000 00:00 0 
7fcc37adc000-7fcc37add000 r--p 00022000 08:01 1053733                    /lib/x86_64-linux-gnu/ld-2.19.so
7fcc37add000-7fcc37ade000 rw-p 00023000 08:01 1053733                    /lib/x86_64-linux-gnu/ld-2.19.so
7fcc37ade000-7fcc37adf000 rw-p 00000000 00:00 0 
7ffdc1cff000-7ffdc1d20000 rw-p 00000000 00:00 0                          [stack]
7ffdc1dd8000-7ffdc1ddb000 r--p 00000000 00:00 0                          [vvar]
7ffdc1ddb000-7ffdc1ddd000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
这两个地区的目的是什么

7fcc36ad6000-7fcc36ad7000 ---p 00000000 00:00 0 
7fcc36ad7000-7fcc372d7000 rw-p 00000000 00:00 0 
在打印“线程1中的malloc之后和free之前”之后,它将在下面创建另外两个区域:

7fcc30000000-7fcc30021000 rw-p 00000000 00:00 0 
7fcc30021000-7fcc34000000 ---p 00000000 00:00 0 

这两个地区的目的是什么?

你的问题涉及许多完全不同的事情,因此答案很长

7fcc36ad6000-7fcc36ad7000 ---p 00000000 00:00 0 
7fcc36ad7000-7fcc372d7000 rw-p 00000000 00:00 0 
第一个问题是

7fcc37491000-7fcc37691000 ---p 001ba000 08:01 1053757                    /lib/x86_64-linux-gnu/libc-2.19.so

这个不可访问的内存区域是库的相邻ELF段之间的间隙(它应该占用连续的内存块)。
--p
保护模式禁止将此间隙用于临时内存分配。如果在加载库时
strace(1)
进程,您可能会看到如下内容:

mmap(NULL, 1848896, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3</usr/lib/libc-2.28.so>, 0) = 0x7f9673d8f000
mprotect(0x7f9673db1000, 1671168, PROT_NONE) = 0
mmap(0x7f9673db1000, 1355776, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3</usr/lib/libc-2.28.so>, 0x22000) = 0x7f9673db1000
mmap(0x7f9673efc000, 311296, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3</usr/lib/libc-2.28.so>, 0x16d000) = 0x7f9673efc000
mmap(0x7f9673f49000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3</usr/lib/libc-2.28.so>, 0x1b9000) = 0x7f9673f49000
这看起来像
线程1
的线程堆栈。第二个映射是堆栈本身(
372d7000
-
36ad7000
=
800000
=8 MiB,这是许多发行版中的默认堆栈大小限制,而这又是
pthread
的默认堆栈大小),第一个映射是堆栈保护页。此页面使用模式
--p
保护堆栈不溢出,并在溢出发生时触发SEGFULT(因为写入此写保护页面)

注意:在较旧的Linux内核中,线程堆栈在
maps
文件中用
[stack:TID]
名称进行了注释,但此功能已被删除,因此我无法保证此映射确实是一个线程堆栈(尽管看起来像)。但是,您可以使用
strace
clone()
syscall的
child\u stack
参数中找到确切的线程堆栈位置,并与此映射进行比较

继续第三个问题是

7fcc30000000-7fcc30021000 rw-p 00000000 00:00 0 
7fcc30021000-7fcc34000000 ---p 00000000 00:00 0 
这就是
thread1
中的
malloc()
分配您请求的内存所做的。简而言之,整个区域
7fcc300000-7fcc3400000
是一个堆,从中进行分配。从该堆分配的
rw-p
间隔
7fcc3000000000-7fcc30021000
,将随着您使用
malloc()请求越来越多的内存而增长。当此堆耗尽时,将使用
mmap()
请求新堆

正如您可能注意到的,我没有对您的问题中的以下映射进行解释:

7fcc37abe000-7fcc37ac1000 rw-p 00000000 00:00 0 
7fcc37ad8000-7fcc37adc000 rw-p 00000000 00:00 0 

我不能很快认出那些家伙,也不能确定这些是普通的分配。可能这需要单独调查,因为此主题已经太长。

没有权限的映射可能是某种“保护”页面,和/或保留虚拟地址空间以供将来增长(以防止
mmap(NULL,…)
随机选择。可能是TLS?与这两个非常相似的小anon映射很少,因此我无法证明每一个特定映射的用途并说“这一个肯定是TLS”。最好分别使用调试器检查每一个以确保(
catch syscall mmap
然后查看谁是调用者)。关于第三个问题的答案,如果您在主线程中看到malloc,您将发现malloc没有生成保护区域。即,按“enter”直到它打印“在malloc之后,在主线程中释放之前”。这是为什么?这里的主线程使用标有
[heap]
的区域。您可以执行1000个
malloc()
s的100k块,您将看到该区域在增长。由于历史原因,该区域有点特殊,其大小不是通过
mmap()
syscall调整的,而是通过
brk()调整的
。您可能有兴趣阅读一些关于glibc的
malloc()
工作原理的资料,例如。
7fcc30000000-7fcc30021000 rw-p 00000000 00:00 0 
7fcc30021000-7fcc34000000 ---p 00000000 00:00 0 
7fcc37abe000-7fcc37ac1000 rw-p 00000000 00:00 0 
7fcc37ad8000-7fcc37adc000 rw-p 00000000 00:00 0