mmap()系统调用在调用进程的虚拟地址空间中创建一个新内存映射。映射分为两种。
-文件映射:文件映射将一个文件的一部分直接映射到调用进程的虚拟内存中。一旦一个文件被映射之后就可以通过在相应的内存区域中操作字节来访问文件内容了。映射的分页会在需要的时候从文件中(自动)加载。
-匿名映射:一个匿名映射没有对应的文件。相反,这种映射的分页会被初始化为0。(一个内容总是被初始化为0的虚拟文件的映射)
一个进程的映射中的内存可以与其他进程中的映射共享(即各个进程的页表条目指向RAM中相同分页)(fork())。
当多个进程共享相同内存分页时,每个进程可能会看到其他进程对分页内容做出的改变,这取决于映射是私有还是共享的。
-私有映射(MAP_PRIVATE):对映射的改变其他进程不可见,变更将不会在底层文件上进行。内核使用写时复制技术,当进程试图修改内容时,创建一个新的分页。
-共享映射(MAP_SHARED):变更对所有共享进程可见,发生在底层文件上。
#include
void * mmap(void *addr,size_t length,int prot,int flags,int fd,off_t offset);
addr参数制定了映射被放置的虚拟地址。如果将addr设为NULL,那么内核将会为映射选择一个合适的地址。这是创建映射的首选方法。
成功时会返回新映射的起始地址。发生错误时返回MAP_FAILED。
lenth参数指定了映射的字节数。lenth会被内核提升为分页大小的下一个倍数。
prot参数是一个位掩码,指定了施加于应设置上的保护信息,其取值是PROT_NONE(区域无法访问)或者为下列三者组合(OR)
值 | 描述 |
---|---|
PROT_READ | 区域内容可读 |
PROT_WRITE | 区域内容可写 |
PROT_EXEC | 区域内容可执行 |
flags参数为MAP_PRIVATE或MAP_SHARED
fd,offset是用于文件映射的(匿名映射忽略)。fd是一个标识被映射的文件的文件描述符。offset指定了映射在文件中的起点,必须是系统分页大小的倍数。
int main(int argc,char*argv[]){
struct stat st;
int fd;
char *addr;
fd=open(argv[1],O_RDWR);
fstat(fd,&st);//获取文件信息
addr=mmap(NULL,st.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
write(STDOUT_FILENO,addr,st.st_size);//将内容输出到终端
return 0;
}
如果一个进程在访问一个内存区域违反了该区域的保护位(PROT),那么内核将会向该进程发送一个SIGSEGV信号。
内存保护信息在进程私有的虚拟内存表中。因此,不同进程可能会有不同的保护位来映射同一个内存区域。
#include
int munmap(void *addr,size_t length);
addr参数是待解除映射的地址范围的起始地址,必须与一个分页边界对齐。
length参数是一个非负整数,它指定了待解除映射区域的大小(字节数)。
当一个进程最终或执行了一个exec()之后进程所有的映射会自动解除。
为确保一个共享文件映射的内容会被写入底层文件中,在使用munmap()解除一个映射之前需要调用msync().
复制文件
open中flag要和mmap中一致 不然会mmap失败
#include
#include
#include
#include
#include
int main(int argc,char *argv[])
{
struct stat st;
void *addr1;
void *addr2;
int fd1=open(argv[1],O_RDONLY,0777);
int fd2=open(argv[2],O_CREAT|O_RDWR,0777);
fstat(fd1,&st);
fflush(stdout);
ftruncate(fd2,st.st_size);
addr1=mmap(NULL,st.st_size,PROT_READ,MAP_SHARED,fd1,0);
addr2=mmap(NULL,st.st_size,PROT_WRITE|PROT_READ,MAP_SHARED,fd2,0);
if(addr1==MAP_FAILED&&addr2==MAP_FAILED) return 0;
memcpy(addr2,addr1,st.st_size);
close(fd1);
close(fd2);
return 0;
}