mmap() 函数的一点点介绍
看虚拟内存的时候有很多不明白,所以干脆停下来看一看mmap()的一些实例。于是分析了一下,拿出来分享。下面是一段简单的映射一段内存放文件的程序示例mmap()的用法
程序代码:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/mman.h> int main(int argc, char *argv[]) { struct stat sb; off_t len; char *p; int fd; if( argc < 2 ) { fprintf( stderr, "usage: %s <file>\n", argv[0]); return 1; } fd = open ( argv[1], O_RDONLY ); if ( fd == -1) { perror( "open" ); return 1; } if( fstat ( fd, &sb ) == -1 ) { perror ( "fstat" ); return 1; } if ( !S_ISREG( sb.st_mode ) ) { fprintf( stderr, "%s is not a file\n",argv[1]); return 1; } p = mmap ( 0, sb.st_size, PROT_READ, MAP_SHARED, fd, 0 ); if( p == MAP_FAILED ) { perror( "mmap" ); return 1; } if ( close(fd) == -1) { perror( "close" ); return 1; } for ( len = 0; len < sb.st_size; len++) putchar (p[len]); if( munmap( p, sb.st_size) == -1 ) { perror( "munmap" ); return 1; } return 0; }
给出一个mmap的原型吧
void *
mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
程序中addr若为0, 则系统帮你从哪里开始映射,一般都会选择0吧
len 则是从fstat中获得
prot则是保护标志,程序里被标志为PROT_READ(页可以被读)
flags 则指定了被映射的对象的类型。MAP_SHARED把它标记标记为共享,
也就是说在一个进程中写入那个区域后会影响其它进程(同样被映射到那个区域的进程)
S_ISREG是一个宏用来检测信息,这样我们就可以知道给出的文件是不是regular file
通过mmap() 函数来操纵文件有而不是使用read()或者是write()有一些好处。
读写内存映射文件(memory-mapped file) 可以避免在使用read()
或者是write()时碰到的一些不必要的复制。
用read() 和 write() 时, 要先把数据从内核缓冲区复制到应用程序的缓冲区或者相反。
为什么GNU grep 很快,就是因为grep曾经默认使用mmap()而很努力的避开输入中使用read()
从而避免无谓的复制。
比如数据必须先被复制到user-space buffer中或是必须先从user-space buffe中复制出来
读写内存映射文件(memory-mapped file)并且不会有任何不必要的系统调用(system call) 或者是
进程切换(context switch). 和访问内存一样简单
但是使用mmap()是需要注意的则是内存的浪费问题
比如对于一个4kb的页, 如果只是映射7byte,,那就是有4089 byte 被浪费了,
因为出映射出来的内存都是整数大小.
提示:
可以映射至设备文件/dev/zero来实现匿名内存映射。
fd = open ( "/dev/zero", O_RDONLY );
ptr = mmap( 0, desired_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0 );
close(fd);
与下面一行代码是等效的
ptr = mmap ( 0, desired_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 );
注: /dev/zero 是一个永远输出0 的特殊文件
如果指定了MAP_ANONYMOUS(匿名映射,则不需要和任何文件关联)此时fd要指定为-1
[ 本帖最后由 madfrogme 于 2012-8-21 19:20 编辑 ]