windows2000提供了如下幾種線程池函數(shù)用于線程管理:
一、異步調(diào)用函數(shù):
BOOL QueueUserWorkItem(
PTHREAD_START_ROUTINE pfnCallback,
PVOID pvContext,
ULONG dwFlags);
該函數(shù)將“工作項(xiàng)目”放入線程池并且立即返回。工作項(xiàng)目是指一個(gè)用pfnCallback參數(shù)標(biāo)識(shí)的函數(shù)。它被調(diào)用并且傳遞單個(gè)參數(shù)pvContext.工作項(xiàng)目函數(shù)原型如下:
DWORD WINAPI WorkItemFunc(PVOID pvContext);
dwFlags參數(shù):WT_EXECUTEDEFAULT? 工作項(xiàng)目放入非I/O組件得線程中
???????????? WT_EXECUTEINIOTHREAD 工作項(xiàng)目放入I/O組件的線程中,這樣的線程在I/O請(qǐng)求沒(méi)有完成之前不會(huì)被終止運(yùn)行????????????????????????????????? ,防止因?yàn)榫€程被終止導(dǎo)致I/O請(qǐng)求丟失。
???????????? WT_EXECUTEINPERSISTENTTHREAD 放入永久線程池,
???????????? WT_EXECUTELONGFUNCTION? 工作項(xiàng)目需要長(zhǎng)時(shí)間的工作,系統(tǒng)會(huì)據(jù)此安排更多的線程。
線程池不能設(shè)置線程個(gè)數(shù)的上限,否則排隊(duì)個(gè)數(shù)超過(guò)線程個(gè)數(shù)上限的時(shí)候,會(huì)導(dǎo)致所有的線程都被中斷。
工作項(xiàng)目函數(shù)如果訪問(wèn)了已經(jīng)被卸載的DLL,會(huì)產(chǎn)生違規(guī)訪問(wèn)。
二、按規(guī)定的時(shí)間間隔調(diào)用函數(shù)
創(chuàng)建定時(shí)器隊(duì)列:
HANDLE CreateTimerQueue();
在隊(duì)列中創(chuàng)建定時(shí)器:
BOOL CreateTimerQueueTimer(
PHANDLE phNewTimer,
HANDLE hTimerQueue,
WAITORTIMERCALLBACK pfnCallback,
PVOID pvContext,
DWORD dwDueTime,
DWORD dwPeriod,
ULONG dwFlags);
工作回調(diào)函數(shù)原型如下:
VOID WINAPI WaitOrTimerCallback(
PVOID pvContext,
BOOL fTimerOrWaitFired);
dwFlags比前面的多了一個(gè)標(biāo)志:WT_EXECUTEINTIMERTHREAD,表示由組件的定時(shí)器線程(定時(shí)器組件只有一個(gè)線程)運(yùn)行這個(gè)
工作函數(shù),此時(shí)的工作函數(shù)必須是很快返回的,否則定時(shí)器組件將無(wú)法處理其他的請(qǐng)求。
刪除定時(shí)器:
BOOL DeleteTimerQueueTimer(
HANDLE hTimerQueue,
HANDLE hTimer,
HANDLE hCompletionEvent);
在定時(shí)器線程中刪除定時(shí)器會(huì)造成死鎖。設(shè)定hCompletionEvent為INVALID_HANDLE_VALUE,那么在定時(shí)器的所有排隊(duì)工作項(xiàng)目沒(méi)有完成之前,DeleteTimerQueueTimer不會(huì)返回,也就是說(shuō)在工作項(xiàng)目中對(duì)定時(shí)器進(jìn)行中斷刪除會(huì)死鎖。可以給hCompletionEvent傳遞事件句柄,函數(shù)會(huì)立即返回,在排隊(duì)工作完成之后,會(huì)設(shè)置該事件。
重新設(shè)定定時(shí)器://不能修改已經(jīng)觸發(fā)的單步定時(shí)器。
BOOL ChangeTimerQueueTimer(
HANDLE hTimerQueue,
HANDLE hTimer,
ULONG dwDueTime,
ULONG dwPeriod;
刪除定時(shí)器隊(duì)列:
BOOL DeleteTimerQueueEx(
HANDLE hTimerQueue,
HANDLE hCompletionEvent);
三、當(dāng)單個(gè)內(nèi)核對(duì)象變?yōu)橐淹ㄖ獱顟B(tài)時(shí)調(diào)用函數(shù)
BOOL RegisterWaitForSIngleObject(
PHANDLE phNewWaitObject,
HANDLE hObject,
WAITORTIMERCALLBACK pfnCallback,
PVOID pvContext,
ULONG dwMilliseconds,
ULONG dwFlags);
pfnCallBack原型:
VOID WINAPI WaitOrTimerCallbadkFunc(
PVOID pvContext,
BOOLEAN fTimerorWaitFired);
如果等待超時(shí),fTimerorWaitFired==TRUE,如果是已通知狀態(tài),則為FALSE.
dwFlags可以傳遞參數(shù):WT_EXECUTEINWAITTHREAD,它讓等待組件得線程之一運(yùn)行工作項(xiàng)目函數(shù)。注意項(xiàng)同前。
如果等待的內(nèi)核對(duì)象是自動(dòng)重置的,那么會(huì)導(dǎo)致工作函數(shù)被反復(fù)調(diào)用,傳遞WT_EXECUTEONLYONCE會(huì)避免這種情況。
取消等待組件的注冊(cè)狀態(tài):
BOOL UnregisterWaitEx(
HANDLE hWaitHandle,
HANDLE hCompletionEvent);
四、當(dāng)異步I/O請(qǐng)求完成時(shí)調(diào)用函數(shù)
將設(shè)備和線程池的非I/O組件關(guān)聯(lián)
BOOL BindIoCompletionCallback(
HANDLE hDevice,
POVERLAPPED_COMPLETION_ROUTINE pfnCallback,
ULONG dwFlags//始終為0);
工作函數(shù)原型:
VOID WINAPI OverlappedCompletionRoutine(
DWORD dwErrorCode,
DWORD dwNumberOfBytesTransferred,
???????????????????????? ,
POVERLAPPED pOverlapped);
Windows的內(nèi)存結(jié)構(gòu)
從98,2000,到64位的windows,內(nèi)存管理方式都是不同的,32位的win2000用戶內(nèi)存是從0x10000到0x7fffffff(64kB-2G),2000 Advanced server可以達(dá)到(64kB-3G),其中最高64kB也是禁止進(jìn)入的。再往上則由系統(tǒng)使用。98則是從0x400000-0x7fffffff(4M-2G),2G-3G是系統(tǒng)用來(lái)存放32位共享數(shù)據(jù)的地方,如很多系統(tǒng)動(dòng)態(tài)連接庫(kù)。0-4M是為了兼容16位程序保留的。3G-4G由系統(tǒng)自身使用。98的內(nèi)核區(qū)是不受保護(hù)的,2000受保護(hù)。
對(duì)虛擬地址空間的分配稱作保留,使用虛擬內(nèi)存分配函數(shù)(VirtualAlloc),釋放使用VirtualFree(),目前,所有cpu平臺(tái)的分配粒度都是64kB,頁(yè)面大小則不同,x86是4kB,Alpha是8kB,系統(tǒng)在保留內(nèi)存的時(shí)候規(guī)定要從分配粒度邊界開(kāi)始,并且是頁(yè)面的整數(shù)倍,用戶使用VirtualAlloc都遵守這個(gè)規(guī)定,但系統(tǒng)不是,它是從頁(yè)面邊界開(kāi)始分配的。
將物理存儲(chǔ)器映射到保留的內(nèi)存區(qū)域的過(guò)程稱為提交物理存儲(chǔ)器,提交是以頁(yè)面為單位進(jìn)行的,也使用VirtualAlloc函數(shù)。
物理存儲(chǔ)器是由內(nèi)存和(硬盤(pán)上的)頁(yè)文件組成的,如果訪問(wèn)的數(shù)據(jù)是在頁(yè)文件中,則稱為頁(yè)面失效,cpu會(huì)把訪問(wèn)通知操作系統(tǒng),操作系統(tǒng)負(fù)責(zé)將數(shù)據(jù)調(diào)入內(nèi)存,并指導(dǎo)cpu再次運(yùn)行上次失效的指令。
當(dāng)啟動(dòng)一個(gè)程序的時(shí)候,系統(tǒng)并不是將整個(gè)文件讀入內(nèi)存或者頁(yè)文件,而是將這個(gè)文件直接映射到虛擬內(nèi)存空間,并將需要的數(shù)據(jù)讀入內(nèi)存,即將硬盤(pán)上的文件本身當(dāng)作頁(yè)文件(雖然不是)。當(dāng)硬盤(pán)上的一個(gè)程序的文件映像(這是個(gè)exe文件或者dll文件)用作地址空間的物理存儲(chǔ)器,它稱為內(nèi)存映射文件。當(dāng)一個(gè).exe或者dll文件被加載時(shí),系統(tǒng)將自動(dòng)保留一個(gè)地址空間的區(qū)域,并將該文件映射到該區(qū)域中。但系統(tǒng)也提供了一組函數(shù),用于將數(shù)據(jù)文件映射到一個(gè)地址空間的區(qū)域中。
物理存儲(chǔ)器的頁(yè)面具有不同的保護(hù)屬性:
PAGE_NOACESS
PAGE_READONLY
PAGE_READWRITE
PAGE_EXECUTE
PAGE_EXECUTE_READ
PAGE_EXECUTE_READWRITE
PAGE_WRITECOPY
PAGE_EXECUTE_WRITECOPY
后兩個(gè)屬性是配合共享頁(yè)面機(jī)制使用的。WINDOWS支持多個(gè)進(jìn)程共享單個(gè)內(nèi)存塊,比如運(yùn)行notepad的10個(gè)實(shí)例,可以讓他們共享應(yīng)用程序的代碼和數(shù)據(jù),這樣可以大大提高性能,但要求該內(nèi)存塊是不可寫(xiě)的。于是系統(tǒng)在調(diào)入.exe或者dll的時(shí)候,會(huì)計(jì)算那些頁(yè)面是可以寫(xiě)入的,為這些頁(yè)面分配虛擬內(nèi)存。然后同其他的頁(yè)面一起映射到一塊虛擬內(nèi)存,但賦PAGE_WRITECOPY或者PAGE_EXECUTE_WRITECOPY屬性(通常包含代碼的塊是PAGE_EXECUTE_READ,包含數(shù)據(jù)的塊是PAGE_READWRITE)。當(dāng)一個(gè)進(jìn)程試圖將數(shù)據(jù)寫(xiě)入共享內(nèi)存塊時(shí),系統(tǒng)會(huì)進(jìn)行如下操作:尋找預(yù)先分配的一個(gè)空閑頁(yè)面,將試圖修改的頁(yè)面拷貝到這個(gè)空閑頁(yè)面,賦予PAGE_READWRITE或者PAGE_EXECUTE_READWRITE屬性,然后更新進(jìn)程的頁(yè)面表,使得用戶可以對(duì)新的頁(yè)面進(jìn)行寫(xiě)入。
還有三個(gè)特殊的保護(hù)屬性:PAGE_NOCACHE PAGE_WRITECOMBINE PAGE_GUARD,前兩個(gè)用于驅(qū)動(dòng)程序開(kāi)發(fā),最后一個(gè)可以讓?xiě)?yīng)用程序在頁(yè)面被寫(xiě)入的時(shí)候獲得一個(gè)異常。
塊的意思是一組相鄰的頁(yè)面,它們具有相同的保護(hù)屬性,并且受相同類(lèi)型的物理存儲(chǔ)器支持。
賦予虛擬內(nèi)存頁(yè)面保護(hù)屬性的意義是為了提高效率,而且這個(gè)屬性總會(huì)被物理存儲(chǔ)器的保護(hù)屬性取代。
如果數(shù)據(jù)在內(nèi)存中沒(méi)有對(duì)齊,那么cpu要多次訪問(wèn)才能得到數(shù)據(jù),效率很低。
內(nèi)存管理函數(shù):
獲得系統(tǒng)信息:
VOID GetSystemInfo(LPSYSTEM_INFO psinf);//可以得到頁(yè)面大小,分配粒度,最大內(nèi)存地址,最小內(nèi)存地址。
獲得內(nèi)存狀態(tài):
VOID GlobalMemoryStatus(LPMEMORYSTATUS pmst);
獲得內(nèi)存地址的某些信息:
DWORD VirtualQuery(
LPVOID pvAddress,
PMEMORY_BASIC_INFORMATION pmbi,
DWORD dwLength);
DWORD VirtualQuery(
HANDLE hProcess,
LPVOID pvAddress,
PMEMORY_BASIC_INFORMATION pmbi,
DWORD dwLength);
內(nèi)存映射文件的優(yōu)點(diǎn):
1 節(jié)省頁(yè)面文件;
2 加快程序啟動(dòng);
3 在多個(gè)進(jìn)程間共享數(shù)據(jù)。
進(jìn)程的啟動(dòng)過(guò)程:
系統(tǒng)首先將.exe文件映射到地址空間,缺省基地址是0x400000,然后查詢.exe的輸入表,將其使用的所有.dll也映射到地址空間(基地址在每個(gè).dll文件中,如果不能滿足,需要重定位),然后將執(zhí)行.exe的啟動(dòng)代碼。此時(shí).exe文件還在硬盤(pán)上。每次代碼跳到一個(gè)尚未加載到內(nèi)存的指令地址,就會(huì)出現(xiàn)一個(gè)錯(cuò)誤,系統(tǒng)會(huì)發(fā)現(xiàn)這個(gè)錯(cuò)誤,并將代碼加再到內(nèi)存中。
如果再創(chuàng)建這個(gè).exe文件的一個(gè)實(shí)例。那么直接將原來(lái)的地址空間中的內(nèi)容映射到新的地址空間就可以了。這樣多個(gè)實(shí)例就可以共享相同的代碼和數(shù)據(jù)。如果某個(gè)實(shí)例要改變共享內(nèi)容,系統(tǒng)就為要更改的頁(yè)面申請(qǐng)一個(gè)新的頁(yè)面,將內(nèi)容拷貝一份,然后用新的頁(yè)面代替地址空間中原來(lái)頁(yè)面的映射就可以了。98同2000不同,它不待修改便立即為所有的實(shí)例分配新的頁(yè)面。
使用內(nèi)存映射文件:
1 創(chuàng)建或打開(kāi)一個(gè)文件內(nèi)核對(duì)象:
HANDLE CreateFile(
PCSTR pszFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
PSECURITY_ATTRIBUTES psa,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile);
失敗的返回值是INVALID_HANDLE_VALUE
2 創(chuàng)建一個(gè)文件映射內(nèi)核對(duì)象:
HANDLE CreateFileMapping(
HANDLE hFile,
PSECURITY_ATTRIBUTES psa,
DWORD fdwProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
PCTSTR pszName);
如果給函數(shù)的fdwProtect傳遞PAGE_READWRITE標(biāo)志,那么磁盤(pán)上文件的大小會(huì)變?yōu)橥诚裎募嗤笮 ?br />失敗的返回值是NULL。
3 將文件映射到進(jìn)程的地址空間:
PVOID MapViewOfFile(
HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
SIZE_T dwNumberOfBytesToMap);
windows2000會(huì)根據(jù)要求將部分文件映射到地址空間,而win98總是把全部?jī)?nèi)容映射到地址空間,并且僅能映射到2G-3G空間,此空間為共享空間,所有的進(jìn)程如果映射相同的文件,那么都會(huì)映射到相同的地址,一個(gè)進(jìn)程甚至不必映射就可以訪問(wèn)這個(gè)空間里其他進(jìn)程的映射文件,win2000多個(gè)進(jìn)程映射同一個(gè)文件返回的地址通常是不同的。
4 從進(jìn)程的地址空間中撤銷(xiāo)文件數(shù)句的映像
BOOL UnmapViewOfFile(PVOID pvBaseAddress);
將文件映像寫(xiě)入磁盤(pán):
BOOL FlushViewOfFile(
PVOID pvAddress,
SIZE_T dwNumberOfBytesToFlush);
windows保證單個(gè)文件映射對(duì)象的多個(gè)視圖具有相關(guān)性。但不保證但個(gè)文件的多個(gè)映射對(duì)象有相關(guān)性。
使用MapViewOfFileEx代替MapViewOfFile可以設(shè)定文件映射的基地址:
PVOID MapViewOfFileEx(
HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
SIZE_T dwNumberOfBytesToMap,
PVOID pvBaseAddress);
使用內(nèi)存映射文件在進(jìn)程間共享數(shù)據(jù)
共享機(jī)制:RPC ,COM,OLE,DDE,窗口消息(WM_COPYDATA),剪貼板,郵箱,管道,套接字。
在單機(jī)上,它們的底層實(shí)現(xiàn)方法都是內(nèi)存映射文件。
可以在頁(yè)文件中直接創(chuàng)建文件映射對(duì)象,方法是給CreateFileMapping函數(shù)的hFile參數(shù)傳遞INVALID_HANDLE_VALUE.注意,
CreateFile()函數(shù)運(yùn)行失敗也會(huì)返回這個(gè)參數(shù),因此一定要檢查CreateFile()的返回值。記住,文件函數(shù)運(yùn)行失敗的可能性太大了。
第三章:多個(gè)進(jìn)程共享對(duì)象。
堆棧:優(yōu)點(diǎn):可以不考慮分配粒度和頁(yè)面邊界之類(lèi)的問(wèn)題,集中精力處理手頭的任務(wù),缺點(diǎn)是:分配和釋放內(nèi)存塊的速度比其他機(jī)制慢,并且無(wú)法直接控制物理存儲(chǔ)器的提交和回收。
進(jìn)程的默認(rèn)堆棧是1MB,可以使用/HEAP鏈接開(kāi)關(guān)調(diào)整大小,DLL沒(méi)有相關(guān)的堆棧。
堆棧的問(wèn)題在于:很多windows函數(shù)要使用臨時(shí)內(nèi)存塊,進(jìn)程的多個(gè)線程要分配內(nèi)存塊,這些內(nèi)存都是在默認(rèn)堆棧上分配的,但規(guī)定時(shí)間內(nèi),每次只能由一個(gè)線程能夠分配和釋放默認(rèn)堆棧的內(nèi)存塊,其他想要處理內(nèi)存塊的線程必須等待。這種方法對(duì)速度又影響。可以為進(jìn)程的線程創(chuàng)建輔助堆棧,但windows函數(shù)只能使用默認(rèn)堆棧。
獲取進(jìn)程默認(rèn)堆棧句柄:
HANDLE GetProcessHeap();
創(chuàng)建輔助堆棧的理由
1 保護(hù)組件:
多個(gè)組件的數(shù)據(jù)混合交叉的存放在一塊內(nèi)存里,那么一個(gè)組件的錯(cuò)誤操作很容易影響到另外一個(gè)組件。而要定位錯(cuò)誤的來(lái)源將十分困難。
2 更有效的內(nèi)存管理
通過(guò)在堆棧中分配同樣大小的對(duì)象,可以更加有效的管理內(nèi)存,減少內(nèi)存碎片。
3 進(jìn)行本地訪問(wèn):
將同種數(shù)據(jù)集中到一定的內(nèi)存塊,可以在操作的時(shí)候訪問(wèn)較少的頁(yè)面,這就減少了RAM和硬盤(pán)對(duì)換的可能.
4 減少線程同步的開(kāi)銷(xiāo):
通過(guò)告訴系統(tǒng)只有一個(gè)線程使用堆棧(創(chuàng)建堆棧時(shí)使用HEAP_NO_SERIALIZE標(biāo)志給fdwOptions),可以避免堆棧函數(shù)執(zhí)行額外的用于保證堆棧安全性的代碼,提高效率,但此時(shí)用戶必須自己維護(hù)線程的安全性,系統(tǒng)不再對(duì)此負(fù)責(zé)。
5 迅速釋放堆棧。
因?yàn)閿?shù)據(jù)單一,因此釋放的時(shí)候只要釋放堆棧即可,不必顯示的釋放每個(gè)內(nèi)存塊。
創(chuàng)建輔助堆棧:
HANDLE HeapCreate(
DWORD fdwOptions,
SIZE_T dwInitialSize,
SIZE_T dwMaximumSize);
從堆棧中分配內(nèi)存:
PVOID HeapAlloc(
HANDLE hHeap,
DWORD fdwFlags,
SIZE_T dwBytes);注意:當(dāng)分配超過(guò)(1MB)內(nèi)存塊的時(shí)候,最好使用VirtualAlloc();
改變內(nèi)存塊的大小:
PVOID HeapReAlloc(
HANDLE hHeap,
DWORD fdwFlags,
PVOID pvMem,
SIZE_T dwBytes);
檢索內(nèi)存塊的大小:
SIZE_T HeapSize(
HANDLE hHeap,
DWORD fdwFlags,
LPVOID pvMem);
釋放內(nèi)存塊:
BOOL HeapFree(
HANDLE hHeap,
DWORD fdwFlags,
PVOID pvMem);
撤銷(xiāo)堆棧:
BOOL HeapDestroy(HANDLE hHeap);
使用輔助堆棧的方法:重載對(duì)象的new操作符,在輔助堆上分配內(nèi)存,并給對(duì)象添加一個(gè)靜態(tài)變量用于保存堆句柄。
其它堆棧函數(shù):
獲取進(jìn)程中所有堆棧得句柄:
DWORD GetProcessHeaps(DWORD dwNumHeaps,PHANDLE pHeaps);
驗(yàn)證堆棧完整性:
BOOL HeapValidate(
HANDLE hHeap,
DWORD fdwFlags,
LPCVOID pvMem);
合并地址中的空閑塊
UINT HeapCompact(
HANDLE hHeap,
DWORD fdwFlags);
BOOL HeapLock(HANDLE hHeap);
BOOL HeapUnlock(HANDLE
遍歷堆棧:
BOOL HeapWalk(
HANDLE hHeap,
PProcess_HEAP_ENTRY pHeapEntry);
各個(gè)dll也可以有自己的輸入表。
如何編寫(xiě)DLL:
在DLL的頭文件中,有如下代碼:
#ifdef MYLIB
#else
??? #define MYLIB extern "C" __declspec(dllimport)???
#endif
在每個(gè)輸出變量和輸出函數(shù)的聲明前,用MYLIB修飾。
在DLL的實(shí)現(xiàn)文件中,有如下代碼:
#i nclude "windows.h"
#define MYLIB extern "C" __declspec(dllexport)
#i nclude "Mylib.h"
其它的同編寫(xiě)普通C++程序完全相同。 "C" 表示按C方式鏈接和調(diào)用函數(shù)。C++編譯器缺省按照__stdcall方式編譯和調(diào)用,這種方式會(huì)改變函數(shù)的內(nèi)部名字。此處如果把"C"都去掉也可以,但C程序?qū)o(wú)法調(diào)用。另外,使用GetProcAddress函數(shù)時(shí)也會(huì)發(fā)生困難,因?yàn)?br />編譯程序已經(jīng)把函數(shù)名字改變了,無(wú)法用原來(lái)的名字得到函數(shù)地址。(核心編程說(shuō)的不明白,沒(méi)想到這本書(shū)錯(cuò)誤這么多)
發(fā)行的時(shí)候,將頭文件、.lib文件和DLL文件給用戶就可以了。lib文件的作用是說(shuō)明了頭文件中函數(shù)所在的DLL文件,如果沒(méi)有l(wèi)ib文件,編譯器將在鏈接過(guò)程中提示錯(cuò)誤:unresolved external symbol 函數(shù)名。
事實(shí)上,調(diào)用DLL有兩種方式,第一種是比較常用,即包含DLL的頭文件,并在鏈接的時(shí)候?qū)?dòng)態(tài)鏈接庫(kù)同exe文件像連接,建立輸入表。這個(gè)時(shí)候需要.lib文件。第二種方法exe文件中沒(méi)有輸入表,程序使用LoadLibrary(Ex)和GetProcAddress()顯式的加載DLL文件(卸載用FreeLibrary()),這個(gè)時(shí)候不需要.lib文件。
HINSTANCE LoadLibrary(PCTSTR pszDLLpathName);
HINSTANCE LoadLibraryEx(PCTSTR pszDLLpathName,NULL,0);
兩次調(diào)用LoardLibrary并不會(huì)裝載兩次dll文件,只是將dll映射進(jìn)進(jìn)程的地址空間。系統(tǒng)會(huì)自動(dòng)為每個(gè)進(jìn)程維護(hù)一個(gè)dll的計(jì)數(shù)。FreeLiabray會(huì)使計(jì)數(shù)減一,如果計(jì)數(shù)為0,系統(tǒng)就會(huì)將dll從進(jìn)程的地址空間卸載。
HINSTANCE GetModuleHandle(PCTSTR pszModuleName);//確定dll是否已經(jīng)被映射進(jìn)地址空間。
HINSTANCE hinstDll=GetModuleHandle("MyLib");
if(hinstDll==NULL)
{
??? hinstDll=LoadLibrary("MyLib");
}
DWORD GetModuleFileName(
??? HINSTANCE hinstModule,
??? PTSTR pszPathName,
??? DWORD cchPath
}
可以獲得某個(gè)模塊(.exe或者dll)的全路徑名。
幾個(gè)函數(shù)的用法:(注意GetProcAddress()函數(shù)的用法,如何定義和使用一個(gè)函數(shù)指針)
typedef int (*MYPROC)(int,int);
int main()
{
?HINSTANCE t;
?t=LoadLibraryEx(TEXT("tt.dll"),NULL,0);
?if(t)
?{
??cout<<TEXT("load success")<<endl;
?}
?HINSTANCE hinstDll=GetModuleHandle("tt.dll");
?if(hinstDll==NULL)
?{
??cout<<TEXT("first load failed")<<endl;
??hinstDll=LoadLibrary("MyLib");
?}
?size_t sz=100;
?PTCHAR str=new TCHAR[sz];
?GetModuleFileName(t,str,sz);
?cout<<str<<endl;
?delete str;
?MYPROC add=NULL;
?add=(MYPROC)GetProcAddress(t,"add");
??? if(NULL!=add)
??? {
??cout<<(*add)(1,2)<<endl;
?}
?FreeLibrary(t);
?return 0;
}
UNICODE
ANSI/UNICODE通用的定義方法(轉(zhuǎn)換只需要在編譯的時(shí)候使用_UNICODE和UNICODE):
TCHAR _TEXT("success") PTSTR PCTSTR _tcscpy(),_tcscat();
使用BYTE PBYTE定義字節(jié),字節(jié)指針和數(shù)據(jù)緩沖。
傳遞給函數(shù)的緩存大小:sizeof(szBuffer)/sizeof(TCHAR)
給字符串分配內(nèi)存:malloc(nCharacters*sizeof(TCHAR));
其它的字符串函數(shù):
PTSTR CharLower(PTSTR pszString);
PTSTR CharUpper(PTSTR pszString);
轉(zhuǎn)換單個(gè)字符:
TCHAR c=CharLower((PTSTR)szString[0]);
轉(zhuǎn)換緩存中的字符串(不必以0結(jié)尾):
DWORD CharLowerBuff(
PTSTR pszString,
DWORD cchString);
DWORD CharUpperBuff(
PTSTR pszString,
DWORD cchString);
BOOL IsCharAlpha(TCHAR ch);
BOOL IsCharAlpahNumeric(TCHAR ch);
BOOL IsCharLower(TCHAR ch);
BOOL IsCharUpper(TCHAR ch);
線程本地存儲(chǔ)(TLS):為進(jìn)程的每個(gè)線程存儲(chǔ)私有數(shù)據(jù)。用于那些一次傳遞參數(shù)后多次調(diào)用的函數(shù)(函數(shù)會(huì)保存上次調(diào)用的數(shù)據(jù))。
實(shí)現(xiàn)方法:進(jìn)程中有一個(gè)位標(biāo)志樹(shù)組(win2000的這個(gè)數(shù)組大小超過(guò)1000)。在每個(gè)線程中有一個(gè)對(duì)應(yīng)的PVOID數(shù)組。通過(guò)設(shè)定位標(biāo)志樹(shù)組的某個(gè)位來(lái)分配每個(gè)線程中的PVOID數(shù)組得相應(yīng)單元。函數(shù)需要每次檢索線程的PVOID數(shù)組,獲得該線程的相應(yīng)數(shù)據(jù)。
DWORD TlsAlloc();? //為每個(gè)線程分配一個(gè)空的PVOID數(shù)組單元。
BOOL TlsSetValue(? //線程設(shè)定自己的PVOID數(shù)組單元。
DWORD dwTlsIndex,
PVOID pvTlsValue);
PVOID TlsGetValue(
DWORD dwTlsIndex);? //檢索PVOID數(shù)組。
BOOL TLSFree(
DWORD dwTlsIndex);? //釋放PVOID數(shù)組單元
靜態(tài)TLS:__declspec(thread) DWORD gt_dwStartTime=0;//只能修飾全局或者靜態(tài)變量。
DLL掛接(進(jìn)程注入):讓自己的DLL插入到其他進(jìn)程的地址空間。
1 使用注冊(cè)表插入DLL
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs
將你的DLL路徑放入這個(gè)關(guān)鍵字下面。當(dāng)User32.dll被映射到進(jìn)程中的時(shí)候,它會(huì)加在這個(gè)關(guān)鍵字下的每個(gè)庫(kù)。
注意:(1)對(duì)win98無(wú)效
????? (2)由于加載事間比較早,你的DLL可能無(wú)法調(diào)用kernel32以外的dll.
?????? (3) 如果進(jìn)程沒(méi)有使用user32.dll,這個(gè)方法無(wú)效。
?????? (4) 需要重新啟動(dòng)。
????? (5)user32不會(huì)檢查每個(gè)庫(kù)是否加載成功。
2 使用windows鉤子
HOOK hHook=SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,hinstDll,0);
BOOL UnhookWindowsHookEx(HHOOK hhook);
具體過(guò)程如下:
????? (1)進(jìn)程B的一個(gè)線程準(zhǔn)備發(fā)送消息給一個(gè)窗口。
????? (2)系統(tǒng)察看線程上是否已經(jīng)安裝了WH_GETMESSAGE鉤子。
????? (3)系統(tǒng)察看GetMsgProc的DLL是否已經(jīng)映射到了進(jìn)程B的地址空間,如果沒(méi)有,系統(tǒng)將把DLL映射到B的地址空間,并自動(dòng)增加引用計(jì)數(shù)。
????? (4)調(diào)用GetMsgProc函數(shù),返回時(shí),系統(tǒng)會(huì)自動(dòng)為DLL的引用計(jì)數(shù)減一。
3 使用遠(yuǎn)程線程來(lái)插入DLL
????? (1)使用VirtualAllocEx,分配遠(yuǎn)程進(jìn)程的地址空間的內(nèi)存。
????? (2)使用WriteProcessMemory,將Dll的路徑名拷貝到第一個(gè)步驟中已經(jīng)分配的內(nèi)存中。
????? (3)使用GetProcAddress,獲得LoadLibrary的實(shí)際地址。
????? (4)使用CreateRemoteThread,在遠(yuǎn)程進(jìn)程中創(chuàng)建一個(gè)線程。
????? 退出:?
????? (5)使用VirtualFreeEx,釋放內(nèi)存
????? (6)使用GetProcAddress,獲得FreeLiabary的地址。
????? (7)使用CreateRemoteThread,在遠(yuǎn)程進(jìn)程中創(chuàng)建一個(gè)線程,調(diào)用FreeLiabary函數(shù)。
4 使用特洛伊DLL插入
? 替換dll.
5 將DLL作為調(diào)試程序插入
6 win98內(nèi)存映射文件,creatprocess
結(jié)構(gòu)化異常處理:
結(jié)束處理程序:__try{} __finally{}
除非__try執(zhí)行中進(jìn)程或者線程結(jié)束,否則總會(huì)執(zhí)行__finally,并且__finally中的return會(huì)替代__try中的return;好的習(xí)慣是將return ,continue,break,goto語(yǔ)句拿到結(jié)構(gòu)化異常處理語(yǔ)句外面,可以節(jié)省開(kāi)銷(xiāo)。將__try中的return 換成__leave,可以節(jié)省開(kāi)銷(xiāo)。在__finally總確定時(shí)正常進(jìn)入還是展開(kāi)進(jìn)入:
BOOL AbnormalTermination();//正常進(jìn)入返回FALSE,局部展開(kāi)或者全局展開(kāi)返回TRUE;
異常處理程序:__try{}__exception(異常過(guò)濾表達(dá)式){}
EXCEPTION_EXECUTE_HANDLE
表示處理異常,處理后轉(zhuǎn)到exception塊后面的代碼繼續(xù)執(zhí)行。
EXCEPTION_CONTINUE_EXECUTION
EXCEPTION_CONTINUE_SEARCH
可以對(duì)異常過(guò)濾表達(dá)式進(jìn)行硬編碼,也可以用一個(gè)調(diào)用一個(gè)函數(shù)來(lái)決定過(guò)濾表達(dá)式,函數(shù)的返回值是LONG.例如,可以進(jìn)行一定處理,然后返回EXCEPTION_CONTINUE_EXECUTION,再次執(zhí)行出錯(cuò)語(yǔ)句。但可能再次出錯(cuò),因此這種方法必須小心,防止生成死循環(huán)。
DWORD GetExceptionCode() 可以獲得異常種類(lèi)。它只能在__except后的括號(hào)或者異常處理程序中調(diào)用。
發(fā)生異常后,操作系統(tǒng)會(huì)像引起異常的線程的棧里壓入三個(gè)結(jié)構(gòu):EXCEPTION_RECORD CONTEXT EXCEPTION_POINTERS,其中第三個(gè)結(jié)構(gòu)包含兩個(gè)成員指針,分別指向前兩個(gè)結(jié)構(gòu),使用函數(shù)可以獲得第三個(gè)結(jié)構(gòu)的指針:
PEXCEPTION_POINTERS GetExceptionInformation();//僅可以在異常過(guò)濾器中調(diào)用,既__exception后面的小括號(hào)。
逗號(hào)表達(dá)式:從左到右對(duì)所有的表達(dá)式求值,并返回最有面的表達(dá)式的值。
引發(fā)軟件異常:
VOID RaiseException(
DWORD dwExceptionCode,
DWORD dwExceptionFlags,
DWORD nNumberOfArguments,
CONST ULONG_PTR *pArguments);
缺省調(diào)試器所在注冊(cè)表:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug? Debugger
win98是存放在win.ini里
調(diào)試器掛接到被調(diào)試進(jìn)程
BOOL DebugActiveProcess(DWORD dwProcessID);
while(*str++!='\0');
應(yīng)該注意的是在不滿足條件后,str仍然會(huì)自加1。
位操作符>>和<<不會(huì)做循環(huán)位移,即不會(huì)把移出的位放到另一頭。
多態(tài)性和動(dòng)態(tài)聯(lián)編的實(shí)現(xiàn)過(guò)程分析
一、基礎(chǔ):
1、多態(tài)性:使用基礎(chǔ)類(lèi)的指針動(dòng)態(tài)調(diào)用其派生類(lèi)中函數(shù)的特性。
2、動(dòng)態(tài)聯(lián)編:在運(yùn)行階段,才將函數(shù)的調(diào)用與對(duì)應(yīng)的函數(shù)體進(jìn)行連接的方式,又叫運(yùn)行時(shí)聯(lián)編或晚捆綁。
二、過(guò)程描述:
1、編譯器發(fā)現(xiàn)一個(gè)類(lèi)中有虛函數(shù),編譯器會(huì)立即為此類(lèi)生成虛擬函數(shù)表 vtable(后面有對(duì)vtable的分析)。虛擬函數(shù)表的各表項(xiàng)為指向?qū)?yīng)虛擬函數(shù)的指針。
2、編譯器在此類(lèi)中隱含插入一個(gè)指針vptr(對(duì)vc編譯器來(lái)說(shuō),它插在類(lèi)的第一個(gè)位置上)。
有一個(gè)辦法可以讓你感知這個(gè)隱含指針的存在,雖然你不能在類(lèi)中直接看到它,但你可以比較一下含有虛擬函數(shù)時(shí)的類(lèi)的尺寸和沒(méi)有虛擬函數(shù)時(shí)的類(lèi)的尺寸,你能夠發(fā)現(xiàn),這個(gè)指針確實(shí)存在。
3、在調(diào)用此類(lèi)的構(gòu)造函數(shù)時(shí),在類(lèi)的構(gòu)造函數(shù)中,編譯器會(huì)隱含執(zhí)行vptr與vtable的關(guān)聯(lián)代碼,將vptr指向?qū)?yīng)的vtable。這就將類(lèi)與此類(lèi)的vtable聯(lián)系了起來(lái)。
4、在調(diào)用類(lèi)的構(gòu)造函數(shù)時(shí),指向基礎(chǔ)類(lèi)的指針此時(shí)已經(jīng)變成指向具體的類(lèi)的this指針,這樣依靠此this指針即可得到正確的vtable,從而實(shí)現(xiàn)了多態(tài)性。在此時(shí)才能真正與函數(shù)體進(jìn)行連接,這就是動(dòng)態(tài)聯(lián)編。
定義純虛函數(shù)方法:virtual returntype function()= 0;
所有的類(lèi)型都可以用new動(dòng)態(tài)創(chuàng)建,包括類(lèi),結(jié)構(gòu),內(nèi)置數(shù)據(jù)類(lèi)型。
exit()函數(shù)包含在"cstdlib"中
泛型編程,使用STL庫(kù)
總共有近75個(gè)泛型算法
所有容器的共通操作:
== != = empty() size() clear(),begin(),end(),以及insert和erase,不過(guò)后者隨容器的的不同而不同
序列式容器:
vector(數(shù)組):插入和刪除的效率較低,但存取效率高。
list(雙向鏈表):與前者相反,插入和刪除的效率較高,但存取效率低。每個(gè)元素包含三個(gè)字段:value back front.
deque(隊(duì)列):在前端和末尾操作效率高。
生成序列式容器的五種方法:
1 產(chǎn)生空的容器:
list<string> slist;
vector<int> vtor;
2 產(chǎn)生特定大小的容器,容器中的每個(gè)元素都以其默認(rèn)值為初值(發(fā)現(xiàn)VC中的int double等沒(méi)有默認(rèn)值)。
list<int> ilist(1024);
vector<string> svec(24);
3 產(chǎn)生特定大小的容器,并為每個(gè)元素指定初值:
list<int ilist(1024,0);
vector<string> svec(24,"default");
4 通過(guò)一對(duì)迭代器產(chǎn)生容器,這對(duì)迭代器用來(lái)表示數(shù)組作為初值的區(qū)間:
int ia[10]={1,2,3,4,5,6,7,8,9,0};
vector<int>? iv(ia+2,ia+8);
5 復(fù)制某個(gè)現(xiàn)有的容器的值:
vector<int> ivec1;
//填充ivec1;
vector<int> ivec2(ivec1);
有6個(gè)方法用于操作開(kāi)始和末尾的元素:push_front() pop_front() push_back() pop_back(),由于pop操作僅刪除元素而不返回元素,因此還需要front() back()方法取開(kāi)始和末尾的元素,另外,vector不包括push_front() pop_front方法,很顯然,無(wú)法實(shí)現(xiàn)。
intert的四種變形:
iterator insert(iterator position,elemType value):將value插到position前。返回值指向被插入的元素。
void insert(iterator position,int count,elemType value):在position前插入count個(gè)元素,每個(gè)元素都是value.
void insert(iterator1 position,iterator2 first,iterator2 last):將first,last之間的元素插到position前.
iterator insert( iterator position):在position前插入元素,初值為所屬類(lèi)型的默認(rèn)值。
erase的兩種變形:
1? iterator erase(iterator posit):刪除posit指向的元素。
list<string>::iterator it=find(slist.begin(),slist,end(),str);
slist.erase(it);
2? iterator erase(iterator first,iterator last):刪除first,last間的元素。
list不支持iterator的偏移運(yùn)算
對(duì)于常值容器,使用常迭代器:
const vector<string> cs_vec;
vector<string::const_iterator iter_cs_vec.begin();
iterator可以當(dāng)作指針用,可以用*取內(nèi)容,也可以用->調(diào)用對(duì)象的成員。
使用泛型算法
#i nclude <algorithm>
find():線性搜索無(wú)序集合
binary_search():二分搜索有序集合。
count():返回元素個(gè)數(shù)。
search():搜索序列,如果存在返回的iterator指向序列首部,否則指向容器末尾。
max_element(begin,end):返回區(qū)間內(nèi)的最大值。
copy(begin,end,begin):元素復(fù)制。
sort(begin,end):排序。
function objects:#i nclude <functional>
算術(shù)運(yùn)算:
plus<type> minus<type> negate<type> multiplies<type> divides<type> modules<type>
關(guān)系運(yùn)算:
less<type> less equal<type> greater<type greater equal<type> equal_to<type> not_equal_to<type>
邏輯運(yùn)算:
logical_and<type> logical_or<type> logical_not<type>
adapter:適配器。
bind1st:將數(shù)值綁定到function object的第一個(gè)參數(shù)。
bind2nd:將數(shù)值綁定到function object的第二個(gè)參數(shù)。
使用map:
#i nclude <map>
#i nclude <string>
map<string,int> words;
words["vermeer"]=1;
map<string,int>::iterator it=words.begin();
for(;it!=words.end();++it)
cout<<"key:"<<it->first<<"value:"<<it->second<<endl;
查找map元素的方法:
words.find("vermeer");//返回iterator,指向找到的元素,找不到返回end();
還可以:
if(words.count(search_word))
count=words[search_word];
使用set:
#i nclude <set>
#i nclude <string>
set<string> word_exclusion;
//判斷是否存在某個(gè)元素
if(word_exclusion.count(tword))
//默認(rèn)情況下,所有元素按less-than運(yùn)算排列
//加入元素
iset.insert(ival);
iset.insert(vec.begin(),vec.end());
與set相關(guān)的算法
set_intersection() set_union() set_difference() set_symmetric_difference()
使用insertion adapters:
#i nclude <iterator>
back_inserter()
inserter()
front_inserter()
使用STL通常會(huì)有很多警告,為了避免在調(diào)試模式(debug mode)出現(xiàn)惱人的警告,使用下面的編譯器命令:
#pragma warning(disable: 4786)
strncpy(dest,source,count) if(count〈=strlen(source)),那么null結(jié)尾不會(huì)被加在dest的尾部,如果count>strlen(source),那么不足的部分會(huì)用null填充。
windows內(nèi)存是由高地址向底地址分配的,但變量的存儲(chǔ)是從底地址到高地址的,如INT類(lèi)型的四個(gè)字節(jié),數(shù)組的每個(gè)元素。
?
內(nèi)存復(fù)制的時(shí)候不能用字符串拷貝函數(shù),因?yàn)榧词故褂胹trncpy指定了復(fù)制的長(zhǎng)度,拷貝函數(shù)也會(huì)遇到'\0'自動(dòng)終止,要使用MEMSET。
由于對(duì)齊的關(guān)系,下面兩個(gè)結(jié)構(gòu)使用sizeof,前者是12,后者是16。
struct DNSAnswer
{
?unsigned short name;
?unsigned short type;
?unsigned short cla;
?unsigned short length;
?unsigned int?? ttl;
};
struct DNSAnswer
{
?unsigned short name;
?unsigned short type;
?unsigned short cla;
?unsigned int?? ttl;
??????? unsigned short length;
};
子類(lèi)可以使用父類(lèi)的保護(hù)成員,而友元比子類(lèi)的權(quán)限還大,可以使用類(lèi)的私有和保護(hù)成員。
在內(nèi)存分配失敗的情況下,系統(tǒng)只有在出錯(cuò)處理函數(shù)為空的情況下,才會(huì)拋出異常:std::bad_alloc(),否則會(huì)反復(fù)調(diào)用處理函數(shù)并再次嘗試分配內(nèi)存。
如果重載了NEW,那么在繼承的時(shí)候要小心,如果子類(lèi)沒(méi)有覆蓋NEW,那么它會(huì)去使用父類(lèi)的NEW ,因此應(yīng)該在new,delete中做檢查
if (size != sizeof(base))???????????? // 如果數(shù)量“錯(cuò)誤”,讓標(biāo)準(zhǔn)operator new,base為類(lèi)名
??? return ::operator new(size);??????? // 去處理這個(gè)請(qǐng)求
? if (size != sizeof(base)) {????? // 如果size"錯(cuò)誤",
??? ::operator delete(rawmemory);? // 讓標(biāo)準(zhǔn)operator來(lái)處理請(qǐng)求
??? return;???????????????????????
? }
c++標(biāo)準(zhǔn)規(guī)定,要支持0內(nèi)存請(qǐng)求(分配一個(gè)字節(jié)),并且可以刪除NULL指針(直接返回)。
在創(chuàng)建線程的時(shí)候,傳遞的變量一定要是全局或者靜態(tài)的變量,因?yàn)閭鬟f的是變量的地址,如果是局部變量地址很快就會(huì)失效。
主線程退出后,其子線程自動(dòng)結(jié)束。
智能指針:它可以避免內(nèi)存泄露,因?yàn)橹悄苤羔樖窃跅I蟿?chuàng)建的;還可以避免堆上內(nèi)存的重復(fù)釋放錯(cuò)誤,因?yàn)樗WC只有一個(gè)指針擁有這塊內(nèi)存的所有權(quán)。