大神教你C++寫時複製實現原理及例項解析
這篇文章主要介紹了C++寫時複製實現原理及例項解析,文中透過示例程式碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下 |
寫入時複製是一種計算機程式設計領域的最佳化策略。其核心思想是,如果有多個呼叫者同時請求相同資源(如記憶體或磁碟上的資料儲存),他們會共同獲取相同的指標指向相同的資源,直到某個呼叫者試圖修改資源的內容時,系統才會真正複製一份專用副本(private copy)給該呼叫者,而其他呼叫者所見到的最初的資源仍然保持不變。
這個過程對其他的呼叫者是透明的(transparently)。
此作法的主要優點是如果呼叫者沒有修改該資源,就不會有副本被建立,因此多個呼叫者只是讀取操作是可以共享同一份資源。
寫時複製技術是一種很重要的最佳化手段,核心是懶惰處理實體資源請求,在多個實體資源之間只是共享資源,起初是並不真正實現資源複製,只有當實體有需要對資源進行修改時才真正為實體分配私有資源。但寫時複製技術技術也有它的優點和缺點:
1、寫時複製技術可以減少分配和複製大量資源時帶來的瞬間延時,但實際上是將這種延時附加到了後續的操作之中。
2、寫時複製技術可以減少不必要的資源分配。比如fork程式時,並不是所有的頁面都需要複製,父程式的程式碼段和只讀資料段都不被允許修改,所以無需複製。
一般把這種被共享訪問的頁面標記為只讀。當一個task試圖向記憶體中寫入資料時,記憶體管理單元(MMU)丟擲一個異常,核心處理該異常時為該task分配一份實體記憶體並複製資料到此記憶體,重新向MMU發出執行該task的寫操作。
比如 的fork()使用寫時複製頁來實現新程式的建立,它是一種可推遲甚至避免資料複製的技術,開始時核心並不會複製整個地址空間,而是讓父子程式共享地址空間,只有在寫時才複製地址空間,使得父子程式都擁有獨立的地址空間,即資源的複製是在只有需要寫入時才會發生。在此之前都是以讀的方式去和父程式共享資源,這樣,在頁根本不會被寫入的場景下,fork()立即執行exec(),無需對地址空間進行復制,fork()的實際開銷就是複製父程式的一個頁表和為子程式建立一個程式描述符,也就是說只有當程式空間中各段的記憶體內容發生變化時,父程式才將其內容複製一份傳給子程式,大大提高了效率。
Linux等的檔案管理系統使用了寫時複製策略。
舉個例子,比如我們有個程式要寫檔案,不斷地根據網路傳來的資料寫,如果每一次fwrite或是fprintf都要進行一個磁碟的I/O操作的話,都簡直就是效能上巨大的損失,
因此通常的做法是,每次寫檔案操作都寫在特定大小的一塊記憶體中(磁碟快取),只有當我們關閉檔案時,才寫到磁碟上(這就是為什麼如果檔案不關閉,所寫的東西會丟失的原因)
在我們經常使用的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; };
以上就是本文的全部內容,希望對大家的學習有所幫助。
原文地址:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31559985/viewspace-2703881/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 映象分層原理及容器層寫時複製
- Redis 複製實現原理Redis
- javascript實現複製一個陣列程式碼例項JavaScript陣列
- NIO原理及例項
- 實現Mysql延時複製MySql
- SpringMVC實現原理及解析SpringMVC
- Java物件複製原理剖析及最佳實踐Java物件
- Python scrapy增量爬取例項及實現過程解析Python
- JavaScript 預解析的原理及實現JavaScript
- 原生js實現的物件複製和擴充套件程式碼例項JS物件套件
- js實現的點選複製選中文字程式碼例項JS
- 基本複製應用例項(轉)
- C++ 多型的實現及原理C++多型
- Python物件導向多型實現原理及程式碼例項Python物件多型
- MySQL5.5半同步複製實現原理MySql
- Exchanger的工作原理及例項
- 用shell實現Mysql延時複製MySql
- InnoDB MVCC實現原理及原始碼解析MVC原始碼
- Vue 原始碼解析(例項化前) - 響應式資料的實現原理Vue原始碼
- Vue 原始碼解析(例項化前) – 響應式資料的實現原理Vue原始碼
- Mysql主從複製原理及搭建MySql
- mysql主從複製原理及配置MySql
- c++介面定義及實現舉例C++
- C++記憶體池的實現例項C++記憶體
- 大神教你實現redis鍵空間通知Redis
- 從 MySQL 到 ClickHouse 實時複製與實現MySql
- AWS CLI 實現 S3與EC2例項間檔案複製S3
- 教你JavaScript實現一鍵複製內容剪貼簿JavaScript
- SpringCloud——Feign例項及原理SpringGCCloud
- js 實現深複製/深複製JS
- mysql 複製原理與實踐MySql
- Redis replication主從複製原理及配置Redis
- mysql 5.7 主從複製搭建及原理MySql
- MySQL 傳統複製與 GTID 複製原理及操作詳解MySql
- jQuery複製頁面元素程式碼例項jQuery
- SqlServer同例項複製資料庫方法SQLServer資料庫
- Oracle 10g高階複製例項Oracle 10g
- 使用impdp實現資料在不同使用者、不同例項之間快速複製