Chinaunix首页 | 论坛 | 博客
  • 博客访问: 279091
  • 博文数量: 94
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 202
  • 用 户 组: 普通用户
  • 注册时间: 2014-08-08 20:07
文章分类

全部博文(94)

文章存档

2017年(19)

2016年(30)

2015年(12)

2014年(33)

我的朋友

分类:

2016-08-26 11:52:03

原文地址:mmap 系统调用 的使用。 作者:Bean_lee

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的时候,这其实是个建议查找地址,用来指导内存区定位的线索,是个用户指定的经验值。

    还记得中有 如下代码:
1) 如果用户打上了MAP_FIXED标志,表示用户王八吃秤砣,铁了心要addr这个地址作为映射起始地址,
     OK,直接把 addr 返回给这个二杆子用户。
2)如果没打上 MAP_FIXED标志,则首先在建议地址addr附近寻找合适的区域。
3)addr 为NULL,方法中有详细介绍。

      从可移植性考虑,一般不能指定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。
      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。


阅读(1763) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~