浅识Linux的DMA拷贝、MMAP映射与sendfile原理

news/2025/2/23 22:35:21

Linux的DMA拷贝、MMAP映射与sendfile原理详解


1. DMA拷贝(Direct Memory Access)

原理
DMA(直接内存访问)是一种硬件机制,允许外设(如网卡、磁盘控制器)直接与内存交互,无需CPU参与数据拷贝。CPU仅需初始化传输参数(如源地址、目标地址、数据长度),后续传输由DMA控制器完成,从而释放CPU资源。

示例
假设从磁盘读取文件到内存:

  1. CPU通知磁盘控制器通过DMA传输数据到内存地址0x1000
  2. 磁盘控制器直接操作内存总线,将数据写入0x1000,完成后通过中断通知CPU。
  3. CPU无需逐字节拷贝数据,可并行处理其他任务。

图例

传统拷贝:  
磁盘 → CPU寄存器 → 内存  

DMA拷贝:  
磁盘 → 内存(直接操作,无需CPU)

2. MMAP映射(Memory Mapping)

原理
MMAP将文件直接映射到进程的虚拟地址空间,使得文件操作如同访问内存。内核通过页表将虚拟地址映射到文件对应的物理页,首次访问时触发缺页中断,由操作系统将文件内容加载到内存。

优势

  • 减少用户态与内核态之间的数据拷贝(如read()/write())。
  • 支持共享内存(多个进程映射同一文件)。

示例

// 将文件映射到内存
int fd = open("large_file.dat", O_RDWR);
void *addr = mmap(NULL, file_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
// 直接通过指针操作文件
memcpy(addr + offset, data, len);
munmap(addr, file_size);

图例

进程虚拟地址空间  
│                  
├─── Code          
├─── Data          
└─── Mapped File (通过页表直接关联磁盘文件)

3. sendfile系统调用

原理
sendfile()用于在内核态直接将文件数据从文件描述符(如磁盘文件)传输到套接字,无需用户态参与。其过程如下:

  1. 文件数据通过DMA从磁盘读取到内核缓冲区(Page Cache)。
  2. 内核直接将缓冲区数据通过DMA拷贝到网卡缓冲区(NIC Buffer)。
  3. 全程无需数据经过用户态,减少2次上下文切换和1次数据拷贝。

优化

  • 结合scatter-gather DMA,实现“零拷贝”(Zero-Copy),无需内核缓冲区到套接字缓冲区的拷贝。

示例

// Web服务器发送静态文件
int file_fd = open("index.html", O_RDONLY);
int socket_fd = accept(...);
sendfile(socket_fd, file_fd, NULL, file_size);

图例

传统传输:  
磁盘 → 内核缓冲区 → 用户缓冲区 → 内核Socket缓冲区 → 网卡  

sendfile传输:  
磁盘 → 内核缓冲区 → 网卡(直接DMA传输)

4、对比总结

技术数据拷贝次数CPU参与度典型场景
DMA0(硬件完成)仅初始化外设与内存交互(如磁盘I/O)
MMAP1(缺页加载)低(内存访问)大文件读写、进程间共享(大视频文件)
sendfile0-1低(系统调用)网络文件传输(如HTTP服务器)

图示补充

  • DMA拷贝:外设与内存直连,绕过CPU。
  • MMAP:进程虚拟地址空间直接映射到文件页。
  • sendfile:内核内部数据管道,避免用户态切换。

通过结合这些技术,Linux可显著提升I/O性能,降低系统开销。

----------分界线-----------------------------------------------------------------------------------------------

以下是 Linux 中 DMA 拷贝MMAP 映射 和 sendfile 的具体调用方法及示例代码


5、DMA 拷贝的隐式调用

DMA 由操作系统自动管理,开发者无需直接调用 DMA 接口,但可通过标准 I/O 函数(如 read/write)隐式触发 DMA 操作。


示例:从磁盘读取文件到内存

#include <fcntl.h>
#include <unistd.h>

int main() {
    int fd = open("data.bin", O_RDONLY); // 打开文件
    char buffer[4096];
    ssize_t bytes_read = read(fd, buffer, sizeof(buffer)); // 触发DMA拷贝
    close(fd);
    return 0;
}

说明

  • read 函数内部通过 DMA 将磁盘数据直接拷贝到用户态内存,无需 CPU 逐字节参与。
  • 数据流向:磁盘 → 内核缓冲区(DMA) → 用户缓冲区

6、MMAP 映射的显式调用

使用 mmap 系统调用将文件映射到进程内存空间,直接通过指针操作文件。


示例:修改文件内容

#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    int fd = open("example.txt", O_RDWR); // 打开可读写文件
    off_t file_size = lseek(fd, 0, SEEK_END); // 获取文件大小
    char *mapped = mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // 映射文件

    // 直接通过内存指针修改文件内容
    mapped[0] = 'A'; // 修改第一个字符为'A'
    msync(mapped, file_size, MS_SYNC); // 同步到磁盘(可选)
    munmap(mapped, file_size); // 解除映射
    close(fd);
    return 0;
}

说明

  • mmap 将文件映射到虚拟内存,修改内存即修改文件(需 MAP_SHARED 标志)。
  • 数据流向:磁盘 → 内核页缓存 → 进程虚拟内存(零拷贝)

7、sendfile 的高效传输

使用 sendfile 系统调用在内核态直接传输文件到网络套接字,避免用户态拷贝。


示例:Web服务器发送静态文件

#include <sys/sendfile.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>

int main() {
    int file_fd = open("index.html", O_RDONLY); // 打开文件
    int sock_fd = socket(AF_INET, SOCK_STREAM, 0); // 创建TCP套接字
    // ... 绑定(bind)、监听(listen)、接受连接(accept)等操作 ...

    off_t offset = 0;
    off_t file_size = lseek(file_fd, 0, SEEK_END); // 获取文件大小
    sendfile(sock_fd, file_fd, &offset, file_size); // 直接发送文件到套接字

    close(sock_fd);
    close(file_fd);
    return 0;
}

说明

  • sendfile 将文件内容从 file_fd 直接传输到 sock_fd,无需用户态缓冲区。
  • 数据流向:磁盘 → 内核缓冲区 → 网卡缓冲区(零拷贝)

(望各位潘安、各位子健/各位彦祖、于晏不吝赐教!多多指正!🙏)


http://www.niftyadmin.cn/n/5863833.html

相关文章

Android studio如何把新项目上传到svn仓库

原文链接&#xff1a;1、Android studio svn上传新项目&#xff0c;2、Android Studio向SVN上传新项目 我在android studio上创建新项目&#xff0c;这项目名&#xff1a;MyApplication6 先看一下&#xff1a;TortoiseSVN\bin下的没有svn.exe的解决问题&#xff0c;把svn.ex…

第二章 基础知识(7) - 配置

注意 客户端上的用户可以看到配置和设置文件&#xff0c;用户可以篡改数据。 请勿在应用的配置或文件中存储应用机密、凭据或任何其他敏感数据使用 WebAssembly 或Auto模式时&#xff0c;请记住所有组件代码都会编译并发送到客户端&#xff0c;用户可以在客户端对其进行反向编…

算法系列之贪心算法

在算法中&#xff0c;贪心算法&#xff08;Greedy Algorithm&#xff09;是一种常见的解决优化问题的算法。贪心算法的核心思想是&#xff1a;在每一步选择中都采取当前状态下最优的选择&#xff0c;即贪心的做出局部最优的决策&#xff0c;从而希望最终能够得到全局最优解。尽…

AI革命下的多元生态:DeepSeek、ChatGPT、XAI、文心一言与通义千问的行业渗透与场景重构

前言 人工智能技术的爆发式发展催生了多样化的AI模型生态&#xff0c;从通用对话到垂直领域应用&#xff0c;从数据挖掘到创意生成&#xff0c;各模型凭借其独特的技术优势与场景适配性&#xff0c;正在重塑全球产业格局。本文将以DeepSeek、ChatGPT、XAI&#xff08;可解释人…

JAVAWeb之Servlet学习

认识 Servlet 就是 Sun 公司开发动态 Web 的一门技术 Sun 在这些 API &#xff08;Application Programming Interface&#xff0c;应用程序接口&#xff09;中提供一个接口叫做&#xff1a;Servlet&#xff0c;如果你想开发一个Servlet程序&#xff0c;只需要完成两个小步骤…

多门店协同管理困难重重,管理系统如何破局?

在零售业的快速发展中&#xff0c;越来越多的企业选择通过多门店的方式扩展市场。然而&#xff0c;随着门店数量的增加&#xff0c;企业在日常运营中的管理难度也逐渐加大&#xff0c;尤其是在协调各个门店之间的资源、信息和流程时&#xff0c;往往面临诸多挑战。如何在多门店…

Windows逆向工程入门之结构体类特性分析

公开视频 -> 链接点击跳转公开课程博客首页 -> ​​​链接点击跳转博客主页 目录 基本特性 构造函数 静态成员 常量成员 继承构造 继承(单) 继承(多) 继承(菱) 多态(单) 多态(多) 虚表(单) 虚表(多) 纯虚 基本特性 #include <iostream> #include &l…

红队内网攻防渗透:内网渗透之内网对抗:实战项目VPC1打靶PHP-RCE三层代理路由防火墙上线密码喷射域控提权

红队内网攻防渗透 实战网络攻防靶场记录1.靶机配置详情讲解1.1 入口点靶机:Windows Server 20121.2 第一层靶机:Windows 7 + Windows 101.3 第二层靶机:Windows 2012 R21.4 第三层靶机:Windows 2016 web +Windows 2016 AD域1.5 攻击者系统 :Kali-linux2.靶场渗透完整流程2…