无论是select、poll还是epoll都需要内核把fd消息通知给用户空间,如何避免不必要的内存复制就显得非常重要。epoll是通过内核和用户空间mmap同一块内存来实现的。在netty中可以选择使用epoll来读写消息。
mmap是将一个文件映射到内存中。
常规文件操作为了提高读写效率和保护磁盘,使用了页缓存机制。这要造成读文件时要将文件页从磁盘拷贝到页缓存中,由于页缓存处在内核空间,不能被用户进程直接寻址,所以还需要将页缓存中数据再次拷贝到内存对应的用户空间中。这样,通过了两次数据拷贝的过程,才能完成进程对文件内容的获取任务。写操作也是一样,待写入的buffer在内核空间不能直接访问,必须要先拷贝到内核空间对应的主存,再写回磁盘(延迟写回),也是需要两次数据拷贝。
而使用mmap操作文件,创建新的虚拟内存区域和建立文件磁盘地址和虚拟内存区域映射这两步,没有任何文件拷贝操作。而之后访问数据时发现内存中并无数据而发起的缺页异常过程,可以通过已经建立好的映射关系,只使用一次数据拷贝,就从磁盘中将数据传入内存的用户空间中,供进程使用。
【从上面的描述中,也可以看出为什么常规的读写操作使用buffer效率要比较高,因为可以减少把数据从磁盘拷贝到内存的次数。】
mmap映射的页和其它的页并没有本质的不同.
所以得益于主要的3种数据结构的高效,其页映射过程也很高效:
(1) radix tree,用于查找某页是否已在缓存.
(2) red black tree ,用于查找和更新vma结构.
(3) 双向链表,用于维护active和inactive链表,支持LRU类算法进行内存回收.
mmap不是银弹.
(1) 对变长文件不适合.
(2) 如果更新文件的操作很多,mmap避免两态拷贝的优势就被摊还,最终还是落在了大量的脏页回写及由此引发的随机IO上.
所以在随机写很多的情况下,mmap方式在效率上不一定会比带缓冲区的一般写快.
参考:http://www.cnblogs.com/huxiao-tee/p/4660352.html