我尝试创建MAP_GROWSDOWN映射,期望它会自动增长。如手册页中所指定:
MAP_GROWSDOWN
MAP_GROWSDOWN 该标志用于堆栈。它向内核虚拟内存系统指示该映射应在内存中向下扩展。返回地址比在进程的虚拟地址空间中实际创建的内存区域低一页。 触摸映射下方的“防护”页面中的地址,将导致 映射增加一页 。可以重复这种增长,直到该映射增长到下一个较低映射的高端的页面内为止,此时触摸“防护”页面将产生 SIGSEGV信号。
该标志用于堆栈。它向内核虚拟内存系统指示该映射应在内存中向下扩展。返回地址比在进程的虚拟地址空间中实际创建的内存区域低一页。 触摸映射下方的“防护”页面中的地址,将导致 映射增加一页 。可以重复这种增长,直到该映射增长到下一个较低映射的高端的页面内为止,此时触摸“防护”页面将产生 SIGSEGV信号。
SIGSEGV
因此,我编写了以下示例来测试映射的增长:
#ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include <stdlib.h> #include <string.h> #include <inttypes.h> #include <errno.h> #include <sys/mman.h> #include <stdio.h> int main(void){ char *mapped_ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_STACK | MAP_GROWSDOWN, -1, 0); if(mapped_ptr == MAP_FAILED){ int error_code = errno; fprintf(stderr, "Cannot do MAP_FIXED mapping." "Error code = %d, details = %s\n", error_code, strerror(error_code)); exit(EXIT_FAILURE); } volatile char *c_ptr_1 = mapped_ptr; //address returned by mmap *c_ptr_1 = 'a'; //fine volatile char *c_ptr_2 = mapped_ptr - 4095; //1 page below the guard *c_ptr_2 = 'b'; //crashes with SEGV }
所以我得到的SEGV不是增加映射。在这里生长意味着什么?
SEGV
更换:
volatile char *c_ptr_1 = mapped_ptr - 4096; //1 page below
用
volatile char *c_ptr_1 = mapped_ptr;
因为:
返回地址比在进程的虚拟地址空间中实际创建的内存区域低一页。 触摸映射下方的“防护”页面中的地址将导致映射按页面增长。
请注意,我测试了该解决方案,并且可以在内核4.15.0-45-generic上按预期工作。