mmap映射、sendfile
mmap可以把磁盘文件的一部分直接映射到内存,这样文件中的位置直接就有对应的内存地址,对文件的读写可以直接用指针来做而不需要read/write函数传统文件的IO方式传统文件读写主要是调用read和write系统调用与内核进行交互数据先从磁盘通过DMA直接内存访问技术到达内核态缓冲区内核态缓冲区又会通过拷贝到达用户缓冲区期间会发生4次拷贝4次上下文切换mmap代替传统IO中的read操作相当于mmapwrite减少了一次cpu的拷贝就是把读缓冲区的地址与用户缓冲区的地址进行映射“用户态”与“内核态”共享这里就体现了从内核态的读缓冲区到用户缓冲区的一次cpu拷贝这个IO方式变为4次内核态切换mmap两次write两次3次拷贝mmap流程1.用户态调用mmap上下文从用户态变为内核态建立地址映射2、DMA把数据从硬盘拷贝到读缓冲区3、上下文从内核态转为用户态mmap调用返回4、调用write方法上下文从用户态转为内核态5、cpu将用户缓冲区的数据拷贝到socket缓冲区并通过DMA复制给硬盘6、上下文从内核态切换为用户态write返回优点1、将用户缓冲区内存是虚拟的只是与内核读缓冲区映射可以节省一半的内存空间适合大文件传输2、减少了一次从内核态读缓冲区到用户态缓冲区的cpu拷贝用内存读写代替了IO读写减少了read系统调用效率高。函数原型#include sys/mman. h void *mmap (void *addr, size_t len, int prot, int flag, int filedes, off_t off) ; /* addr: 指定映射的起始地址。通常设为 NULL由内核决定映射区域的具体位置。 len: 映射到地址空间中的字节数长度。必须为页大小的整数倍 prot: 映射区域的保护方式。可以使用 PROT_NONE 或以下常量的按位或 PROT_READ: 页面可读。 PROT_WRITE: 页面可写。 PROT_EXEC: 页面可执行。--动态库 flag: 影响映射区域的各种特性 MAP_SHARED: 对映射区域的写入数据会复制回文件并允许其他进程共享。 MAP_PRIVATE: 建立一个写入时拷贝Copy-on-Write的私有映射写入不会修改原文件。 MAP_ANONYMOUS: 匿名映射不关联任何文件此时 filedes 忽略。 filedes: 要映射的文件描述符。 off: 文件映射的偏移量通常需要是分页大小的整数倍。 返回值 成功: 返回指向映射区域的指针。 失败: 返回宏 MAP_FAILED (即 (void *)-1)并设置 errno */ int munmap (void *addr, size_t len) ; /* addr: 由 mmap 返回的起始地址。 len: 映射区域的大小。 返回值 成功: 返回 0。 失败: 返回 -1并设置 errno。 */注意事项1、调用mmap时系统会把文件立即读进内存吗不会mmap只是建立地址映射关系不会立即发生磁盘IO数据真正加载是在cpu首次访问这段地址时会触发缺页中断所以mmap是用到哪一页才加载哪一页2、mmap向应用程序提供的内存访问接口是内存地址连续的,但是对应的磁盘文件的block可以不是地址连续的3、mmap提供的内存空间是虚拟空间(虚拟内存),而不是物理空间(物理内存),因此完全可以分配远远大于物理内存大小的虚拟空间4、mmap由操作系统负责管理,对同一个文件地址的映射将被所有线程共享,操作系统确保线程安全以及线程可见性sendfilemmap虽然减少了一次从内核态到用户态的cpu拷贝但还是进行了4次上下文切换还是有开销的现在sendfile方法减少了2次上下文切换使用sendfile数据可以直接在内核空间中进行传播避免了用户空间与内核空间的拷贝又因为sendfile代替了readwrite节省了一次系统调用减少了两次上下文的切换sendfile流程1、用户通过sendfile方法发起系统调用从用户态切换为内核态2、DMA从硬盘拷贝到读缓冲区3、cpu将读缓冲区的数据拷贝到socket缓冲区4、DMA把数据从socket从socket缓冲区拷贝到网卡5、上下文从内核态切换为用户态sendfile返回使用情况通过上面的流程数据对用户空间完全不可见不需要用户空间操作的情况函数原型#include sys/sendfile.h ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count); /* out_fd: 输出文件描述符。数据将被写入此处。在 Linux 2.6.33 之前它必须是一个 Socket此后也可以是普通文件。 in_fd: 输入文件描述符。数据将从此读取。它必须指向支持类似 mmap 操作的文件通常是磁盘上的普通文件。 offset: 指向偏移量变量的指针。 如果为 NULL从文件的当前偏移量开始读取并自动更新文件指针。 如果不为 NULL指向一个起始位置。函数执行后该变量会被更新为传输后的新位置但不会改变 in_fd 原有的文件指针。 count: 打算在两个文件描述符之间传输的字节数。 返回值 成功: 返回实际写入 out_fd 的字节数ssize_t。失败: 返回 -1并设置 errno。 */总结传输方式DMA 拷贝 (硬件与内核)CPU 拷贝 (内核与用户)上下文切换 (状态转换)性能评价传统 IO(read/write)2 次2 次4 次效率最低数据需要在内核和用户空间反复搬运。mmap(内存映射)2 次1 次4 次中等减少了 1 次 CPU 拷贝但上下文切换次数依然较多。sendfile()(零拷贝)2 次1 次2 次效率最高不仅减少了 CPU 拷贝还大幅降低了上下文切换开销。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2440901.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!