一、写时拷贝
fork之后,父子进程共享所有的数据区域,但是内核将这些区域设为只读,只有在父子进程中任意一个进程试图修改数据(局部、全局、堆区)时,内核才会将所要修改的数据所在的页直接拷贝出来作为属于子进程的空间(注意不是拷贝所有数据,仅仅拷贝所要修改数据所在的那一页)。拷贝的内容在.heap段。
所以子进程的数据空间并不是在fork时复制出来的,并且在没有修改数据之前,子进程和父进程的空间是相同的。
二、写时拷贝的作用
1、提高了fork的效率
2、fork之后,子进程往往会调用exec调用新的进程
三、代码段
#include<iostream>
using namespace std;
class CString
{
public:
CString()
{
_str = new char[5];//前四位为标志位
_str[4] = 0;
getcount() = 1;//初始化写时拷贝的标志位
}
CString(const char *str)
{
_str = new char[strlen+1+4];
strncpy_s(_str+4,strlen(str)+1,str,strlen(str));
getcount() = 1;
}
CString(const CString &src)
{
_str = src._str;
getcount()++;
}
CString& operator=(const CString &src)
{
if(this == &src)//防止自赋值
{
return *this;
}
if(--getcount() == 0)//这里检查调用方的_str空间是否有其他对象共享,若没有则清空内存,防止造成内存泄露
{
delete []_str;
}
_str = src._str;
getcount()++;
return *this;
}
~CString()
{
if (--getcount() == 0)//判断是否有对象共享
{
delete[]_str;
}
}
/*
这里就是写时拷贝的核心
当主函数内试图要修改进程中某个数据时,先判断是否有其他对想共享
如果有,则会重新拷贝一份数据给对象。
*/
char &operator[](int pos)
{
if (getcount() > 1)
{
getcount()--;
char *tmp = _str;
_str = new char[strlen(tmp + 4) + 1 + 4];
strncpy_s(_str + 4, strlen(tmp + 4) + 1, tmp + 4, strlen(tmp + 4));
getcount() = 1;
}
return _str[pos+4];
}
private:
int &getcount()
{
return *(int *)_str;
}
char *_str;
friend ostream& operator<<(ostream& out, const CString &str);
};
//输出运算符的重载
ostream& operator<<(ostream& out, const CString &str)
{
out << str._str +4<< endl;
return out;
}