18.9. "mmap" --- 内存映射文件支持
*********************************

======================================================================

内存映射（mmap）文件对象的行为既像 "bytearray" 又像 *文件对象*。 你可
以在大部分接受 "bytearray" 的地方使用 mmap 对象；例如，你可以使用 "re"
模块来搜索一个内存映射文件。 你也可以通过执行 "obj[index] = 97" 来修改
单个字节，或者通过对切片赋值来修改一个子序列: "obj[i1:i2] = b'...'"。
你还可以在文件的当前位置开始读取和写入数据，并使用 "seek()" 前往另一个
位置。

内存映射文件是由 "mmap" 构造函数创建的，其在 Unix和 Windows上是不同的
。 无论哪种情况，你都必须为一个打开的文件提供文件描述符以进行更新。 如
果你希望映射一个已有的 Python 文件对象，请使用该对象的 "fileno()" 方法
来获取 *fileno* 参数的正确值。 否则，你可以使用 "os.open()" 函数来打开
这个文件，这会直接返回一个文件描述符（结束时仍然需要关闭该文件）。

注解:

  如果要为可写的缓冲文件创建内存映射，则应当首先 "flush()" 该文件。 这
  确保了对缓冲区的本地修改在内存映射中可用。

For both the Unix and Windows versions of the constructor, *access*
may be specified as an optional keyword parameter. *access* accepts
one of three values: "ACCESS_READ", "ACCESS_WRITE", or "ACCESS_COPY"
to specify read-only, write-through or copy-on-write memory
respectively. *access* can be used on both Unix and Windows.  If
*access* is not specified, Windows mmap returns a write-through
mapping.  The initial memory values for all three access types are
taken from the specified file.  Assignment to an "ACCESS_READ" memory
map raises a "TypeError" exception. Assignment to an "ACCESS_WRITE"
memory map affects both memory and the underlying file.  Assignment to
an "ACCESS_COPY" memory map affects memory but does not update the
underlying file.

要映射匿名内存，应将 -1 作为 fileno 和 length 一起传递。

class mmap.mmap(fileno, length, tagname=None, access=ACCESS_DEFAULT[, offset])

   **（ Windows 版本）** 映射被文件句柄 *fileno* 指定的文件的 *length*
   个字节，并创建一个 mmap 对象。如果 *length* 大于当前文件大小，则文
   件将扩展为包含 *length* 个字节。如果 *length* 为 "0"，则映射的最大
   长度为当前文件大小。如果文件为空， Windows 会引发异常（你无法在
   Windows上创建空映射）。

   如果 *tagname* 被指定且不是 "None" ，则是为映射提供标签名称的字符串
   。 Windows 允许你对同一文件拥有许多不同的映射。如果指定现有标签的名
   称，则会打开该标签，否则将创建该名称的新标签。如果省略此参数或设置
   为 "None" ，则创建的映射不带名称。避免使用 tag 参数将有助于使代码在
   Unix和Windows之间可移植。

   *offset* 可以被指定为非负整数偏移量。 mmap 引用将相对于从文件开头的
   偏移。 *offset* 默认为0。 *offeset* 必须是 "ALLOCATIONGRANULARITY"
   的倍数。

class mmap.mmap(fileno, length, flags=MAP_SHARED, prot=PROT_WRITE|PROT_READ, access=ACCESS_DEFAULT[, offset])

   **(Unix 版本)** 映射文件描述符 *fileno* 指定的文件的 *length* 个字
   节，并返回一个 mmap 对象。如果 *length* 为 "0" ，则当调用 "mmap" 时
   ，映射的最大长度将为文件的当前大小。

   *flags* 指明映射的性质。 "MAP_PRIVATE" 会创建私有的写入时拷贝映射，
   因此对 mmap 对象内容的修改将为该进程所私有，而 "MAP_SHARED" 会创建
   与其他映射同一文件区域的进程所共享的映射。 默认值为 "MAP_SHARED"。

   如果指明了 *prot*，它将给出所需的内存保护方式；最有用的两个值是
   "PROT_READ" 和 "PROT_WRITE"，分别指明页面为可读或可写。 *prot* 默认
   为 "PROT_READ | PROT_WRITE"。

   可以指定 *access* 作为替代 *flags* 和 *prot* 的可选关键字形参。 同
   时指定 *flags*, *prot* 和 *access* 将导致错误。 请参阅上文中
   *access* 的描述了解有关如何使用此形参的信息。

   *offset* 可以被指定为非负整数偏移量。 mmap 引用将相对于从文件开头的
   偏移。 *offset* 默认为 0。 *offset* 必须是 "ALLOCATIONGRANULARITY"
   的倍数，它在 Unix 系统上等价于 "PAGESIZE"。

   要确保已创建内存映射的有效性，描述符 *fileno* 所指定的文件在 Mac OS
   X 和 OpenVMS 上会与物理后备存储进行内部自动同步。

   这个例子演示了使用 "mmap" 的简单方式:

      import mmap

      # write a simple example file
      with open("hello.txt", "wb") as f:
          f.write(b"Hello Python!\n")

      with open("hello.txt", "r+b") as f:
          # memory-map the file, size 0 means whole file
          mm = mmap.mmap(f.fileno(), 0)
          # read content via standard file methods
          print(mm.readline())  # prints b"Hello Python!\n"
          # read content via slice notation
          print(mm[:5])  # prints b"Hello"
          # update content using slice notation;
          # note that new content must have same size
          mm[6:] = b" world!\n"
          # ... and read again using standard file methods
          mm.seek(0)
          print(mm.readline())  # prints b"Hello  world!\n"
          # close the map
          mm.close()

   "mmap" 也可以在 "with" 语句中被用作上下文管理器:

      import mmap

      with mmap.mmap(-1, 13) as mm:
          mm.write(b"Hello world!")

   3.2 新版功能: 上下文管理器支持。

   下面的例子演示了如何创建一个匿名映射并在父进程和子进程之间交换数据
   。:

      import mmap
      import os

      mm = mmap.mmap(-1, 13)
      mm.write(b"Hello world!")

      pid = os.fork()

      if pid == 0:  # In a child process
          mm.seek(0)
          print(mm.readline())

          mm.close()

   映射内存的文件对象支持以下方法:

   close()

      关闭 mmap。 后续调用该对象的其他方法将导致引发 ValueError 异常。
      此方法将不会关闭打开的文件。

   closed

      如果文件已关闭则返回 "True"。

      3.2 新版功能.

   find(sub[, start[, end]])

      返回子序列 *sub* 在对象内被找到的最小索引号，使得 *sub* 被包含在
      [*start*, *end*] 范围中。 可选参数 *start* 和 *end* 会被解读为切
      片表示法。 如果未找到则返回 "-1"。

      在 3.5 版更改: 现在支持可写的 *字节类对象*。

   flush([offset[, size]])

      将对文件的内存副本的修改刷新至磁盘。 如果不使用此调用则无法保证
      在对象被销毁前将修改写回存储。 如果指定了 *offset* 和 *size*，则
      只将对指定范围内字节的修改刷新至磁盘；在其他情况下，映射的全部范
      围都会被刷新。 *offset* 必须为 "PAGESIZE" 或
      "ALLOCATIONGRANULARITY" 的倍数。

      **(Windows version)** A nonzero value returned indicates
      success; zero indicates failure.

      **(Unix 版本)** 返回零值以表示成功。 当调用失败时将引发异常。

   move(dest, src, count)

      将从偏移量 *src* 开始的 *count* 个字节拷贝到目标索引号 *dest*。
      如果 mmap 创建时设置了 "ACCESS_READ"，则调用 move 将引发
      "TypeError" 异常。

   read([n])

      返回一个 "bytes"，其中包含从当前文件位置开始的至多 *n* 个字节。
      如果参数省略，为 "None" 或负数，则返回从当前文件位置开始直至映射
      结尾的所有字节。 文件位置会被更新为返回字节数据之后的位置。

      在 3.3 版更改: 参数可被省略或为 "None"。

   read_byte()

      将当前文件位置上的一个字节以整数形式返回，并让文件位置前进 1。

   readline()

      Returns a single line, starting at the current file position and
      up to the next newline.

   resize(newsize)

      改变映射以及下层文件的大小，如果存在的话。 如果 mmap 创建时设置
      了 "ACCESS_READ" 或 "ACCESS_COPY"，则改变映射大小将引发
      "TypeError" 异常。

   rfind(sub[, start[, end]])

      返回子序列 *sub* 在对象内被找到的最大索引号，使得 *sub* 被包含在
      [*start*, *end*] 范围中。 可选参数 *start* 和 *end* 会被解读为切
      片表示法。 如果未找到则返回 "-1"。

      在 3.5 版更改: 现在支持可写的 *字节类对象*。

   seek(pos[, whence])

      设置文件的当前位置。 *whence* 参数为可选项并且默认为
      "os.SEEK_SET" 或 "0" (绝对文件定位)；其他值还有 "os.SEEK_CUR" 或
      "1" (相对当前位置查找) 和 "os.SEEK_END" 或 "2" (相对文件末尾查找
      )。

   size()

      返回文件的长度，该数值可以大于内存映射区域的大小。

   tell()

      返回文件指针的当前位置。

   write(bytes)

      将 *bytes* 中的字节数据写入文件指针当前位置的内存并返回写入的字
      节总数 (一定不小于 "len(bytes)"，因为如果写入失败，将会引发
      "ValueError")。 在字节数据被写入后文件位置将会更新。 如果 mmap
      创建时设置了 "ACCESS_READ"，则向其写入将引发 "TypeError" 异常。

      在 3.5 版更改: 现在支持可写的 *字节类对象*。

      在 3.6 版更改: 现在会返回写入的字节总数。

   write_byte(byte)

      将整数值 *byte* 写入文件指针当前位置的内存；文件位置前进 "1"。
      如果 mmap 创建时设置了 "ACCESS_READ"，则向其写入将引发
      "TypeError" 异常。
