歡迎您光臨本站 註冊首頁

C++寫時拷貝實現原理及實例解析

←手機掃碼閱讀     zmcjlove @ 2020-06-26 , reply:0

一、什麼是寫時拷貝

寫入時複製是一種計算機程序設計領域的優化策略。其核心思想是,如果有多個調用者同時請求相同資源(如內存或磁盤上的數據存儲),他們會共同獲取相同的指針指向相同的資源,直到某個調用者試圖修改資源的內容時,系統才會真正複製一份專用副本(private copy)給該調用者,而其他調用者所見到的最初的資源仍然保持不變。
 這個過程對其他的調用者是透明的(transparently)。
 

此作法的主要優點是如果調用者沒有修改該資源,就不會有副本被建立,因此多個調用者只是讀取操作是可以共享同一份資源。

寫時拷貝技術是一種很重要的優化手段,核心是懶惰處理實體資源請求,在多個實體資源之間只是共享資源,起初是並不真正實現資源拷貝,
 

只有當實體有需要對資源進行修改時才真正為實體分配私有資源。但寫時拷貝技術技術也有它的優點和缺點:
 

1、寫時拷貝技術可以減少分配和複製大量資源時帶來的瞬間延時,但實際上是將這種延時附加到了後續的操作之中。

2、寫時拷貝技術可以減少不必要的資源分配。比如fork進程時,並不是所有的頁面都需要複製,父進程的代碼段和只讀數據段都不被允許修改,所以無需複製。
 

二、寫時拷貝技術的應用
 

1、虛擬內存管理中的寫時複製
 

一般把這種被共享訪問的頁面標記為只讀。當一個task試圖向內存中寫入數據時,內存管理單元(MMU)拋出一個異常,內核處理該異常時為該task分配一份物理內存並複製數據到此內存,
 

重新向MMU發出執行該task的寫操作。
 

比如Linux的fork()使用寫時拷貝頁來實現新進程的創建,它是一種可推遲甚至避免數據拷貝的技術,開始時內核並不會複製整個地址空間,而是讓父子進程共享地址空間,只有在寫時才複製地址空間,使得父子進程都擁有獨立的地址空間,即資源的複製是在只有需要寫入時才會發生。在此之前都是以讀的方式去和父進程共享資源,這樣,在頁根本不會被寫入的場景下,fork()立即執行exec(),無需對地址空間進行復制,fork()的實際開銷就是複製父進程的一個頁表和為子進程創建一個進程描述符,也就是說只有當進程空間中各段的內存內容發生變化時,父進程才將其內容複製一份傳給子進程,大大提高了效率。
 

2、數據存儲中的寫時複製
 

  Linux等的文件管理系統使用了寫時複製策略。

  舉個例子,比如我們有個程序要寫文件,不斷地根據網絡傳來的數據寫,如果每一次fwrite或是fprintf都要進行一個磁盤的I/O操作的話,都簡直就是性能上巨大的損失,

  因此通常的做法是,每次寫文件操作都寫在特定大小的一塊內存中(磁盤緩存),只有當我們關閉文件時,才寫到磁盤上(這就是為什麼如果文件不關閉,所寫的東西會丟失的原因)

3、軟件應用中的寫時複製
 

在我們經常使用的STL標準模板庫中的string類,也是一個具有寫時才拷貝技術的類。為了提高性能,STL中的許多類都採用了寫時拷貝技術。但是在C++11標準中為了提高並行性取消了這一策略

  class String  {  public:    //構造函數(分存內存)    String(char* tmp)    {      _Len = strlen(tmp);      _Ptr = new char[_Len + 1 + 1];      strcpy(_Ptr, tmp);      // 在數組尾部設置引用計數      _Ptr[_Len + 1] = 0;     }    //析構函數    ~String()    {      //引用計數減一      _Ptr[_Len + 1]--;       // 引用計數為0時,釋放內存      if (_Ptr[_Len + 1] == 0)      {        delete[] _Ptr;      }    }      //拷貝構造(共享內存)    String(string& str)    {      if (this->_Ptr != str)      {        //共享內存,.data()返回的是將string的類型轉換成char類型的指針        const char *p = str.c_str();        char* pp;        strcmp(pp, p);        this->_Ptr = pp;         this->_Len = str.size();        this->_Ptr[_Len + 1] ++; //引用計數加一        }    }      //對[]符進行重載,對字符串進行操作的時候,開始寫時複製    char& operator[](unsigned int idx)    {      if (idx > _Len || _Ptr == 0)      {        static char nullchar = 0;        return nullchar;      }      //引用計數減一      _Ptr[_Len + 1]--;         char* tmp = new char[_Len + 1 + 1];        strncpy(tmp, _Ptr, _Len + 1);        _Ptr = tmp;      // 設置新的共享內存的引用計數      _Ptr[_Len + 1] = 0;       return _Ptr[idx];    }    private:    int _Len;    char* _Ptr;  };

 


[zmcjlove ] C++寫時拷貝實現原理及實例解析已經有395次圍觀

http://coctec.com/docs/c/language/show-post-239787.html