现在的位置: 首页 > 综合 > 正文

5.3 内存映射

2013年09月14日 ⁄ 综合 ⁄ 共 3413字 ⁄ 字号 评论关闭

5.3  内存映射
内存映射允许不同的进程通过一个共享的文件通信。内存映射可以被用在进程间通信或者一种方便的对文件的存取方式。
内存映射把一个文件和一个进程的内存空间联系在一起。Linux 把文件分成页大小的块,然后把它们复制到虚拟内存页中
,这样内容就可以在进程的地址空间可用。这样,进程就可以通过一般的内存读写的方式读写取文件的内容。这样就可以
快速的访问文件的内容。
    你可以认为内存映射如同分配一个足够的缓冲区,然后把文件的内容读到该缓冲区,当缓冲区的内容改变时再写回到文
件中。Linux为你处理了读和写的操作。
5.3.1 映射一个普通文件
使用 mmap() 把一个文件映射到进程空间。
SYNOPSIS
       #include <sys/mman.h>
       void *mmap(void *addr, size_t length, int prot, int flags,
                  int fd, off_t offset);
描述:
    mmap() 在进程的虚拟地址空间创建了一个新的映射。这个新映射的开始地址由 addr指定。 length 指定了映射的
    长度(以字节为单位)。 prot 描述了需要的内存保护(不能和文件的打开方式相冲突)它可以是 PROT_NONE 或者
    是如下各项的 按位相或:
    PROT_EXEC //页可以执行
    PROT_READ //页可读
    PROT_WRITE//页可写
    
    PROT_NONE //页不可访问[只可单独使用]
    flags 决定了对内存中映射的改变是否对其他进程可见,是否对相应的文件执行更新操作。它们只可以为下面的某一项。
    MAP_SHARED//共享映射。对内存映射的更新对其他进程可见,而且要反应到文件上。改变可能在调用 msync()或者
                    munmap()之前没有被真正的反映到文件上。
    MAP_PRIVATE//对内存映射的改变不会被写回到相关的文件。但会写到一个相应的私有的复制文件中。这对其他进程不可见。
    这一项也可以和上面的两项一起使用。
    MAP_FIXED//如果指定了这一标志,Linux将会使用你请求的地址来映射文件,而不是仅仅把你传入的地址作为一个参考暗示。
                    这时这个地址必须是按页对齐的。
    fd 是要执行映射的文件的描述符。
    offset 必须是页大小的整数倍。
返回值:成功时返回内存映射的开始地址。失败时返回 MAP_FAILED.

munmap()可以用来释放内存映射。
5.3.3 例子
如下的程序 A:
产生一个随机数,写入到相应的映射文件中。
程序B:
读出这个随机数,乘以2写回到映射文件中。

Listing 5.5 (mmap-write.c) Write a Random Number to a Memory-Mapped File
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#define FILE_LENGTH 0x100
/* Return a uniformly random number in the range [low,high].  */
int random_range (unsigned const low, unsigned const high)
{
  unsigned const range = high - low + 1;
  return low + (int) (((double) range) * rand () / (RAND_MAX + 1.0));
}
int main (int argc, char* const argv[])
{
  int fd;
  void* file_memory;
  /* Seed the random number generator.  */
  srand (time (NULL));
  /* Prepare a file large enough to hold an unsigned integer.
  fd = open (argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
  lseek (fd, FILE_LENGTH+1, SEEK_SET);
  write (fd, “”, 1);
  lseek (fd, 0, SEEK_SET);
  /* Create the memory mapping. */
  file_memory = mmap (0, FILE_LENGTH, PROT_WRITE, MAP_SHARED, fd, 0);
  close (fd);
  /* Write a random integer to memory-mapped area. */
  sprintf((char*) file_memory, “%d/n”, random_range (-100, 100));
  /* Release the memory (unnecessary because the program exits). */
  munmap (file_memory, FILE_LENGTH);
  return 0;
}
Listing 5.6 (mmap-read.c) Read an Integer from a Memory-Mapped File, and
Double It
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#define FILE_LENGTH 0x100
int main (int argc, char* const argv[])
{
  int fd;
  void* file_memory;
  int integer;
  /* Open the file. */
  fd = open (argv[1], O_RDWR, S_IRUSR | S_IWUSR);
  /* Create the memory mapping. */
  file_memory = mmap (0, FILE_LENGTH, PROT_READ | PROT_WRITE,
                      MAP_SHARED, fd, 0);
  close (fd);
  /* Read the integer, print it out, and double it. */
  sscanf (file_memory, “%d”, &integer);
  printf (“value: %d/n”, integer);
  sprintf ((char*) file_memory, “%d/n”, 2 * integer);
  /* Release the memory (unnecessary because the program exits). */
  munmap (file_memory, FILE_LENGTH);
  return 0;
}
你可以如下步骤运行,
% ./mmap-write /tmp/integer-file
% cat /tmp/integer-file
42
% ./mmap-read /tmp/integer-file
value: 42
% cat /tmp/integer-file
84

5.3.3 文件的共享访问
不同的进程可以使用内存映射使用同一个关联文件来进程通信。指定 MAP_SHARED 这样你的任何改变将会写入到相关的文件中。
如果你不知定这个标志,Linux可能会只把改变写在内存中而不是立即反映到关联文件中。
你可以强制Linux把改变写入磁盘文件。这要使用 msync().
SYNOPSIS
       #include <sys/mman.h>

       int msync(void *addr, size_t length, int flags);

DESCRIPTION
                addr //映射文件在内存中的起始地址。
                length //映射区域的大小
                flags //可以使用如下的标志:
                        MS_ASYNC//指定更新被安排,但是,调用立即返回,在返回前更新不一定被执行。
                        MS_SYNC //更新立即执行。在完成更新之前 msync调用阻塞。
                        MS_INVALIDATE //所有的其他文件映射无效,这样其他进程就可以看到改变后的数据。
                        

抱歉!评论已关闭.