我试图了解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;
}
启动程序时,/proc/<pid>/maps
中的内容为:
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
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]
这些存储区的用途是什么?
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
这两个地区的目的是什么?
答案 0 :(得分:2)
您的问题涵盖了许多完全不同的内容,因此答案将很长。
第一个问题是
的含义7fcc37491000-7fcc37691000 ---p 001ba000 08:01 1053757 /lib/x86_64-linux-gnu/libc-2.19.so
在
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
该不可访问的内存区域是库的相邻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
第一个mmap()
将第一个ELF段映射到内存中,但为整个库保留空间。这样做是为了允许内核自行选择库的位置。为了保护段mprotect(..., PROT_NONE)
之间的任何可能的间隙,请调用;然后使用mmap()
将所有剩余的段映射到内存中-这也将适当的内存页的保护模式从---p
更改为段所需的任何模式。看看how it actually works,您可能会觉得很有趣。如果您想验证在加载过程中---p
间隙是如何形成的,还可以对库的二进制文件使用readelf(1)
并使用段的位置和对齐方式进行一些十六进制数学运算,将结果与strace
。
第二个问题是以下匿名映射:
7fcc36ad6000-7fcc36ad7000 ---p 00000000 00:00 0
7fcc36ad7000-7fcc372d7000 rw-p 00000000 00:00 0
这看起来像thread 1
的线程堆栈。第二个映射是堆栈本身(372d7000
-36ad7000
= 800000
= 8 MiB,这是许多发行版中的默认堆栈大小限制,而后者又是默认堆栈大小pthread
),第一个是堆栈保护页面。模式为---p
的页面可防止堆栈溢出,并在发生溢出时(由于对此受写保护的页面进行写操作)而触发段错误。
注意:在较早的Linux内核中,线程栈在[stack:TID]
文件中用maps
名称进行注释,但是此功能已删除,因此我不能保证该映射实际上是一个线程堆栈(尽管看起来像)。但是,您可以使用strace
从系统调用child_stack
的{{1}}参数中找到确切的线程堆栈位置,并与该映射进行比较。
继续。 第三个问题是
clone()
好吧,这就是7fcc30000000-7fcc30021000 rw-p 00000000 00:00 0
7fcc30021000-7fcc34000000 ---p 00000000 00:00 0
中的malloc()
分配您所请求的内存的过程。简而言之,整个区域thread 1
是一个堆,从中进行分配。从{堆中分配的7fcc30000000-7fcc34000000
间隔rw-p
将随着您使用7fcc30000000-7fcc30021000
请求越来越多的内存而增大。当该堆耗尽时,将使用malloc()
请求新的堆。
您可能已经注意到,对于您的问题中的以下映射,我没有任何解释:
mmap()
我无法很快认出这些家伙,并且不确定这些是普通的分配。可能需要单独调查,因为该主题已经太长了。