最近在做圖像處理(其實(shí)是利用攝像機(jī)采回來的Image進(jìn)行分析,從而得到目標(biāo)的運(yùn)動(dòng)信息),,既然是圖像處理那就得有圖像Source,,沒錯(cuò)那就是Camera,實(shí)驗(yàn)室的這個(gè)Camera還挺高級(jí)的,是千兆以太網(wǎng)接口的,,呵呵,,管他是什么接口——反正我們的任務(wù)就是將數(shù)據(jù)采集到指定的緩沖區(qū)內(nèi),然后再把緩沖區(qū)內(nèi)的數(shù)據(jù)“顯示出來”或者“分析分析”,。按照常規(guī)的做法,,你可能會(huì)這樣申請(qǐng)一段緩沖區(qū),估計(jì)編寫過Windows程序得人10個(gè)得有9個(gè)會(huì)這么開辟一段“內(nèi)存”: (1)LPVOID pbuf = VirtualAlloc(....) 或者 (2)LPVOID pbuf= malloc(....) 或者 (3)LPVOID pbuf = new[...] 好的,,到這里我再回顧一下我們的目的:將數(shù)據(jù)從千兆以太網(wǎng)接口取回來然后存到pbuf所指向的緩沖區(qū)內(nèi),。 恩!沒錯(cuò),,既然是和物理設(shè)備進(jìn)行數(shù)據(jù)交換,,那么我們就希望數(shù)據(jù)交換具備實(shí)時(shí)性,那靠什么保障呢,?在普通的PC平臺(tái)下,,這只能依靠申請(qǐng)純物理內(nèi)存來做到這一點(diǎn),因?yàn)閷?duì)于開辟在物理內(nèi)存——DDR3內(nèi)存條上的緩沖區(qū)而言,,其訪問時(shí)間會(huì)很短,,至于為什么相信玩過一些高端FPGA電路板的人會(huì)很清楚,,如果沒有接觸過這類集成電路,,那么你去查看DDR3的手冊(cè),你會(huì)發(fā)現(xiàn)DDR3的總線速率不過零點(diǎn)幾個(gè)納秒,,因此一次讀/寫周期(相當(dāng)于數(shù)個(gè)總線周期)也就2~3個(gè)納秒,。 至此,有些看客可能會(huì)說,,你到底想說什么呢,?你這不是廢話嗎?你的pbuf不是已經(jīng)通過上述方式申請(qǐng)了內(nèi)存了嗎,? 我要澄清的是,,事情遠(yuǎn)沒有那么簡單!MicroSoft也遠(yuǎn)沒有那么膚淺,!事實(shí)上學(xué)過計(jì)算機(jī)的人應(yīng)該會(huì)很清楚這個(gè)問題:Windows在用戶模式(Ring3)采用的是虛擬內(nèi)存機(jī)制,,簡而言之:每個(gè)進(jìn)程(Process)都有自己獨(dú)立的4個(gè)G的虛擬內(nèi)存空間。然而,,你可以看看你的系統(tǒng)配置,,你最多也不過4個(gè)G的內(nèi)存,那么如果我運(yùn)行10個(gè)進(jìn)程豈不是需要40個(gè)G的虛擬內(nèi)存空間,?這不矛盾了,。。。 恩,,要解釋清這一點(diǎn),,可以參考下MS的相關(guān)資料,其實(shí)原理很好理解,,就是用硬盤來代替內(nèi)存,。將當(dāng)前需要運(yùn)行的“可執(zhí)行片段”(請(qǐng)不要笑話我幼稚的理解,我實(shí)在找不出什么好詞了--?。┨峤坏絻?nèi)存,,同時(shí)將那些在內(nèi)存中不經(jīng)常使用的數(shù)據(jù)換出到硬盤上。我為了敘述的簡單,,以后就將真正的物理存儲(chǔ)資源(我們希望將緩沖區(qū)開辟到這里)稱為非分頁資源,,而將映射到硬盤上的虛擬內(nèi)存稱為分頁資源。 Windows訪問非分頁資源其實(shí)就是CPU訪問物理存儲(chǔ)器,,就是我們所能夠想象到的CPU通過其總線對(duì)內(nèi)存條的訪問,。然而當(dāng)Windows訪問分頁資源的時(shí)候,首先會(huì)觸發(fā)一個(gè)異常(Exception),,然后會(huì)通過該異常執(zhí)行一些服務(wù)代碼,,這些服務(wù)代碼,會(huì)將硬盤上的數(shù)據(jù)再換回到內(nèi)存中去,。 通過上述闡述,,你應(yīng)該理解一個(gè)道理:在用戶模態(tài)(Ring3層)所有的內(nèi)存分配函數(shù)是針對(duì)虛擬內(nèi)存而言的,換句話說,,就是無法保證你開辟的緩沖區(qū)到底是分頁資源還是非分頁資源,。如果你通過方法(1),(2)或者(3)所分配的內(nèi)存恰好是分頁資源,。這將意味著,,你將無法以預(yù)期的實(shí)時(shí)性能來和千兆以太網(wǎng)接口進(jìn)行數(shù)據(jù)交換,原因已經(jīng)說過了:你其實(shí)是在和硬盤打交道而不是真正的內(nèi)存,。 那么我們的任務(wù)就來了:如何實(shí)現(xiàn)再用戶模式(Ring3層)確保所分配的內(nèi)存確實(shí)是物理內(nèi)存——非分頁資源呢,? 方法其實(shí)也不難:就是寫一個(gè)驅(qū)動(dòng)(Driver),呵呵,,驅(qū)動(dòng)運(yùn)行在Ring0因此很容易申請(qǐng)到物理內(nèi)存,! 其實(shí)在這個(gè)驅(qū)動(dòng)程序中,你的目的不是為了訪問某個(gè)硬件設(shè)備,,而是單純的開辟一段物理內(nèi)存并“返回”至用戶模式的應(yīng)用程序中,。 在這樣的一個(gè)驅(qū)動(dòng)程序中,你沒有必要實(shí)現(xiàn)所有的派遣函數(shù)(Dispatch Routine),,事實(shí)上,,你只要實(shí)現(xiàn)IRP_MJ_DEVICE_CONTROL(在DDK中定義:wdm.h)所對(duì)應(yīng)的派遣函數(shù)就夠了,,當(dāng)然你也肯定也得實(shí)現(xiàn)[IRP_MJ_PNP, IRP_MN_REMOVE_DEVICE]要不然你怎么卸載你的驅(qū)動(dòng)啊,?呵呵,,不要偷懶,要不然Windows不會(huì)讓你有好果子吃的,。 言歸正傳,,IRP_MJ_DEVICE_CONTROL的會(huì)被應(yīng)用層的DeviceIoControl這個(gè)API函數(shù)所觸發(fā),嘿嘿,,技巧就在這里:DeviceIoControl的定義如下,, BOOL DeviceIoControl( HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped ); 我們著重看一下黑色字體的參數(shù),這兩個(gè)參數(shù),,代表了驅(qū)動(dòng)程序所返回的“內(nèi)容”,。在調(diào)用這個(gè)函數(shù)的時(shí)候,的確由用戶模式的應(yīng)用程序分配lpOutBuffer,,但是如果dwioControlCode指定了操作方式為METHOD_IN_DIRECT或者M(jìn)ETHOD_OUT_DIRECT的話(具體請(qǐng)參照DDK說明,,這里僅僅是提供一個(gè)思路),那么就可以在驅(qū)動(dòng)里面做這樣我個(gè)人認(rèn)為很邪門的事情以保證lpOutBuffer確實(shí)開辟到了物理內(nèi)存上面去: // Map the user-mode buffer to the real physical memory UCHAR * pOBuffer = (UCHAR*)MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); 對(duì),,原理就是把用戶模式的虛擬地址映射到真正的物理地址上去,!其中Irp就是所謂的IRP(I/O Request Package)結(jié)構(gòu)體指針,Irp->MdlAddress包含了Ring3層你通過方法(1),,(2)或者(3)所分配的虛擬內(nèi)存首地址,,pOBuffer就是真實(shí)的物理地址。換言之經(jīng)過DeviceIoControl的調(diào)用以及相應(yīng)驅(qū)動(dòng)的配合,,用戶態(tài)的虛擬內(nèi)存(確切地說是用戶態(tài)虛擬內(nèi)存中的分頁資源成分)就被牢牢地鎖在了物理內(nèi)存上,。只要你不顯式的通過VirtualFree等函數(shù)進(jìn)行釋放,,那么你在用戶層(Ring3)對(duì)你所申請(qǐng)的虛擬內(nèi)存進(jìn)行操作,,實(shí)際上就是對(duì)在驅(qū)動(dòng)程序中重新映射后的物理內(nèi)存進(jìn)行操作,因而極大地提高了存儲(chǔ)效率,! 為了驗(yàn)證這一點(diǎn),,我進(jìn)行了如下操作: (A)開辟用戶態(tài)的緩沖區(qū)(不確定到底是分頁的還是非分頁的) (B)調(diào)用DeviceIoControl嘗試將所開辟的緩沖區(qū)鎖定到物理內(nèi)存上去 (C)在應(yīng)用程序中:memset(pbuf, 0xaa, buf_size) (D)再次調(diào)用DeviceIoControl打印出上一次所分配的物理內(nèi)存中的內(nèi)容。 如果打印出0xAA的話就證明我的設(shè)想是成立的,,結(jié)果表明此想法是完全行的通的,! 具體驗(yàn)證代結(jié)果如下: 可以看出,所分配的用戶模式緩沖區(qū)首地址為:0x04be0000,,然而這段地址其實(shí)被映射到了Kernel mode下的0xa6a6a000為首地址的非分頁內(nèi)存上去了,,因而無論是驅(qū)動(dòng)對(duì)0xa6a6a000進(jìn)行操作還是應(yīng)用程序?qū)?x04be0000進(jìn)行操作,其實(shí)都是訪問的同一段物理內(nèi)存,,這就達(dá)到了我們最終的目的了,! ———————————————— 版權(quán)聲明:本文為CSDN博主「LnTigerLn」的原創(chuàng)文章,,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文及本聲明,。 |
|