零拷贝技术原理以及实现

简介: 零拷贝技术原理以及实现

内核态和用户态

内核态:指的是操作系统层面的资源调度,例如内存分配,进程/线程管理调度,硬件驱动,io中断等等.

用户态:指的是软件层面的,对变量的操作,运算等低权限操作,我们所有的软件都可以认为运行在用户态

用户态有着独立的虚拟地址映射,软件只能操作自身进程的变量内存. 由于软件层面也是需要去获取io数据,去创建进程线程的,这个时候,使用语言的函数调用,就会去调用系统的内核函数,从用户态转换成内核态执行.

例如以下代码:

<?php
/**
 * Created by PhpStorm.
 * User: tioncico
 * Date: 20-7-2
 * Time: 下午9:37
 */
$a = 1;//用户态,只能操作该进程的内存
$b = 2;
$c = $a + $b;//用户态,只调用cpu运算,并且结果保存在进程内存中
if (pcntl\_fork()) {//pcntl\_fork调用 内核 fork函数,由用户态转内核态,复制一个新的进程
    echo "hello world";//用户态转内核态,从用户态获取字符串,转到内核态缓冲区并输出.
} else {
    $data = file\_get\_contents("http://www.php20.cn");
    //1:首先程序将网址等,转换成http协议头
    //2:将http协议头字符串从程序缓冲区复制到系统内核socket缓冲区
    //3:从socket缓冲区复制到网卡发送
    //4:接收到数据之后,由网卡复制到系统内核socket缓冲区
    //5:系统内核socket缓冲区复制到程序内存中,并赋值到$data
}

在大部分时间,程序都会运行在用户态上,只有当程序需要获取高权限时,通过一些方法从用户态陷入到内核态执行:

- io相关操作,设置时钟指令,内存操作(申请内存,清理内存)

- 终端,异常,陷入等

- 进程/线程 管理

- 系统调用,调用硬件等

- 用户内存地址的转换,由程序地址转为物理地址映射

零拷贝

在上面,我们简单的了解了用户态和内核态的区别,在用户态中,所有的内存地址都是独立的虚拟地址,如果需要读取外部数据时,将由内核态的缓冲区复制一份到用户态内存中,例如下面的这个例子:

echo file\_get\_contents("./test.txt");//在fpm中执行

它的流程为:

1:从硬盘中读取数据到内核态缓冲区,第一次复制拷贝

2:内核态复制数据到用户态,第二次拷贝

3:用户态获取数据之后,echo 发送数据,复制数据到内核态,socket缓冲区中,第三次拷贝

4:内核态 socket缓冲区中数据复制到网卡中,转成网卡协议发送,第四次拷贝

可以看出,当我们通过http请求一个文件时,会出现4次拷贝.

为什么不直接将硬盘数据复制到用户态?

操作系统会根据读取的文件,预存储到内核态内存中,因为硬盘效率非常慢,所以当有多次相同文件读取请求时,可以将文件数据从内核态缓存中直接复制到用户态内存中,节省文件操作

零拷贝技术就是避免cpu将数据从一块存储位置拷贝到另一块位置,通过以下技术可以实现零拷贝:

mmap


mmap 可以将一个文件预加载到内核空间中,并于用户空间共享内存地址,这样就可以避免数据从内核态复制到用户态中,可节省一次拷贝.例如:

#include <sys/mman.h> /* for mmap and munmap */
#include <sys/types.h> /* for open */
#include <sys/stat.h> /* for open */
#include <fcntl.h>     /* for open */
#include <unistd.h>    /* for lseek and write */
#include <stdio.h>
int main(int argc, char **argv) {
    int fd;
    int file_length = 0;
    char *mmap_address;
    char file_name\[\] = "/Users/tioncico/CLionProjects/cTest/1.txt";
    fd = open(file\_name, O\_RDWR | O\_CREAT, S\_IRUSR | S_IWUSR);//打开文件
    file\_length = lseek(fd, 0L, SEEK\_END);
    mmap\_address = mmap(NULL, file\_length, PROT\_READ, MAP\_SHARED, fd, 0);//mmap映射文件地址
    printf("%s",mmap_address);//假设这里是发送数据
    close(fd);
    munmap(mmap\_address,file\_length);
    return 0;
}

它的流程为:

1:首先将磁盘文件数据读取  file_length 大小(注意,mmap这边大小应该为内核空间数据页大小的倍数,例如4k*2) 到内核空间中.第一次拷贝

2:通过mmap,将内核空间地址映射到用户态地址

3:通过printf(假设是socket网卡发送). 将内核框架数据拷贝到socket缓冲区,第二次拷贝

4:socket缓冲区数据复制到网卡中,第三次拷贝

sendfile

#include<sys/sendfile.h>
ssize\_t senfile(int out\_fd,int in\_fd,off\_t* offset,size_t count);

使用 sendfile函数流程为:

1:首先读取磁盘文件,将数据读取拷贝到内核空间中 ,第一次拷贝

2:将内核空间的内存地址以及偏移量传输到socket缓冲区中

3:socket直接从内核空间读取数据

4:将内核空间的数据通过socket复制到网卡中,第二次拷贝

sendfile只能实现文件->socket,不能由socket到文件

目录
相关文章
文件覆盖写入和追加写入:使用场景、命令和技巧详解
文件覆盖写入和追加写入:使用场景、命令和技巧详解
1960 0
|
iOS开发 MacOS Python
在Mac 上搭建Pygame开发环境(含安装错误的解决办法)
在Mac 上搭建Pygame开发环境(含安装错误的解决办法)
1332 0
|
安全 Java
【Java面试】ConcurrentHashMap的key为什么不允许为null?
【Java面试】ConcurrentHashMap的key为什么不允许为null?
592 0
|
6月前
|
敏捷开发 设计模式 测试技术
软考软件评测师——软件工程之开发模型与方法
本内容主要介绍了软件开发过程中的核心概念及主流模型,包括瀑布模型、原型模型、增量模型、螺旋模型和敏捷开发等。每种模型各有优劣,适用于不同场景:瀑布模型适合需求明确的大型项目;螺旋模型适用于高风险复杂系统;增量模型支持模块化开发;原型模型适合需求模糊的小型项目;敏捷方法则强调灵活响应与持续交付。此外,还通过历年真题解析,深入探讨了各模型的应用场景及其特点,为实际开发提供了理论指导与实践经验。选择合适的开发模型需综合考虑需求明确度、项目规模、团队经验等因素。
|
设计模式 前端开发 C#
使用 Prism 框架实现导航.NET 6.0 + WPF
使用 Prism 框架实现导航.NET 6.0 + WPF
484 10
ly~
|
缓存 监控 编译器
如何优化 SDL 图形库在不同系统中的性能
SDL(Simple DirectMedia Layer)是一个跨平台的多媒体开发库,用于优化不同系统中的图形库性能。在银河麒麟系统中,通过SDL渲染模式提升视频监控客户端性能,支持更多播放路数。优化方法包括:1) 图像加载时使用SDL_ConvertSurface转换图像格式,避免内存中存在多份拷贝;2) 在VSCode下配置SDL时,合理设置编译器选项和项目架构,减少API调用带来的性能开销;3) 使用SDL库在景嘉微7200显卡上优化渲染性能,提高播放路数。综合考虑图像加载优化、系统特性利用及硬件兼容性,以实现最佳性能。
ly~
783 6
|
前端开发 虚拟化 内存技术
SPDK vhost target
SPDK vhost target
|
数据管理 API 调度
阿里云百炼平台知识检索应用评测:搭建之旅与一点建议
阿里云百炼平台成为企业智能化转型的重要工具之一。
用户态和内核态是如何切换的?
【10月更文挑战第28天】用户态和内核态的切换是通过系统调用指令、异常和中断等机制来实现的。这些机制确保了应用程序能够在需要时请求内核提供的服务,同时也保证了内核能够对系统资源进行有效的管理和保护,维护系统的稳定性和安全性。通过准确地保存和恢复上下文信息,实现了用户态和内核态之间的无缝切换,为计算机系统的正常运行提供了有力保障。
|
JavaScript
element-ui 在vue中el-input输入框的autofocus属性失效【解决方案】
element-ui 在vue中el-input输入框的autofocus属性失效【解决方案】
844 1