mmap()函数疑问?
mmap()如果指定了要映射的虚拟区间,并且这段区间还映射着物理内存,可以用do_munmap() “Clear Old maps”,为什么?比如用户进程通过malloc()得到200~300这块虚拟空间,并且与物理内存建立了映射关系,这时又调用mmap()希望将某个文件映射到100~1000。
① 如果这个情况下mmap()返回错误,那么mmap()就没有实际意义了,因为每次调用它,都得用户程序员自己确定一块还没有使用的虚拟空间;
② 按照mmap()函数的真实逻辑,它会先撤消200~300这段区间已有的映射,确保100~1000是“干净”的,再使用。
进程A:malloc()得到200~300空间
此时切换到进程B:mmap()将某个文件映射到100~1000
此时回到进程A,往200~300写,不是会干扰到进程B吗,而且如果这个时候free(),又会怎样? 你不是连验证程序都没有写过。回答你的问题。
1. mmap是可以不指定虚拟地址,由内核分配的。
2. 进程A和进程B都有独立的3G用户态虚拟地址空间。互不干预。这是LInux的常识呀。 回复 2# Tinnal
嗯,例子举的不对,我是想问在调用mmap()指定映射100~1000时,没办法确定这段虚拟地址是不是已经在用了,如果已经被之前调用malloc()的时候使用了,mmap()好像是把之前与物理页面的映射断开,而建立与文件的映射,不明白这样做为什么可以这样做。 Tinnal 发表于 2016-03-13 10:21 static/image/common/back.gif
你不是连验证程序都没有写过。回答你的问题。
1. mmap是可以不指定虚拟地址,由内核分配的。
2. 进程A和进 ...
我提问的时候应该是想写线程。 回复 4# _nosay
占对同一个进程的情况。如果你没有在mmap时没有指定MAP_FIXED, 在某一个虚拟地址已经被映射了的情况下,就算你再指定相同的地址, Linux也会重新给你分配一个不重叠的虚拟地址给你,和你直接传0进去的情况一下。
如果你采用MAP_FIXED告诉Linux你一定要映射这个地址,那Linux也没有办法,就只能解掉原来的映射罗。从mmap的man手册里就可以看到。
MAP_FIXED
Don't interpret addr as a hint: place the mapping at exactly that address. addr must be a multiple of the page size. If the memory region specified by addr and len overlaps pages of any existing mapping(s), then the overlapped part of the existing mapping(s) will be discarded. If the specified address cannot be used, mmap() will fail. Because requiring a fixed address for a mapping is less portable, the use of this option is discouraged.
回复 5# Tinnal
那指定映射地址的应用场合有哪些,用户程序不太可能在调用mmap()的时候知道哪些虚拟地址是闲着的,和最开始的问题那样,如果mmap100~1000的时候,将malloc的200~300解除了,但这时有些指令还有可能把它当成malloc200~300使用(包括调用free()),不会出问题吗? 具体的例子一下子想不起来,貌似有些exploit代码里喜欢用MAP_FIXED(比如老版本内核里,把NULL地址映射到邪恶的用户态程序段) 回复 7# nswcfd
nswcfd{:qq13:} 你是搞邪恶的,还是反邪恶的?
我写的一个测试程序,先malloc()得到(0x9a04008, 0x9a06008),再调用mmap()映射(0x9a05000, 0x9a07000)(起始地址按页面对齐了一下,两个区间是重叠的),最后free()时程序挂了:
$ ./a.out
malloc: (0x9a04008, 0x9a06008)
mmap: (0x9a05000, 0x9a07000)
*** glibc detected *** ./a.out: double free or corruption (out): 0x09a04008 ***
a.out: malloc.c:2451: sYSMALLOc: Assertion `(old_top == (((mbinptr) (((char *) &((av)->bins[((1) - 1) * 2])) - __builtin_offsetof (struct malloc_chunk, fd)))) && old_size == 0) || ((unsigned long) (old_size) >= (unsigned long)((((__builtin_offsetof (struct malloc_chunk, fd_nextsize))+((2 * (sizeof(size_t))) - 1)) & ~((2 * (sizeof(size_t))) - 1))) && ((old_top)->size & 0x1) && ((unsigned long)old_end & pagemask) == 0)' failed.
Aborted (core dumped)#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#define PAGE_SHIFT 12
#define TWO_PAGE 2*4*1024
#define PAGE_SIZE (1UL << PAGE_SHIFT)
#define PAGE_MASK (~(PAGE_SIZE-1))
#define PAGE_ALIGN(addr)(((addr)+PAGE_SIZE-1)&PAGE_MASK)
int main()
{
char *p1 = NULL;
char *p2 = NULL;
p1 = malloc(TWO_PAGE);
if (p1 == NULL) {
printf("malloc failed\n");
goto err;
}
printf("malloc: (%p, %p)\n", p1, p1+TWO_PAGE);
// 地始地址不按页面对齐,mmap()失败
p2 = mmap((void *)PAGE_ALIGN((unsigned long)p1), TWO_PAGE, PROT_WRITE, MAP_ANONYMOUS | MAP_FIXED | MAP_SHARED, -1, 0);
if (p2 == (void *)(-1)) {
printf("mmap failed: %p\n", (void *)PAGE_ALIGN((unsigned long)p1));
goto err;
}
printf("mmap: (%p, %p)\n", p2, p2+TWO_PAGE);
free(p1);
munmap(p2, TWO_PAGE);
return 0;
err:
if (p1 != NULL)
free(p1);
if (p2 != NULL)
munmap(p2, TWO_PAGE);
return -1;
}
回复 6# _nosay
不知道你为什么一直举例malloc和你mmap冲突。 malloc使用的heap和mmaps区域,在Linux里头是有规定的。
见:http://duartes.org/gustavo/blog/post/anatomy-of-a-program-in-memory/
内存就那么3G, 你非得用mmap不按规矩的使用,当然出问题了。 就像你不按FHS存放文件一样。没错,你可以肆意的用已有的接口去实现你另类的想法。但也得承担不按规范的结果。
在没有全局的内存规划前提下,最好的方式是addr传0, 让内核给你选。
还有:https://lwn.net/Articles/91829/
如果你想在Linux的地址空间里驰骋,请选择64位linux,把目前能用的内存mmap几次都用不完。