ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python中mmap模块处理大文本的操作方法

Python中mmap模块处理大文本的操作方法 假使存在一个具体的业务需求, 要求对规模达到20G的大容量文件执行处理操作, 此时应当采取何种应对策略与实施路径。我们有可能进行以下的实现方式。def get_datas():source_text_path 路径with open(source_text_path, rb) as f:data f.readlines()yield dataif __name__ __main__:for e in get_datas():deal_data(e) # 处理数据虽然这样做可以实现预期效果, 但是在进行处理的时候, 所消耗的资源以及带来的性能影响并不是非常友好, 因此我们需要进行优化操作, 具体的办法就是使用mmap模块。mmap它属于是把那个文件或者其它什么对象, 映射到进程的地址空间里面去的一个方法, 虚拟内存映射文件就是这样一种方式, 实现磁盘上面的文件的地址和进程虚拟地址空间的某段虚拟地址之间的一一对应关系。它省掉了在内核态和用户态之间进行页面拷贝的这个动作, 也就是所谓的两态间copy。它会直接将用户态的虚拟地址与内核态空间进行映射。让进程可以直接读取内核空间。速度提高了, 内存占用也少了。说得更直白一点, mmap函数是用来实现内存共享的, 所谓内存共享, 指的是两个不同的进程去共享同一块内存的情况, 具体来说, 就是让同一块物理内存被映射到每一个进程各自的地址空间里面, 需要注意的是, 这块物理内存的大小的数值已经被规定好了, 它一定要比实际写入的数据大, 并且它还有一个名称。当需要进行写入操作的时候, 要先找到那个内存的名称, 接着把数据写入到内存里面去。等到需要读取数据的时候, 必须先清楚你要读取的数据有多大, 这是因为物理内存的容量通常会比实际需要读取的内容要大一些, 如果全部读取的话可能会读到一些没有用的空数据。然后, 要去寻找对应的物理块, 最后再进行读取操作。mmap 介绍mmap.mmap(fileno, length, tagnameNone, accessACCESS_DEFAULT[, offset])参数说明它是一个非负整数的偏移量, 默认的起始值为0。Unixmmap.mmap(fileno, length, flagsMAP_SHARED, protPROT_WRITE|PROT_READ, accessACCESS_DEFAULT[, offset])参数说明文件描述符有如下支持的方法在处理EOF这一事物的时候, write()这个方法会直接抛出异常情况, 而括号里面写的那个东西还有read()这个方法, 它们是一点也不做任何处理的。使用mmap读取大文件from mmap import mmapdef read_data(file_path):with open(file_path, r) as f:m mmap(f.fileno(), 0)g_index 0for index, char in enumerate(m):if char b\n:yield m[g_index:index 1].decode()g_index index 1if __name__ __main__:file_path for content in read_data(file_path):print(content)什么时候用mmap使用mmap来读取超大文件, 并不是mmap的主要应用场景, 官方文档也没有提到这一点。如果只是单纯地读取超大文件, 使用文件对象的read(N), 来得更快、更好、更简单。我们现在来谈一谈关于标准库之中的 mmap 这个模块的事情, 当前的这个需求是让要对体积非常大的文件进行数据的读取和写入操作, 这个文件的总大小已经接近了 40G 这么一个大数字, 那些比如用 等工具的软件会直接产生拒绝对此文件进行打开的处理行为, 如果采用 r 的打开模式去接触文件的话是可以实现任意读写功能的, 不过必须得特别的小心才是, 具体()这个是否能够使用完全就是看每个文件每一行内部的字符串长度到底是多少, 假若每一行内没有发现任何换行的特征存在那么就不能用了, 即使已经把每行的大小给知道了, 也必须再附带一个参数 N 来对最大允许读取的数量进行控制。那个括号是肯定不能用的, 就算带着参数, 也有可能直接导致卡死调用read(N)是没有问题的, 主要需要控制的是N的大小。总而言之, 传统的读写文件的方法是可以使用的, 不过它并不是很方便。在速度方面, 这也是一个需要面对的问题。传统的缓存IO方式, 涉及到操作系统内核态的内存以及进程虚拟空间内存这些地方的内容交换。当处理超大文件的时候, 这种交换操作会浪费大量的CPU时间以及内存。关于这一点, mmap是另一种可供选择的方式。它省掉了内核态和用户态之间进行页拷贝的这一步动作也就是两态间的copy, 而是把用户态中的虚拟地址直接和内核态的空间做映射, 所以进程能够直接读取内核空间, 这让运行速度提高了, 同时内存占用也变少了。总而言之, 为了提升读写效率并且保护磁盘, 常规的文件操作引入了页缓存机制, 这会导致在读取文件的时候, 必须先要把文件页从磁盘拷贝到页缓存中, 因为页缓存处于内核空间里面, 用户进程无法直接进行寻址, 所以还需要把页缓存中的数据页再次拷贝到内存对应的用户空间中, 就这么通过了两轮数据拷贝的过程, 才最终完成了进程对于文件内容的获取任务。在进行写入操作的时候, 情况也是一样的。需要被写入的数据起初是在一个内核空间中, 因为那个地方不能被直接访问, 所以必须首先要把数据拷贝到该内核空间所对应的主存里, 然后再把数据写回到磁盘里头去, 这种机制被称为延迟写回。这个过程中, 同样也是需要执行两次数据的拷贝工作。而当大家在使用mmap这种手段去操作文件的时候, 在创建新的虚拟内存区域这一步, 和建立文件磁盘地址以及那个新创建的虚拟内存区域之间的映射关系那一步里面, 是一丁点文件拷贝操作的影子都找不到的, 是完全没有的。然后, 在后续再去访问那些数据的时候, 如果发现内存里头压根就没有这些数据存在, 于是就去发起了缺页异常这一套过程, 这整个过程能够通过之前已经建立好的那种映射关系来帮忙, 只需要使用一次数据拷贝的操作就足够了, 就可以直接从那个磁盘当中把数据给传输到内存里面的用户空间里边去, 以便供后面的进程来进行使用。综合所有的情况来看, 平常的文件读写操作, 它得先在磁盘那里把数据拿出来, 接着拷贝到页缓存里, 最后再拷贝到用户主存中, 这么来回折腾, 一共要进行两次数据拷贝的过程。但是呢, 如果用mmap这个办法去操控文件的话, 那就简单多了, 只需要从磁盘直接把数据拷贝到用户主存, 这样只用进行一次数据拷贝就搞定了。说穿了, mmap最核心的关键点在于, 它让用户空间和内核空间之间的数据可以直接进行交互, 这样就省去了因为不同空间之间数据不互通而产生的那些特别麻烦的繁琐过程。所以, 在某些特定的场景里面, 使用mmap的运作效率会更高一些。从官网上看关于mmap的介绍, 生成的mmap对象表现就像一个对象, 可以直接用index的方式读写, 可以切片, 同时, mmap对象还有一组类似文件操作的接口, 如read, flush等, 也就是说, 即mmap对象兼具和file对象的功能。不过呢, 这一点是必须要注意到的, 就是当我们面对那种特别大的文件进行读取操作的时候, 眼下可以先不去考虑写入方面的问题, 但是数据从磁盘进入到内核这一过程当中, 是会不可避免地占用内存空间的, 所以说绝对不可以采取一口气把整个文件全部加载到内存中的这种做法, 使用类似于read(N)这样的方式来分批读取是必定需要的, 而说mmap这种方式可能提高效率的情况, 其实需要留意的是, 如果老是去频繁地建立和关闭mmap这种映射关系, 并且每一次建立都是为了让它指向这个大文件里面不同的位置, 那么这样做反而会使得效率变得更低。在通常的情形之下, 所谓的read这样的系统调用其实现过程里并不需要去使用mmap这种技术。mmap的另一个应用场景, 是进程间的内存共享。多个进程将同一个文件映射到同一段内核地址上, 从而实现相互之间的共同访问。总结使用mmap的时机到这儿为止, 这篇关于中mmap模块处理大文本的文章就介绍到这里了。大家如果想了解更多相关于中mmap模块的内容, 可以去搜索脚本之家以前的文章, 或者继续浏览下面的相关文章。希望大家以后多多支持脚本之家。
返回列表