【Linux】写时复制(CopyOnWrite)|写时拷贝

写时复制(Copy-On-Write, COW)是一种优化技术,用于推迟或避免内存拷贝,直到数据被修改。在Linux的fork()调用中,子进程与父进程共享内存页,只有在修改时才复制。此外,COW也被应用于字符串和智能指针如shared_ptr的实现中,通过引用计数来管理内存。" 73864046,6167150,使用log4j2配置日志添加IP和UUID,"['日志', 'log4j2', '配置']
摘要由CSDN通过智能技术生成

目录

什么是写时拷贝

写时拷贝原理


原文:

https://blog.csdn.net/Code_beeps/article/details/92838520

http://c.biancheng.net/view/1272.html

什么是写时拷贝

写时拷贝(copy-on-write, COW)就是等到修改数据时才真正分配内存空间,这是对程序性能的优化,可以延迟甚至是避免内存拷贝,当然目的就是避免不必要的内存拷贝。

在这里插入图片描述

典型例子:

在 Linux 系统中,调用 fork 系统调用创建子进程时,并不会把父进程所有占用的内存页复制一份,而是与父进程共用相同的内存页,而当子进程或者父进程对内存页进行修改时才会进行复制 —— 这就是著名的 写时复制 机制。

(也就是只有进程空间的某页内存的内容要发生变化时,才会将父进程的该页内存复制一份给子进程。)

这些共享页面标记为写时复制,这意味着如果任何一个进程写入共享页面,那么就创建共享页面的副本,写时复制如图 1 所示,图中分别反映了修改页面 C 的前与后。

进程1修改页面C前后

写时拷贝原理


  写时拷贝技术实际上是运用了一个 “引用计数” 的概念来实现的。在开辟的空间中多维护四个字节来存储引用计数。
有两种方法:
①:多开辟四个字节(pCount)的空间,用来记录有多少个指针指向这片空间。
②:在开辟空间的头部预留四个字节的空间来记录有多少个指针指向这片空间。
  当我们多开辟一份空间时,让引用计数+1,如果有释放空间,那就让计数-1,但是此时不是真正的释放,是假释放,等到引用计数变为 0 时,才会真正的释放空间。如果有修改或写的操作,那么也让原空间的引用计数-1,并且真正开辟新的空间。

linux 下的 fork() 就是用的写时拷贝技术,引用计数不光在 string 这里用到,还有智能指针 shared_ptr 也用到了引用计数来解决拷贝问题。

举个例子
string 的写时拷贝(维护一个指针):

class String
{
public:
	//构造
	String(const char* str)
		:_str(new char[strlen(str) + 1])
		,_pCount(new int(1))
	{
		strcpy(_str, str);
	}

	//拷贝构造
	String(const String& s)
		:_str(s._str)
		,_pCount(s._pCount)
	{
		(*_pCount)++;
	}

	//赋值运算符重载
	String& operator=(const String& s)
	{
		if(_str != s._str)
		{
			if(--(*_pCount) == 0)
			{
				delete[] _str;
				delete _pCount;
			}
			_str = s._str;
			_pCount = s._pCount;
			(*_pCount)++;
		}
		return *this;
	}

	~String()
	{
		if(--(*_pCount) == 0)
		{
			delete[] _str;
			delete _pCount;
		}
	}

	char& operator[](size_t pos)
	{
		if(*_pCount > 1)
		{
			char* newstr = new char[strlen(_str) + 1];
			strcpy(newstr, _str);
			--(*_pCount);
			_str = newstr;
			_pCount = new int(1);
		}
		return _str[pos];
	}

	const char* c_str()
	{
		return _str;
	}
private:
	char* _str;
	int* _pCount;
};

源码中的写法:在空间的头部维护四个字节的空间,记录引用的个数。放在头部维护效率能高一些,如果放在尾部维护的话,每次开辟新的空间都要讲这四个字节也向后挪动相应的位置,所以放在前面效率高点

class String
{
public:
	//构造
	String(const char* str)
		:_str(new char[strlen(str) + 4 + 1])
	{
		_str += 4;		//前四个字节放引用计数
		strcpy(_str, str);
		GetRefCount() = 1;
	}

	//拷贝构造
	String(const String& s)
		:_str(s._str)
	{
		GetRefCount()++;
	}

	//赋值运算符重载
	String& operator=(const String& s)
	{
		if(_str != s._str)
		{
			if(--GetRefCount() == 0)
			{
				delete[] (_str - 4);
			}
			_str = s._str;
			GetRefCount()++;
		}
		return *this;
	}

	~String()
	{
		if(--GetRefCount() == 0)
		{
			delete[] (_str - 4);
			_str = nullptr;
		}
	}

	char& operator[](size_t pos)
	{
		if(GetRefCount() > 1)
		{
			--GetRefCount();
			char* newstr = new char[strlen(_str) + 4 + 1];
			newstr += 4;
			strcpy(newstr, _str);
			_str = newstr;
			GetRefCount() = 1;
		}
		return _str[pos];
	}

	int& GetRefCount()
	{
		return *((int*)(_str - 4));		//前四个字节为引用计数
	}
private:
	char* _str;
};

推荐文章:https://www.cnblogs.com/biyeymyhjob/archive/2012/07/20/2601655.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值