mmap 系统调用 的使用

 

转自:http://blog.chinaunix.net/uid-24774106-id-365941.html?utm_source=jiancool

mmap这个领域有很多优秀的书籍 博文介绍,本文做的事情只是将这些东西串起来,以更好懂的方式讲一下。

本文绝非原创,从很多博文及书籍中copy的东西。写成文章,也为了防止自己遗忘。
 
OK  我们开始
1 mmap的应用
mmap的本质是,把一个 文件或者posix 共享内存区队形映射到调用进程的地址空间。
三个目的
a)使用普通文件提供内存映射IO
b)使用特殊文件提供匿名映射IO
c)使用shm_open 以提供无亲缘关系的进程间的posix共享内存区
 
    mmap系统调用并不是完全为了用于共享内存而设计的。它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作。而Posix或系统V的共享内存IPC则纯粹用于共享目的,当然mmap()实现共享内存也是其主要应用之一。
    mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。
 
    #include
    void *mmap(void *addr ,size_t len,int prot,int flags,int fd,off_t offset)
                           如果成功返回被映射区域的起始地址。
 
    下面挨个解释每个参数的含义
    第一个参数 是个地址,指向用户地址空间的一个地址,当然我们的函数就是为了寻找一个可用的地址空间,将文件描述符fd 对应的文件从偏移量 offset 开始,copy文件的len个字符进入用户地址空间。将起始地址返回。为什么第一个参数就是用户地址空间的指针呢?这不是骑驴找驴吗?
    这个addr参数可以 NULL,这种情况我们比较容易理解。当 addr不为NULL的时候,这其实是个建议查找地址,用来指导内存区定位的线索,是个用户指定的经验值。
 
    还记得 进程虚拟地址空间之arch_get_unmapped_area中有 如下代码:
1) 如果用户打上了MAP_FIXED标志,表示用户王八吃秤砣,铁了心要addr这个地址作为映射起始地址,
     OK,直接把 addr 返回给这个二杆子用户。
2)如果没打上 MAP_FIXED标志,则首先在建议地址addr附近寻找合适的区域。
3)addr 为NULL,方法 进程虚拟地址空间之arch_get_unmapped_area中有详细介绍。
 
      从可移植性考虑,一般不能指定MAP_FIXED这个标记。
 
      if (flags & MAP_FIXED)
return addr;
 
if (addr) {
addr = PAGE_ALIGN(addr);
vma = find_vma(mm, addr);
if (TASK_SIZE - len >= addr &&
    (!vma || addr + len <= vma->vm_start))
return addr;
}
 
    第二个参数len,没啥好说的,干脆和 fd、offset 一起介绍了。 这个含义是 将fd对应的文件,从offset位置开始,长为len的内容映射到内存地址空间
    需要解释的是 fd,存在一种情况叫匿名映射,所谓匿名映射,表示不存在fd这么个真实的文件。
    1 BSD 提供匿名映射的办法是 fd =-1,同时 flag 指定为 MAP_SHARE|MAP_ANON(hy:MAP_PRIVATE)。
      ptr = mmap(NULL,sizeof(int),PROT_READ|PROT_WRITE,
                  MAP_SHARED|MAP_ANON, -1,0);
    2 SVR4 提供匿名映射的办法是 open  /dev/zero设备文件,把返回的文件描述符,作为mmap的fd参数。
      fd = open("/dev/zero",O_RDWR);
 
    跑一下题, /dev/zero 是一个 特殊的文件,当你读它的时候,它会提供无限的空字符(NULL,  ASCII NUL, 0x00)
 一个作用是用它作为源,产生一个特定大小的空白文件。下面的命令产生出一个bean 文件在当前目录下。
大小为1M。
    dd if=/dev/zero of=bean count=1024 bs=1024
 
OK,言归正传, 第三个参数 prot ,这个是负责保护作用的
 
PROT_READ 数据可读
PROT_WRITE 数据可写
PROT_EXEC 数据可执行
PROT_NONE 数据不可访问。
 
第四个参数 flags
MAP_SHARED 变动是共享的,换言之,如果映射的内存区域的数据变化了,磁盘上的文件也得跟着变
 
MAP_PRIVATE 变动是私有的,换言之,映射的内存区域 爱怎么折腾怎么折腾,fd对应的文件不变化
 
MAP_FIXED 这个就是前面提到的二杆子参数,非addr这个地址不可。。
 
 
OK 映射完成后,fd对应的文件可以close ,没啥影响。
 
 

 

 
  OK,学习完了mmap,来个直观的感受:
#include  
#include
#include
#include
#include
#define FILE_LENGTH 0x400
 
int main(int argc, char *argv[])
{
   int fd;
   void *map_memory;
 
   /* Open a file to be mapped. */
   fd = open("/tmp/shared_file", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
   lseek(fd, FILE_LENGTH-1, SEEK_SET);
   write(fd, "", 1);
   lseek(fd, 0, SEEK_SET);
 
   /* Create map memory. */
   map_memory = mmap(0, FILE_LENGTH, PROT_WRITE, MAP_SHARED, fd, 0);
   close(fd);
 
   /* Write to mapped memory. */
   if (strlen(argv[1]) < FILE_LENGTH)
      sprintf((char *)map_memory, "%s", argv[1]);
 
   sleep(100);
 
   exit(0);
}
 
# ./mmap_write hello&
[1] 14667
# cat /proc/14667/maps
08048000-08049000 r-xp 00000000 16:42 213757     /tmp/mmap_write
08049000-0804a000 rw-p 00000000 16:42 213757     /tmp/mmap_write
40000000-40015000 r-xp 00000000 16:42 12828674   /lib/ld-2.3.2.so
40015000-40016000 rw-p 00014000 16:42 12828674   /lib/ld-2.3.2.so
40016000-40017000 rw-p 00000000 00:00 0
40017000-40018000 -w-s 00000000 16:42 213753     /tmp/shared_file
42000000-4212e000 r-xp 00000000 16:42 13991938   /lib/tls/libc-2.3.2.so
4212e000-42131000 rw-p 0012e000 16:42 13991938   /lib/tls/libc-2.3.2.so
42131000-42133000 rw-p 00000000 00:00 0
bfffe000-c0000000 rwxp fffff000 00:00 0
 
我们看到 /tmp/shared_file 文件已经映射到内存中取了,占用空间0x40018000 - 0x40017000 = 4K。FILE_LENGTH  大小是0x400 = 1K,但是 映射的分配是以页面为单位分配的,即最小分配4K
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值