分享

深浅拷贝、COW及零拷贝

 Baruch 2024-03-02 发布于四川

图片

一、拷贝

copy,中文翻译成拷贝,也算是拼音大法,拷贝其实就是复制,按照传统的思维方式来思考,复制就拥有了两份啊。映射到计算机上,如果也是如此,那么就没有什么可讨论的了。可话说回来,计算机世界中,资源太稀缺了。在现在,虽然家庭中使用计算机,相对来说资源的稀缺性明显少见了。但是在实际工程应用中,却比比皆是。
早期的计算机,不但资源缺少(内存按K,硬盘按M),为了节省资源,提高速度使用浅拷贝。同时,由于CPU速度低,为了提高效率,就搞出了COW(Copy On Write-写时复制)和零拷贝。通过简单的说明,应该明白为什么一个简单的复制,在计算机中搞得如此复杂,不外乎是节省资源,提高效率。
但万物都有两面性,拷贝有深浅,就会有掌握不深入时的数据异常。零拷贝掌握不透,就不明白数据复制的时机。而COW在LINUX中更是应用的广泛,是理解父子进程内存分布的一个重要环节。
存储介质的容量和访问速度的不匹配以及价格的巨大差异,是各种拷贝技术兴起的根本原因。

二、说明和解析

1、深拷贝
顾名思义,就是把内存对象完全复制了一份,或者说在内存中有两份一模一样的内存。
2、浅拷贝
只是增加了一个对同一块内存的操作句柄(指针)。

图片


3、COW
写时复制,它通过父进程和子进程最初共享相同的页面(虚拟内存)来工作。共享页面标记为写时复制,即任何一个进程写入当前共享页面时,创建这些共享页面的副本。

图片


4、零复制
零拷贝其实就是指在数据拷贝过程中全程尽量避免(当然完全避免最好,但实际还是有差距)内存和CPU的参与,直接由DMA来操作(比如网卡和磁盘的数据交互)。

图片

三、内存的处理

所谓拷贝主要是在存储介质中的操作,在内存中,对于深拷贝和浅拷贝,只是对内存使用的一个不同的机制,在深拷贝中,需要完全的拷贝出来一份与当前内存相同大小的额外空间,而浅拷贝,则只是简单的增加一个指向(或者说可以得内存的句柄)句柄(指针)。换句话说,浅拷贝是多个指针操作相同的内存,那么结果可想而知,如果没有同步控制,数据一定会乱套。反之,深拷贝则不会有这种问题。不过,深拷贝增加了复制的时间开销和内存空间占用的开销,而浅拷贝则不会有(或者说只是一个指针的开销)。
COW的在内存中的处理有点类似于深浅拷贝的转换,为了提高效率,如果子进程没有写操作内存,只是读取的话,就共用一部分物理内存(fork出的子进程和父进程共用物理内存空间,但虚拟空间仍然不同),这也是和深浅拷贝细节不同之处。
而零拷贝更倾向于内存介质之间的传输,比如Cache和内存,内存和硬盘之类的,通过DMA(伪),mmap+write和sendfile。它其实的主要方式实现了介质之间的直接通信,而不是通过OS或者其它辅助的技术来多次拷贝和传输。在高版本的Linux中,当计算机中的网卡支持 SG-DMA(The Scatter-Gather Direct Memory Access)技术,就可以和sendfile直接融入,达到真正的零拷贝。但这些都基于PageCache即磁盘调整缓存技术。在实际项目中,Kafka和Nginx都有实际的工程应用,有兴趣可以看看。

四、示例

这里用c++给出一个深浅拷贝的例子,对于COW和零复制不好举例子,请大家自行查阅相关的技术源码:

//CopyData.h文件
#include <iostream>  

void TestCopy();
class CopyData
{

private:
    int num = 0;
    char *name = nullptr;
public:
    CopyData();
    ~CopyData();
    CopyData(const CopyData &s);//拷贝构造函数 
};

//CopyData.cpp文件
CopyData::CopyData()
{
    name = new char(100);
    std::cout << 'CopyData construct!' << std::endl;

}
CopyData::~CopyData()
{
    std::cout << '~CopyData destory! '  << std::endl;
    delete name;
    name = nullptr;
}
CopyData::CopyData(const CopyData &s)
{
    name = new char(100);
    strcmp(name,s.name);
    std::cout << ' CopyData copy construst!' << std::endl;
}

void TestCopy()
{
    CopyData c;
    CopyData c1(c);// 复制 
}

`

运行结果:

CopyData construct!
 CopyData copy construst!
~CopyData destory!

深浅拷贝也是所有语言必考的一个面试题,在新的js技术中,也有这项技术的存在,这是没想到的。照这种发展方向来看,未来可能所有的前端也要认真学习操作系统和计算机原理这两门课了。

五、总结

只要计算机世界中存储的数量和存储的速度一直不匹配,那么,拷贝技术就不可能会停止下来,它会发展出更好更快的方法和算法。反过来,如果存储的容量和速度都是无限的,那么拷贝就自然回归到了本质,只是一份简单的复制罢了。
学习一项技术的本质,就是要弄清楚这项技术的起源、发展和目的。弄明白为什么这么做?未来要怎么做?
知其然,然后知其所以然!

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多