国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發(fā) > 綜合 > 正文

C++永久對象存儲 (Persistent Object Storage for C++)

2024-07-21 02:40:36
字體:
供稿:網(wǎng)友

  
 

 
 
C++永久對象存儲 (Persistent Object Storage for C++)
簡介 描述對象類型 從存儲器中分配和釋放對象 永久對象協(xié)議 存儲器構(gòu)造函數(shù) 打開存儲器 POST++ 的安裝 POST++ 類庫 和 POST++一起使用 STL 類 替換標(biāo)準(zhǔn)分配子 如何使用 POST++ S調(diào)試 POST++ 應(yīng)用的細(xì)節(jié) 關(guān)于 POST++ 更多的一些信息 簡介
POST++ 提供了對應(yīng)用對象的簡單有效的存儲. POST++ 基于內(nèi)存文件鏡像機(jī)制和頁面鏡像處理。POST++ 消除了對永久對象訪問的開銷. 此外 POST++ 支持多存儲,虛函數(shù), 數(shù)據(jù)更新原子操作, 高效的內(nèi)存分配和為指定釋放內(nèi)存方式下可選的垃圾收集器. POST++ 同樣可以很好的工作在多繼續(xù)和包含指針的對象上。

 
描述對象類型
POST++ 存儲治理需要一些信息以使永久對象類型支持垃圾收集器,裝載時引用重定位和初始化虛表內(nèi)函數(shù)指針。但不幸的是C++語言沒有提供運(yùn)行時從類中或許這些信息的機(jī)制。為了避免使用一些非凡的工具(預(yù)處理器)或“臟哄騙”途徑(從調(diào)試信息中獲取類信息),這些信息必須由程序員來指明。這些稱為類注冊器的東西可以簡單的通過POST++提供的一些宏來實(shí)現(xiàn)。

POST++ 在從存儲器重載入對象時調(diào)用缺省構(gòu)造函數(shù)來初始化對象。為了使對象句柄能夠存儲,程序員必須在類定義中包含宏 CLASSINFO(NAME, FIELD_LIST) . NAME 指明對象的名字。 FIELD_LIST 描述類的的引用字段。在頭文件 classinfo.h 定義了三個宏用于描述字段:
REF(x) 描述一個字段. REFS(x) 描述一個一維固定數(shù)組字段。. (例如:定長數(shù)組). VREFS(x) 描述可變一維數(shù)組字段。可變數(shù)組只能是類的最后一個成員。當(dāng)你定義類的時候,你可以指定一個僅包含一個元素的數(shù)組。具體對象實(shí)例中的元素個數(shù)可以在生成時指定。
這些宏列表必須用空格分開: REF(a) REF(b) REFS(c). 宏 CLASSINFO 定義了缺省構(gòu)造函數(shù) (沒有參數(shù)的構(gòu)造函數(shù)) 和類描述符. 類描述符是類的一個靜態(tài)成員名為 self_class. 這樣類 foo 的描述符可以通過 foo::self_class 訪問. 基類和成員的缺省構(gòu)造函數(shù)會被編譯器自動調(diào)用,你不必?fù)?dān)心需要明確調(diào)用他們。但是對于序列化的類中的結(jié)構(gòu)成員不要忘記在結(jié)構(gòu)定義中使用 CLASSINFO 宏。然后通過存儲器治理注冊該類使其可被訪問。這個過程由宏 REGISTER(NAME) 完成。類名將和對象一起放在存儲器中。在打開存儲器的時候類在存儲和應(yīng)用程序之間被鏡像。存儲器中的類名和程序中的類名進(jìn)行比較。假如有類沒有被程序定義或應(yīng)用程序和存儲器中的類有不同的大小,程序斷言將失敗。

下面的例子闡述了這些規(guī)則:

strUCt branch { object* obj; int key; CLASSINFO(branch, REF(obj));};class foo : public object { PRotected: foo* next; foo* prev; object* arr[10]; branch branches[8]; int x; int y; object* childs[1]; public: CLASSINFO(foo, REF(next) REF(prev) REFS(arr) VREFS(linked)); foo(int x, int y);};REGISTER(1, foo);main() { storage my_storage("foo.odb"); if (my_storage.open()) { my_root_class* root = (my_root_class*)my_storage.get_root_object(); if (root == NULL) { root = new_in(my_storage, my_root)("some parameters for root"); } ... int n_childs = ...; size_t varying_size = (n_childs-1)*sizeof(object*); // We should suBTract 1 from n_childs, because one element is already // present in fixed part of class. foo* fp = new (foo:self_class, my_storage, varying_size) foo(x, y); ... my_storage.close(); } }

 
從存儲器中分配和釋放對象
POST++ 為了治理存儲內(nèi)存提供了非凡的內(nèi)存分配子. 這個分配子使用兩種不同的方法: 針對分配小對象和大對象。所有的存儲內(nèi)存被劃分為頁面(頁面的大小和操作系統(tǒng)的頁面大小無關(guān),目前版本的 POST++ 中采用了 512 字節(jié)). 小對象是這樣一些對象,他們的大小小于或等于256字節(jié)(頁面大小/2). 這些對象被分配成固定大小的塊鏈接起來。每一個 鏈包含相同大小的塊。分配對象的大小以8個字節(jié)為單位。
為每個對象分配的包含這些塊大小為256的的鏈的數(shù)量最好不要大于14(不同的均衡頁面數(shù)). 在每個對象之前 POST++ 分配一個對象頭,包含有對象標(biāo)識和對象大小。考慮到頭部剛好8個字節(jié),并且在C++中對象的大小總大于0,大小為8的塊鏈可以舍棄。分配和釋放小對象通常情況下是非常快的: 只需要從L1隊(duì)列中進(jìn)行一次插入/刪除操作. 假如鏈為空并且我們試圖分配新的對象,新頁被分配用來存儲像目前大小的對象(頁被劃分成塊添加到鏈表中)。大對象(大于256字節(jié))所需要的空間從空閑頁隊(duì)列中分配。大對象的大小和頁邊界對齊。POST++ 使用第一次喂給隨機(jī)定位算法維護(hù)空閑頁隊(duì)列(所有頁的空閑段按照地址排列并用一個非凡的指針跟隨隊(duì)列的當(dāng)前位置)。存儲治理的實(shí)現(xiàn)見文件 storage.cxx

使用顯式還是隱含的內(nèi)存釋放取決于程序員。顯式內(nèi)存釋放要快(非凡是對小對象而言)但是隱含內(nèi)存釋放(垃圾收集)更加可靠。在 POST++ 中使用標(biāo)志和清除垃圾收集機(jī)制。在存儲中存在一個非凡的對象:根對象。垃圾收集器首先標(biāo)志所有的對象可被根對象訪問(也就是可以從根對象到達(dá),和通過引用遍歷)。這樣在第一次GC階段所有未被標(biāo)志的對象被釋放。垃圾收集器可以在對象從文件載入的時候生成(假如你傳遞 do_garbage_collection 屬性給 storage::open() 方法)。也可以在程序運(yùn)行期間調(diào)用 storage::do_mark_and_sweep() 方法調(diào)用垃圾收集器。但是請務(wù)必確定沒有被程序變量指向的對象不可從根對象訪問(這些對象將被GC釋放)。

基于多繼續(xù)C++類在對象中可以有非零偏移并且對象內(nèi)也可能有引用。這是我們?yōu)槭裁匆褂梅欠驳募夹g(shù)訪問對象頭的原因。POST++ 維護(hù)頁分配位圖,其中每一個位對應(yīng)存儲器中的頁。假如一些大對象分配在幾個頁中,所有這些對象占用的頁所對應(yīng)的位除了第一個外都被置為1。所有其他頁在位圖中有對應(yīng)清空位。要找到對象起始地址,我們首先按頁大小排列指針值。然后 POST++ 從位圖中查找對象起始頁(該頁在位圖中有零位)。然后從頁開始處包含的對象頭中取出對象大小的信息。假如大小大于頁大小的一半那我們已經(jīng)找到了對象描述:它在該頁的開始處。反之我們計算頁中所使用的固定塊的大小并且把頁中指針偏移按塊大小計算出來。這種頭部定位方案被垃圾收集器使用,類 object 定義了 Operator delete,和被從對象頭部解析出對象大小和類信息的方法使用。

在 POST++ 中提供了非凡重載的 new 方法用于存儲中的對象分配。這個方法需要創(chuàng)建對象的類描述,創(chuàng)建對象的存儲器,以及可選的對象實(shí)例可變部分的大小作為額外的參數(shù)。宏 new_in(STORAGE, CLASS) 提供永久對象創(chuàng)建“語法糖”。永久對象可以被重定義的 operator delete 刪除。
永久對象協(xié)議
在 POST++ 中所有的永久對象的類必須繼續(xù)自 object.h 中定義的類 object 。這個類不含任何變量并提供了分配/釋放對象及運(yùn)行時得到類信息和大小的方法。類 object 可以是多繼續(xù)中一個基類(基類的次序無所謂)。每一個永久類必須有一個供POST++ 系統(tǒng)使用的構(gòu)造函數(shù)(見 Describing object class 一節(jié))。這意味著你不能使用沒有參數(shù)的構(gòu)造函數(shù)來初始化。假如你的類構(gòu)造函數(shù)甚至沒有有意義的參數(shù),你必須加一個虛構(gòu)的以和宏 CLASSINFO 創(chuàng)建的構(gòu)造函數(shù)區(qū)別開來。

為了訪問永久存儲器中的對象程序員需要某種根對象,通過它可以使用普通的C指針訪問到每一個其他對象。POST++ 存儲器提供了兩個方法用于指定和得到根對象的引用:

void set_root_object(object* obj); object* get_root_object();

當(dāng)你創(chuàng)建新存儲時 get_root_object() 返回 NULL。你需要通過 set_root_object() 方法創(chuàng)建根對象并且在其中保存引用。下一次你打開存儲時,根對象可以通過 get_root_object() 得到。

提示:在實(shí)際應(yīng)用中類通常在程序開發(fā)和維護(hù)過程中被改變。不幸的是 POST++ 考慮到的簡單沒有提供自動對象轉(zhuǎn)換的工具(參見 GOODS 中的懶惰對象更新設(shè)計示例),所以為了避免添加新的字段到對象中,我只能建議你在對象中保留部分空間供將來使用。這對根對象來說意義尤其重大,因?yàn)樗切录尤雽ο蟮膬?yōu)選者。你也需要避免轉(zhuǎn)換根對象的引用。假如沒有其他對象含有指向根對象的引用,那么根對象可以被簡單的改變(通過 set_root_object 方法)到新類的實(shí)例。POST++ 存儲提供設(shè)置和取得村出版標(biāo)識的方法。這個標(biāo)識可以用于應(yīng)用根據(jù)存儲器和應(yīng)用的版本來更新存儲器中對象。

 
存儲器構(gòu)造函數(shù)
你可以在應(yīng)用中同時使用幾個存儲器。存儲器構(gòu)造函數(shù)有一個必需的參數(shù) - 存儲文件路徑。假如這個文件沒有擴(kuò)展名,那么 POST 為文件名添加一個后綴“.odb”。這個文件名也被 POST++ 用于形成幾個輔助文件的名字:

 
文件描述使用時機(jī)后綴包含新存儲器映像的臨時文件用于非事務(wù)處理模式下保存存儲器新映像".tmp"事務(wù)記錄文件用于事務(wù)模式下保存鏡像頁面".log"保存存儲器文件備份僅用于Windows-95下重命名臨時文件".sav"
存儲器構(gòu)造函數(shù)的另兩個參數(shù)具有缺省值。第一個參數(shù) max_file_size 指出存儲器文件擴(kuò)展限制。假如存儲器文件大于 storage::max_file_size 那么它不會被切除但是也不可能更進(jìn)一步的擴(kuò)展。假如 max_file_size 大于文件大小,行為依靠于打開存儲器的模式。在事務(wù)模式下,文件在讀寫保護(hù)下被鏡像到內(nèi)存中。Windows-NT/95 擴(kuò)展文件大小到 max_file_size。文件大小被 storage::close() 方法縮短到存儲器中最后一個對象的邊界。在 Windows 中為了以讀寫模式打開存儲器需要在磁盤上至少有 storage::max_file_size 的空閑字節(jié)數(shù)即使你不預(yù)備向其中加入新對象。

存儲器構(gòu)造函數(shù)的最后一個參數(shù)是 max_locked_objects,這個參數(shù)僅在事務(wù)模式下用于提供鏡像頁面的寫事務(wù)記錄文件的緩沖區(qū)。為了提供數(shù)據(jù)一致性 POST++ 必須保證修改頁在刷新到磁盤前鏡像頁被保存在事務(wù)記錄文件中。POST++ 使用兩個途徑中的一個:同步記錄寫 (max_locked_objects == 0) 和在內(nèi)存中頁面鎖定的緩沖寫。通過內(nèi)存中鎖定頁面,我們可以保證它在事務(wù)記錄緩沖錢不被交換到磁盤上。
鏡像頁面在異步方式下被寫到事務(wù)記錄文件中 (包括啟用操作系統(tǒng)緩沖)。當(dāng)鎖定頁面數(shù)超過 max_locked_pages,記錄文件緩沖被刷新到磁盤上并且所有鎖定頁面被解鎖。這個方法可以顯著的提高事務(wù)處理能力(在NT下提高了5倍)。但是不幸的是不同的操作系統(tǒng)使用不同的方法在內(nèi)存中鎖定頁面。
Windows 95 根本不支持。 在 Windows NT 每個進(jìn)程可以鎖定它的頁面,但是鎖定頁面的總數(shù)不可以超過進(jìn)程運(yùn)行配置限制。在缺省情況下進(jìn)程可以鎖定超過30個的頁面。假如你指定 max_locked_pages 參數(shù)大于30,那么 POST++ 將試圖擴(kuò)展進(jìn)程配置適合你的需求。但是從我的經(jīng)驗(yàn)來看30個和60個鎖定頁面之間性能的差距是非常小的。 在Unix下只有超級用戶可以在內(nèi)存中鎖定頁面。這是之所以文件構(gòu)造函數(shù)檢查進(jìn)程是否具有足夠的權(quán)限使用鎖定操作。因此假如你指定 max_locked_pages 參數(shù)大于0,那么在存儲類創(chuàng)建時將決定使用同步還是異步寫事務(wù)記錄文件。假如你希望使用內(nèi)存鎖定機(jī)制帶來的好處(2-5 倍,根據(jù)事務(wù)類型),你需要改變你的應(yīng)用的所有者為 root 并且給予 set-user-ID 權(quán)限:chmod +s application.
 
打開存儲器
POST++ 使用內(nèi)存內(nèi)存映射機(jī)制訪問文件中的數(shù)據(jù)。在 POST++ 通過兩個不同的方法提供數(shù)據(jù)一致性。首先而且更加先進(jìn)的是基于事務(wù)機(jī)制使用的鏡像頁面在出錯后來提供存儲恢復(fù)和事務(wù)回滾。在寫鏡像頁面前創(chuàng)建運(yùn)算被使用。這個運(yùn)算以如下方式執(zhí)行:所有文件映射頁面被設(shè)置為只讀保護(hù)。任何對這些頁面的寫訪問將引起訪問違反異常。這個異常被一個非凡的句柄捕捉,它改變頁面保護(hù)為可讀寫并放這個頁面的拷貝在事務(wù)記錄文件中(記錄文件名為原文件名和后追“.log”的組合)。所有接下來這個頁面的寫操作將不再引起頁面錯誤。存儲器方法 commit() 刷新所有的改變頁面到磁盤上并截斷記錄文件。storage::commit() 方法被 storage::close() 隱含調(diào)用。假如錯誤在 storage::commit() 操作前發(fā)生,所有的改變將通過拷貝事務(wù)記錄中改變的頁面到存儲數(shù)據(jù)文件被復(fù)原。同樣所有的改變可以通過顯式調(diào)用 storage::rollback() 方法來復(fù)原。通過指定 storage::open() 方法的 storage::use_transaction_log 屬性來選擇文件訪問事務(wù)所基于的模式.

另外一個提供數(shù)據(jù)一致性的手段基于寫拷貝機(jī)制。在這種情況下源文件沒有受到影響。任何試圖對文件鏡像頁面的改變,導(dǎo)致產(chǎn)生一個該頁面的拷貝,它從系統(tǒng)交換區(qū)種分配并具有讀寫許可。文件直到顯式調(diào)用 storage::flush() 方法時才更新。這個方法寫數(shù)據(jù)到臨時文件(帶后綴“.tmp”)然后重命名為原來的。因此這個操作形成文件的一個原子更新(當(dāng)然假設(shè)在操作系統(tǒng)能保證 rename() 操作的原子數(shù))。

注重:假如你沒有使用事務(wù)處理,storage::close() 方法不會刷數(shù)據(jù)到文件中。所以假如你在此前調(diào)用 storage::flush() 方法所有的自上次 flush 之后的改變將會丟失。

Windows 95 細(xì)節(jié):在 Windows 95 中重命名到已有的文件是不行的,所以源文件首先被保存為帶后綴“.sav”的文件名。然后后綴為“.tmp”的臨時文件被重命名為原來的名字以及最后的舊的拷貝被刪除。所以假如錯誤發(fā)生在 flush() 操作中并且之后你找不到存儲文件,請不要驚慌,只需找到以后綴“.sav”結(jié)束的文件并且重命名為原來的就可以了。

提示:假如你計劃在程序執(zhí)行期間保存數(shù)據(jù)我強(qiáng)烈建議你使用事務(wù)處理。也可以采用寫拷貝的途徑但是這樣需要多得多的消耗。同樣假如存儲非常大事務(wù)處理也通常更好,因?yàn)樯膳R時的文件拷貝需要很多的磁盤空面和時間。

這里有幾個屬性供存儲器 open() 方法使用:
support_virtual_functions 假如存儲器中的對象帶有虛函數(shù)則必須設(shè)置這個屬性。假如沒有設(shè)置這個屬性,POST++ 假定所有的永久對象在存儲中只包含有引用(對存儲器中其他對象的)。所以只有在數(shù)據(jù)文件映像的基地址發(fā)生改變時才需要調(diào)整引用(這個地址被存放在數(shù)據(jù)文件的第一個字中并且 POST++ 通常試圖映像文件到相同的地址上來避免不必要的引用調(diào)整)。但是假如對象類包含虛函數(shù),指向虛表的指針被放在對象內(nèi)。假如你重新編譯你的應(yīng)用,這個標(biāo)的地址可能改變。POST++ 庫比較執(zhí)行對象的時間戳和這個應(yīng)用產(chǎn)生的數(shù)據(jù)庫的時間戳進(jìn)行比較。假如這個時間戳不等的話,則會校正虛表的指針。為了得到應(yīng)用時間戳 POST++ 必須可以定位執(zhí)行文件對象。不幸的是沒有找到執(zhí)行文件名的簡便的方法。在 Unix 下POST++ 看命令行解釋器設(shè)置的環(huán)境變量“_”的值。但假如進(jìn)程不是從命令行執(zhí)行的(比如通過system())或者工作目錄被 chdir() 改變這個方法將不起作用。最簡單的方法是使用文件comptime.cxx,它必須在每次重編譯你的應(yīng)用時被編譯并和存儲庫一同被鏈接。在 Windows 中沒有這個問題,執(zhí)行映像的名稱可以通過 Win32 API 得到。在存儲器打開時 POST++ 比較這個時間戳和數(shù)據(jù)文件的時間輟,假如他們不等并且指定了 support_virtual_functions 屬性那么校正所有對象(通過調(diào)用缺省構(gòu)造函數(shù))。 read_only 通過設(shè)置這個屬性程序員說明他只需要數(shù)據(jù)文件讀權(quán)限。POST++ 將創(chuàng)建數(shù)據(jù)文件的只讀視圖并且任何改變存儲器中的對象或者分配新對象的嘗試將會導(dǎo)致保護(hù)違例錯。這里有一個例外:假如不能夠映像數(shù)據(jù)文件到相同的地址或者應(yīng)用程序發(fā)生改變時并且指定了 support_virtual_functions ,那么對此區(qū)域的保護(hù)被臨時改變?yōu)閷懣截惒⑶已b載的對象被轉(zhuǎn)換。 use_transaction_log 設(shè)置這個屬性強(qiáng)制對所有數(shù)據(jù)文件更新使用事務(wù)。影子頁面被用來執(zhí)行事務(wù)。事務(wù)在第一次修改存儲后被打開。通過 storage::commit() 或者 storage::rollback() 操作顯式的關(guān)閉。方法 storage::commit() 保存所有的改變頁面到磁盤上并且截斷事務(wù)記錄,方法 storage::rollback() 忽略此次事務(wù)中的所有改變。 no_file_mapping 缺省情況下 POST++ 將映像數(shù)據(jù)文件到進(jìn)程虛擬內(nèi)存中。這種情況下打開數(shù)據(jù)庫的時間將大大減少,因?yàn)槲募撁鎸⒃谛枰獣r調(diào)入。但是假如數(shù)據(jù)庫大小不是非凡大或者數(shù)據(jù)庫中所有數(shù)據(jù)需要立即訪問,那么把文件讀入內(nèi)存優(yōu)于使用虛擬內(nèi)存映像因?yàn)檫@種情況下沒有額外的頁面溢出錯誤。標(biāo)志 no_file_mapping 阻止 POST++ 映像文件并根據(jù)分配的內(nèi)存段讀文件。 fault_tolerant 這個標(biāo)志被應(yīng)用程序用于在系統(tǒng)或應(yīng)用出錯情況下想保護(hù)數(shù)據(jù)庫的一致性。假如使用了事務(wù) use_transaction_log 這個標(biāo)志不必指定,因?yàn)橐恢滦钥梢杂墒聞?wù)機(jī)制來提供。
假如沒有指定 use_transaction_log 標(biāo)志并且設(shè)置了 fault_tolerant 標(biāo)志, POST++ 將不改變源文件而保持它的一致性。這依靠讀文件到內(nèi)存中(假如沒有設(shè)置 no_file_mapping 標(biāo)志)或者使用寫拷貝頁面保護(hù)。在后一種情況下試圖改變映像到文件的頁面將導(dǎo)致在系統(tǒng)交換文件中生成頁面拷貝。flush() 方法將保存內(nèi)存內(nèi)數(shù)據(jù)庫的映像到臨時文件中然后使用原子操作重命名到源文件。假如沒有指定 fault_tolerant 標(biāo)志,POST++ 在數(shù)據(jù)庫頁面上原有位置進(jìn)行修改,提供最大的應(yīng)用性能(因?yàn)闆]有拷貝修改頁面和保存數(shù)據(jù)庫映像到臨時文件的額外開銷)。在修改頁面沒有馬上刷新到磁盤的條件下,部分改變可能因?yàn)橄到y(tǒng)錯誤而丟失(最壞的事是部分修改的頁面保存了而另外一些沒有保存 - 這樣數(shù)據(jù)庫的一致性可能被攪亂了)。 do_garbage_collection 當(dāng)設(shè)置了這個屬性時 POST++ 將在打開儲存器時執(zhí)行垃圾收集。垃圾收集操作和指針對齊聯(lián)系在一起。使用垃圾收集往往比手工內(nèi)存釋放來的安全(考慮到掛起的引用問題),但是顯式內(nèi)存釋放開銷較少。POST++ 中的垃圾收集相比顯式內(nèi)存分配有一個更大的優(yōu)勢:內(nèi)存收集器對小對象使用的頁面進(jìn)行優(yōu)化。假如頁中沒有 已分配的小對象那么垃圾收集器將在空閑頁中包含這一頁。這不會在顯式釋放時完成,因?yàn)樾ο蟮目臻e單元被串成鏈不能簡單從這個鏈中移開(在垃圾收集器中所有的鏈被重新構(gòu)造)。即使你使用顯式內(nèi)存釋放,我仍建議你每隔一定時間做垃圾收集來檢查引用的一致性和沒有內(nèi)存泄漏(garbage_collection 方法返回釋放對象的數(shù)目,假如你確信你已經(jīng)釋放了所有的不能到達(dá)的對象,那么這個值將會是0)。考慮到垃圾收集器修改存儲中所有的對象(設(shè)置掩碼位),重連鏈中 空閑對象),在事務(wù)模式下運(yùn)行GC可能是消耗時間和磁盤空間的操作,因?yàn)樗形募械捻搶⒈豢截惖绞聞?wù)記錄文件中)。
你可以通過 file::max_file_size 變量指定存儲文件的最大尺寸。假如數(shù)據(jù)文件的大小比 file::max_file_size 并且模式不是 read_only,那么虛擬空間 size_of_file - file::max_file_size 以外的字節(jié)將被保留在文件映像空間的后面。當(dāng)存儲大小擴(kuò)展時(因?yàn)榉峙湫聦ο螅@些頁面將被提交(在 Windows NT)并被使用。假如文件大小大于 file::max_file_size 或者使用了 read_only 模式,那么映像區(qū)域的大小和文件大小一致。在后一種情況下不可能進(jìn)行存儲擴(kuò)展。在 Windows 中我使用 GlobalMemoryStatus() 方法來得到關(guān)于系統(tǒng)真實(shí)可分配的虛擬內(nèi)存的信息并減少 file::max_file_size 為該值。不幸我發(fā)現(xiàn)在 Unix 中沒有輕便的調(diào)用可用來達(dá)到相同的目的(getrlimit 不返回用戶進(jìn)程可使用的虛擬內(nèi)存的確切信息)。

對象存儲的接口在文件 storage.h 定義并且實(shí)現(xiàn)部分可在 storage.cxx 中看到。依靠于操作系統(tǒng)的映像內(nèi)存文件的部分被封裝在 file 類中,其定義在 file.h 實(shí)現(xiàn)在 file.cxx.

 
POST++ 的安裝
POST++ 的安裝十分簡單。目前在以下系統(tǒng)已經(jīng)過測試:Digital Unix, linux, Solaris, Windows NT 4.0, Windows 95. 我希望對于大部分所有其他新 Unix 方言(AIX, HP-UX 10, SCO...)也沒有問題。不幸的是我沒有時用過這些系統(tǒng)。在 Windows 下我使用 Microsoft Visual C++ 5.0 和 Borland 5.02 compilers 編譯。Visual C++ 的 Makefiel 是 makefile,Broland C++ 的 Makefile 是 makefile。

為使用 POST++ 唯一你需要的東西就是函數(shù)庫(在 Unix 下是 libstorage.a 在 Windows 下是and storage.lib)。這個庫可以通過運(yùn)行 make 命令生成。有個非凡的 MAKE.BAT 用于 Microsoft Visual C++,它使用 makefile.mvc 作為輸入調(diào)用 NMAKE (假如你正在使用 Borland 請編輯這個文件或者通過 make.exe -f makefile.bcc 命令調(diào)用)。

在 Unix 下的安裝可以通過拷貝 POST++ 庫和同文件到一些標(biāo)準(zhǔn)系統(tǒng)目錄下來完成。你必須為 makefile 里 INSTALL_LIB_PATH 和 INSTALL_INC_PATH 變量設(shè)置恰當(dāng)?shù)闹挡⑦\(yùn)行 make install 命令。INSTALL_LIB_PATH 的缺省值為 /usr/local/lib  INSTALL_INC_PATH 的是 /usr/local/include。你可以通過明確指定路徑到 POST++ 目錄來編譯和鏈接而避免拷貝文件到系統(tǒng)目錄中。

 
POST++ 類庫
POST++ 包括一些持久類的定義,她們可以用于你的應(yīng)用也可以作為開發(fā) POST++ 類的好例子。你可以看見那些類的實(shí)現(xiàn)中甚至沒有 POST 特定的代碼。這些類包括 array(數(shù)組), matrix(矩陣), string(字符串), L2-list(L2-列表), hash table(哈希表), AVL-tree(AVL-樹), R-tree(R-數(shù)), text object(文本對象). R-tree 提供了提供空間對象(包含空間對等物的對象)的快速訪問。文本對象包含了 Boyer and Moore 算法的修正,擴(kuò)展到由 OR/AND 關(guān)系組合的多樣式搜索。這些類的定義可以在以下文件中發(fā)現(xiàn):

 
描述接口實(shí)現(xiàn)Arrays of scalars and references, matrixes and stringsarray.harray.cxxL2-list and AVL-treeavltree.havltree.cxxHash table with collision chainshashtab.hhashab.cxxR-tree with quadratic method of nodes splittingrtree.hrtree.cxxT-tree (combination of AVL tree and array)ttree.httree.cxxText object with modified Boyer and Moore search algorithmtextobj.htextobj.cxx
在論文 "A study of index structures for main memory database management systems" 中 T.J. Lehman and M.J Carey 提議使用 T-trees 作為主要內(nèi)存數(shù)據(jù)庫的一個存儲的有效的數(shù)據(jù)結(jié)構(gòu)。
T-trees 基于 AVL 樹由 Adelson-Velsky and Landis 提議。在這個分段中,我們提供 T-trees 一個概覽作為在 POST++ 中的實(shí)現(xiàn)。

像 AVL 樹一樣,T-tree的左子樹和右子樹高度差不大于1。和 AVL 樹不一樣,T-tree的每一個節(jié)點(diǎn)排列次序保存多個要害值而不是單一要害值。一個節(jié)點(diǎn)里最左邊和最右邊要害值定義包含在這個節(jié)點(diǎn)內(nèi)要害值的范圍。這樣,一個節(jié)點(diǎn)左子樹只包含比最左要害值小的要害值,而右子樹包含比該節(jié)點(diǎn)最右要害值大的要害值。節(jié)點(diǎn)內(nèi)落在最小和最大要害值之間的要害值被稱作被該節(jié)點(diǎn)約束( bounded )。注重等于最大要害字或最小要害字的要害字可以根據(jù)索引是否全局唯一和查找條件認(rèn)為是被約束或不被約束(e.g. “大于”("greater-than") 相對于 “大于等于”("greater-than or equal-to"))。

一個既有左子樹也有右子樹的節(jié)點(diǎn)被歸為內(nèi)部節(jié)點(diǎn)(internal node),僅有一個子樹的節(jié)點(diǎn)被歸為不完全葉(semi-leaf),一個沒有子樹的節(jié)點(diǎn)被歸為葉(leaf)。為了保持高占用率,每個內(nèi)部節(jié)點(diǎn)具有一個必須包含的要害值的最小數(shù)目(一般為 k-2,假如 k 是可以排列在一個節(jié)點(diǎn)里的要害字的最大數(shù)目)。然而,對于葉子和不完全葉來說沒有占用率條件。

在T-tree中查找相當(dāng)于直接查找。對于每一個節(jié)點(diǎn),檢查看一下要害值是否在該節(jié)點(diǎn)最左和最右要害值之間;假如是這種情況,就是說節(jié)點(diǎn)里包這個要害值的話就返回該值(否則要害值不包含在樹中)。否則,假如要害值比最左要害值小,那么查找左子樹,反之搜索右子樹。重復(fù)這個過程直到發(fā)現(xiàn)這個要害值或者查找到null節(jié)點(diǎn)。

在T-tree中插入和刪除稍微復(fù)雜一些。對于插入操作,首先使用上面的搜索過程來查找約束插入要害值的節(jié)點(diǎn)。假如存在這樣的節(jié)點(diǎn),那么節(jié)點(diǎn)中有空余的話要害值被插入到這個節(jié)點(diǎn)中。假如節(jié)點(diǎn)中沒有空余,那么要害值被插入到節(jié)點(diǎn)中,該節(jié)點(diǎn)的最左要害值被插入到該節(jié)點(diǎn)的左子樹中(假如左子樹為空,就分配一個新的節(jié)點(diǎn)把最左要害值插入)。假如沒有發(fā)現(xiàn)約束節(jié)點(diǎn),那我們用N來表示搜索失敗的最后碰到的節(jié)點(diǎn)并按下述步驟繼續(xù):假如N有空余,那么要害值就插入到N中;否則,要害值將被插入到一個新節(jié)點(diǎn)并根據(jù)要害值以及最左要害值和最右要害值作為N的右或左子節(jié)點(diǎn)/樹。

刪除要害值從判定包含要害值的節(jié)點(diǎn)開始,并從節(jié)點(diǎn)中刪除要害值。假如刪除要害值后剩下一個空的葉結(jié)點(diǎn),那么節(jié)點(diǎn)也被刪除。假如刪除后剩下一個內(nèi)部節(jié)點(diǎn)或者不完全葉其中包含比最小數(shù)量少的要害值,那么不足的將通過移動左子樹中最大的要害值到節(jié)點(diǎn)中或者合并節(jié)點(diǎn)和右子樹來補(bǔ)充。

無論插入還是刪除,分配/釋放節(jié)點(diǎn)可能導(dǎo)致樹不平衡和進(jìn)行旋轉(zhuǎn)操作(RR, RL, LL, LR)。(下面的描述中子樹的高度包括了插入和刪除的影響。)在插入情況下,檢查沿著新分配節(jié)點(diǎn)開始的節(jié)點(diǎn)到根節(jié)點(diǎn)的節(jié)點(diǎn)直到
發(fā)現(xiàn)一個節(jié)點(diǎn)有兩個等高的子樹(在這種情況下不需要旋轉(zhuǎn)了),或者 發(fā)現(xiàn)一個節(jié)點(diǎn)的左子樹和右子樹有大于1的不同高度并執(zhí)行包含節(jié)點(diǎn)的單一旋轉(zhuǎn)。
在刪除情況下,檢查沿著釋放節(jié)點(diǎn)的父母開始到根節(jié)點(diǎn)的節(jié)點(diǎn)直到發(fā)現(xiàn)一個節(jié)點(diǎn)它的子樹高度相差1。而且每一次碰到一個節(jié)點(diǎn)的子樹的高度相差大于1時,進(jìn)行一次旋轉(zhuǎn)。注重釋放一個節(jié)點(diǎn)可能導(dǎo)致多次旋轉(zhuǎn)。

有幾個測試程序來測試 POST++ 持久類庫中的類,他們被包含在缺省的make目標(biāo)中:

 
ProgramTested classestesttree.cxxAVL-tree, l2-node, hash tabletesttext.cxxtext, stringtestspat.cxxrectangle, R-tree, hash tabletestperf.cxxT-tree insert, find and remove operations
 
和POST++一起使用STL類
從 POST++ 存儲器中保存和取得STL類是可能的。POST++ 提供了非凡的STL分配子并重載了new/delete操作符來保持STL對象。有幾個使得STL持久的模型,通過以下幾個宏來控制:
USE_MICROSOFT_STL 使用 Microsoft STL 類,隨 Microsoft Visual C++ 一起附帶。這個類庫和C++ STL 標(biāo)準(zhǔn)不完全兼容。 USE_STD_ALLOCATORS 使用和C++標(biāo)準(zhǔn)中一樣的分配子。分配子對象包含在STL對象中,分配子對象中的實(shí)例方法用于分配/釋放空間。所以有可能執(zhí)行一個“聰明的”分配子:僅在對象包含這個分配子的情況下分配子才為 POST++ 存儲器中的對象分配空間而且這個分配子也放在存儲器中。否則,對象所占空間將通過標(biāo)準(zhǔn) malloc() 函數(shù)這個正常途徑來分配。這個選項(xiàng)可以和 SGI STL 庫以及 Microsoft STL 庫一起使用。注重順從標(biāo)準(zhǔn)的 SGI STL 分配子使用了許多沒有廣泛實(shí)現(xiàn)的語言特性。非凡是,他們依靠于成員模板,局部非凡化,局部分類(排序)的方法模板,typename 要害字,以及使用 template 要害字來引用依靠類型的模板成員。所以在指定這個宏定義時只有少數(shù) C++ 編譯器可以編譯 SGI STL 庫。假如沒有設(shè)置這個宏,那么 POST 提供一個含有靜態(tài)成員函數(shù)的分配子并且所有對象都從 POST++ 存儲器中分配。使用這個分配子的應(yīng)用一次只能打開一個POST++ 存儲器。 REDEFINE_DEFAULT_ALLOCATOR 有兩個途徑使得 STL 對象持久,一個途徑是引入新類型:
typedef basic_string<char, char_traits<char>, post_alloc<char> > post_string;

另外一個途徑是使所有類具有持久能力。當(dāng)定義了 REDEFINE_DEFAULT_ALLOCATOR 宏,任何 STL 類可以在 POST++ 存儲器中分配。為了在持久存儲器中創(chuàng)建新對象,你必須指定存儲器作為 new 操作符額外的參數(shù)。假如存儲器被忽略,那么對象將使用標(biāo)準(zhǔn) malloc() 函數(shù)。

POST++ 到 STL 的接口不需要任何 STL 類的改變,所以你可以使用你想執(zhí)行的任何 STL。但是作為一個結(jié)果,STL類不包含類型描述所以 POST++ 沒有STL 對象的格式信息。所以 POST++ 不能夠進(jìn)行垃圾收集和引用對齊。當(dāng)使用了 STL 接口時,POST++ 存儲器必須始終鏡像到相同的虛擬地址上。假如你傳遞 storage::fixed 標(biāo)志到 storage::open(int flags) 方法,那么假如不能鏡像存儲器到相同的內(nèi)存地址 POST++ 將報告錯誤并且返回 false。假如你的應(yīng)用只使用了一個存儲器并且不鏡像其他對象到虛擬內(nèi)存中,那么幾乎所有的操作下都可能鏡像存儲器到相同的內(nèi)存地址。


POST++ 到 STL 庫的接口定義在在頭文件 post_stl.h 中。這個文件必須包含在任何 STL 包含文件中。同樣宏 REDEFINE_DEFAULT_ALLOCATOR,USE_STD_ALLOCATORS 以及 USE_MICROSOFT_STL 被在 post_stl.h 之前定義。

POST++ 包含使用 STL++ 類的例子 stltest.cxx。這個例子使用兩個 STL 類 - string 和 vector。從標(biāo)準(zhǔn)輸入讀進(jìn)來的行被壓入到 vector 中并且程序被再次啟動所有的vector的元素被打印到標(biāo)準(zhǔn)輸出上。這個例子被包含在為 Microsoft Visual C++ 配置的 makefile 的缺省目標(biāo)中(其使用VC 中附帶的 Microsoft STL 類)。你也可以嘗試用其他版本的 STL 庫來構(gòu)建這個測試但是不要忘記關(guān)于 REDEFINE_DEFAULT_ALLOCATOR 和 USE_STD_ALLOCATORS 宏。這個例子同樣可以和 SGI STLport 3.12 以及 GCC 2.8.1 一起測試。

 
替換標(biāo)準(zhǔn)分配子
在前面一節(jié)中我解釋了如何和 STL 庫一起使用 POST++。但是仍然有很多其他你想用的C++和C庫以及應(yīng)用,而且沒有提供象 STL 這種易通融的分配機(jī)制。在這種情況下唯一可能的解決方案(假如你不想改變此庫的任何源代碼的話)就是用一個 POST++ 提供的來替換標(biāo)準(zhǔn)的分配機(jī)制。這樣任何動態(tài)分配對象都從 POST++ 存儲器中分配(除了一個不能在這種情況下使用的存儲器)。

POST++ 發(fā)行包中包含的文件 postnew.cxx 重定義了標(biāo)準(zhǔn)的 malloc,free,realloc 和 calloc 函數(shù)。當(dāng)打開存儲器時,所有的對象被在存儲器中分配。否則 sbrk() 函數(shù)被用來分配對象(為這些對象分配的空間沒有回收)。可能不需要接觸這些標(biāo)準(zhǔn)C分配函數(shù)而僅需重載缺省的C++操作符 new 和 delete。當(dāng)編譯 postnew.cxx 時定義 DO_NOT_REDEFINE_MALLOC 宏來這么做。從 postnew.cxx 生成的目標(biāo)文件必須在標(biāo)準(zhǔn)C庫前傳遞給鏈接程序。

作為一個 POST++ 這樣使用的例子可以參見 testnew.cxx 和 testqt.cxx。第一個舉例說明了標(biāo)準(zhǔn)C++數(shù)組如何持久化。第二個舉例說明了POST++如何和Qt類庫一起工作。

就 POST++ 沒有關(guān)于存儲類的格式信息這里有一些限制在 POST++ 的使用上:

 
包含虛函數(shù)的類不被支持(POST++ 不能正確的初始化到虛函數(shù)表的指針)。 隱式內(nèi)存釋放(垃圾收集器)是不可能的 - POST++ 沒有關(guān)于對象內(nèi)部指針位置的信息。 存儲器必須總映射到相同的虛擬地址上因?yàn)榧偃缁刂犯淖兞?POST++ 不能調(diào)整指針。
假如所有這些限制不是你的應(yīng)用所必需的,你可以使其持久化而不需要任何代碼的改動。這個方法在C和C++程序中都可以使用。

 
如何使用 POST++
這里有幾個 POST++ 類和應(yīng)用的例子。其中最簡單的就是游戲“猜動物”。這個游戲的算法非常簡單并且結(jié)果看起來給人以深刻的印象(有些象人工智能)。此外這個游戲是一個非常好的例子,闡明了持久對象存儲的好處。這個游戲的源代碼在文件 guess.cxx 中。創(chuàng)建這個游戲包含在缺省的make目標(biāo)中。執(zhí)行g(shù)uess來運(yùn)行它。

Unix specific: 當(dāng)你預(yù)備和 POST++ 庫鏈接你的Unix應(yīng)用并且持久對象中波阿含虛函數(shù),請不要忘記重編譯 comptime.cxx 文件并包含在鏈接列表中。這個文件是必須的用于 POST++ 提供可執(zhí)行文件的時間戳,被放在存儲器中用來判定什么時候應(yīng)用被改變并在需要的時候重新初始化對象內(nèi)的虛函數(shù)表。Attention! 這個文件必須在你每次重新鏈接你的應(yīng)用時被重新編譯。我建議你讓編譯器為你調(diào)用鏈接程序并包含 comptime.cxx 源文件在為運(yùn)行映像目標(biāo)文件提供的對象文件列表中(see makefile)。

 
調(diào)試 POST++ 應(yīng)用的細(xì)節(jié)
這一節(jié)的內(nèi)容對使用了事務(wù)的應(yīng)用是非常有意義的。POST++ 使用頁面保護(hù)機(jī)制來提供當(dāng)源頁面修改時生成影子頁面,當(dāng)存儲器打開或事務(wù)提交時所有文件頁面的映像是只讀保護(hù)的。所以任何試圖修改分配在這些頁面里對象的內(nèi)容將導(dǎo)致一個訪問違例異常。這個異常被指定的 POST++ 句柄處理。但是假如你使用調(diào)試器,它將首先捕捉這個異常并停止應(yīng)用程序。假如你想調(diào)試你的應(yīng)用你必須作一些預(yù)備:
在 Unix 可以充分的告訴調(diào)試器不要捕捉 SIGSEGV 信號。比如對于 GDB 它可以通過命令來完成:handle SIGSEGV nostop noprint pass。假如 SIGSEGV 信號不是由存儲頁面保護(hù)違例產(chǎn)生,但是是程序中的一個錯誤,POST++ 異常處理程序?qū)ⅰ袄斫狻彼皇亲约旱漠惓2⑺统鲆粋€ SIGABRT 信號到己進(jìn)程中,這可以被調(diào)試器捕捉。 在 Windows POST++ 使用不處理異常過濾器來( Unhandled Exception Filter )處理存儲器頁面保護(hù)違例。不幸的是不可能讓 Microsoft Debugger 忽略不處理異常。假如你預(yù)備調(diào)試你的應(yīng)用,你必須把所有你的程序代碼(main 或者 WinMain 函數(shù))封裝為結(jié)構(gòu)化的異常阻塞。你必須在 Borland C++ 中總使用結(jié)構(gòu)化異常處理,因?yàn)?Unhandled Exception Filter 沒有在Borland中被正確調(diào)用。請使用兩個宏 SEN_TRY 和 SEN_access_VIOLATION_HANDLER() 來封裝 main(或 WinMain)的函數(shù)體:
main() { SEN_TRY { ... } SEN_ACCESS_VIOLATION_HANDLER(); return 0; }

請確定調(diào)試器對此異常的行為是“假如沒有處理就停止”而不是“總是停止”(你可以在 Debug/Exceptions 菜單中檢查它)。在文件 testrans.cxx 中你可以發(fā)現(xiàn)使用結(jié)構(gòu)化異常處理的例子。

關(guān)于 POST++ 的更多的一些信息
POST++ 是 freeware。開發(fā)出她希望是有用的。通過她你可以做任何你想做的(在開發(fā)產(chǎn)品中使用 POST++ 沒有任何限制)。我將很興奮來幫助你使用 POST++ 和得到關(guān)于 POST++ 任何類型的信息(錯誤報告,建議...)。POST++ 的免費(fèi)軟件情形并不意味著缺少支持。我保證將努力修正任何報告的錯誤。也提供 e-mail 支持。POST++ 有幾種用途:在不同期間保存信息,在文件中保存對象系統(tǒng),快照, 信息系統(tǒng)... 但是假如你感到你需要在你的應(yīng)用使用更重要的面向?qū)ο髷?shù)據(jù)庫以提供并發(fā),分布式以及事務(wù)處理,請訪問 GOODS (Generic Object Oriented Database System) home page。

Look for new version at my homepage E-mail me about bugs and problems

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 视频| 当雄县| 九台市| 望城县| 锦州市| 芦溪县| 吴忠市| 日土县| 津市市| 新疆| 台中市| 柘城县| 宜君县| 舟山市| 肃北| 关岭| 洛扎县| 桃园县| 讷河市| 昭平县| 屏山县| 綦江县| 博罗县| 平阳县| 海淀区| 西充县| 湘乡市| 太保市| 建湖县| 绥德县| 湖口县| 江孜县| 共和县| 东台市| 全椒县| 遂平县| 周宁县| 新巴尔虎右旗| 西峡县| 河东区| 即墨市|