將NS2改為plugin方式的嘗試
來源:互聯網
上載者:User
1. 問題的產生NS2隻使用了一個主程式,所有的擴充都要編譯到主程式中去。這樣就造成了一個問題,每次擴充都要把整個程式編譯一道,速度慢而且麻煩。更有甚者,象BlueHoc這樣的擴充甚至於還要修改NS2的核心檔案,就更容易出錯。2. 解決的想法將ns2改為plugin的方式,將它的核心放在一個單獨的庫裡,將所有的擴充也做成一個個單獨的庫放在plugin目錄下,NS2主程式啟動時動態載入這些庫。3. 實驗仍舊以BlueHoc為例進行實驗。(1)將ns2分離為一個主程式和一個單獨的庫,這點通過vs2005的IDE很容易就完成了。主程式只使用了ns_tcl.cc,ptypes.cc,tclAppInit.cc,win32.c這幾個檔案,其它的全部放在DLL中。(2)在主程式中添加動態調用plugin的DLL庫的代碼,主要將擴充分為3類,分別為Agent,Application和Package,其中Agent目錄下存放從Agent類繼承而來的子類,Application目錄下存放從Application類繼承而來的子類,Package目錄下存放完整的擴充包,如BlucHoc等。將以下代碼放在Tcl_AppInit函數的末尾: // 動態讀取plugin WIN32_FIND_DATA FindFileData; HANDLE hFind; char path[MAX_PATH], find_path[MAX_PATH]; HMODULE hModule = GetModuleHandle(NULL); const char *p; GetModuleFileName(hModule, path, MAX_PATH); p = strrchr(path, '//'); path[p-path+1] = 0; const char* plugins[] = { "plugin//Agent//", "plugin//Application//", "plugin//Package//", }; for(int i = 0; i < 3; i++) { strcpy(find_path, path); strcat(find_path, plugins[i]); strcat(find_path, "*.dll"); hFind = FindFirstFile(find_path, &FindFileData); do { strcpy(find_path, path); strcat(find_path, plugins[i]); strcat(find_path, FindFileData.cFileName); LoadLibrary(find_path); printf("%s loaded/n", find_path); } while(FindNextFile(hFind, &FindFileData)); FindClose(hFind); }(3)將BlueHoc放在一個單獨的DLL中。(4)由於BlueHoc改寫了DequeTrace::recv中的函數,而我們不希望改寫ns2中的代碼,因此選擇重載Trace/Deque這個類,重載的方法參見前一篇文章《重載TCLCL中的類》。在此過程中發現了一個問題。當調用Address::print_nodeaddr、Address::print_portaddr等函數時,它們均使用new分配了一塊記憶體,並返回此記憶體的指標,這就要求在函數調用完成後,調用者必須釋放這塊記憶體。問題在於現在分配記憶體和釋放記憶體是在兩個不同的模組間完成的,因此造成了釋放記憶體時出錯。最後採用解決的辦法是將NS2中的記憶體配置改用ckalloc完成,最後再用ckfree釋放。(還有一種解決辦法是每個庫都使用相同的“執行階段程式庫DLL”,不過因為vs2005要求調用這些庫要使用manifest,所以沒有採用這種方法)。在解決此問題的過程中,才發現NS2中居然有很多個地方沒有釋放這塊記憶體,這樣不可避免地造成記憶體流失,當然,此記憶體很小,一般也就幾個位元組,無關痛癢就是了。(5)在NS2啟動時還要執行一些初始化的TCL代碼,這就容易了,直接放在DllMain中就OK了。4. 驗證使用修改前的ns2與修改後的程式運行source sim007.tcl,比較out.nam和out.tr,一切OK。終於大功告成!! 在此過程中,還順帶修改了BlueHoc的一個Bug: 開啟bt-classify.h,將int classify(Packet *const p);改為int classify(Packet *p);將相應的.cc檔案中的函式宣告也改過來。