WinCE中OEM適配層點滴之系統(tǒng)初始化
——
圖1 OAL結(jié)構(gòu)圖 |
因為OAL層代碼大多數(shù)和CE啟動時系統(tǒng)初始化工作有關(guān),所以本篇文章以CE的啟動順序為線索。其它OAL知識在下一篇文章中講解。
一、在Boot Loader解壓CE內(nèi)核鏡像文件(nk.bin)后開始跳轉(zhuǎn)到StartUp(),StartUp函數(shù)屬于OAL層,此時CE操作系統(tǒng)內(nèi)核還沒有運行。StartUp函數(shù)的功能主要有兩個,一是初始化CPU為已知狀態(tài)(known state),二是調(diào)用內(nèi)核初始化函數(shù)(x86平臺為KernelInitialize,其它平臺為KernelStart)。初始化CPU工作因CPU的不同而不同,如果是ARM系列,包括設(shè)置CPU為管理員模式、禁止IRQ和FIQ、禁止MMU、清空指令和數(shù)據(jù)緩沖、檢測啟動原因、配置GPIO和內(nèi)存控制器、初始化RTC、保存OEMAddressTable地址等。執(zhí)行完畢后調(diào)用KernetStart。如果是x86系列,包括設(shè)置CPU為保護(hù)模式、初始化內(nèi)存控制器、保存OEMAddressTable地址等。執(zhí)行完畢后調(diào)用KernetInitialize。
二、內(nèi)核初始化函數(shù)的功能也因CPU的不同而不同,不過有一些功能是相同的,如初始化串口(為了輸出調(diào)試信息)、調(diào)用OEMInit函數(shù)等。對于x86系列,初始化工作除了上述的功能外還包括讀取OEMAddressTable內(nèi)容、確定分頁大小、內(nèi)核重定位、初始化中斷分配表、初始化分頁表、內(nèi)存初始化和其它初始化。對于其它系列CPU請參考CE幫助文檔。
1. 串口調(diào)試:
串口調(diào)試函數(shù)包括OEMInitDebugSerial、OEMReadDebugByte、OEMWriteDebugByte等。從OEMInitDebugSerial的源碼可以看出,系統(tǒng)從BOOT_ARG_PTR_LOCATION為首地址的結(jié)構(gòu)中判斷當(dāng)前連接的串口是哪個,然后配置這個串口。如果你的設(shè)備的串口I/O地址設(shè)置和CE默認(rèn)的一致的話,就能在CE內(nèi)核得到CPU控制權(quán)到啟動完畢這段時間里通過串口得到調(diào)試信息。
2. OEMInit
一般在OEMInit中初始化所有外圍的硬件、初始化系統(tǒng)時鐘(system tick)和RTC(real time clock)、初始化KITL(Kernel Independent Transport Layer)。例如I486平臺的OEMinit函數(shù),它先關(guān)聯(lián)所有的IRQ和中斷ID,然后初始化PCI總線、網(wǎng)絡(luò)適配器、電源管理、PIC(可編程中斷控制器)、系統(tǒng)時鐘,最后檢測是否有擴(kuò)展內(nèi)存。另外如果OEM要通過OAL暴露的函數(shù)指針或者全局變量來增強(qiáng)功能的話,就要在此函數(shù)中實現(xiàn)(在下面詳細(xì)講解)。
3. 檢測擴(kuò)展內(nèi)存
我們都知道在config.bib配置文件中設(shè)置CE系統(tǒng)使用RAM總量(如果不知道請參考我的文章Platform Builder之旅系列),注意這個RAM總量不是總的物理內(nèi)存的大小。PB編譯的內(nèi)核包含一個變量ulRAMEnd,將在config.bib中定義的RAM的起始地址 + RAM大小的和賦值給ulRAMEnd。在CE內(nèi)核的啟動過程中,ulRAMEnd的值賦值給全局變量MainMemoryEndAddress,CE內(nèi)核通過訪問MainMemoryEndAddress得到RAM的總量信息。假如基于CE的設(shè)備附加了RAM,而MainMemoryEndAddress的值沒有包括這段附加的RAM,結(jié)果CE內(nèi)核無法知道已經(jīng)附加了RAM。為了讓CE內(nèi)核了解附加RAM的信息,OEM應(yīng)該編寫一個函數(shù)檢測RAM的總量,并把總量值賦給MainMemoryEndAddress。OAL暴露了一個函數(shù)指針pNKEnumExtensionDRAM,OEM應(yīng)該把編寫好的函數(shù)地址賦給這個函數(shù)指針。如果OEM不準(zhǔn)備自己編寫內(nèi)存檢測函數(shù)的話也可以調(diào)用OEMGetExtensionDRAM。從幫助文檔中看出OEMGetExtensionDRAM這個函數(shù)能夠檢測內(nèi)存的總量,但是CE的針對X86 平臺的源碼中沒有具體編寫這個函數(shù)的實現(xiàn)代碼(見%_WINCEROOT%PUBLICCOMMONOAKCSPI486OALcfwpc.c)。也就是說在X86平臺上調(diào)用OEMGetExtensionDRAM是檢測不到RAM的。如果OEM有興趣編寫檢測RAM總量的函數(shù),可以調(diào)用現(xiàn)成的函數(shù)IsDRAM。這個函數(shù)也保存在cfwpc.c中。
三、內(nèi)核初始化函數(shù)執(zhí)行完畢后開始按如下步驟執(zhí)行:
1. 內(nèi)核創(chuàng)建用于與filesys.exe同步的事件對象SYSTEM/FSReady,之后啟動filesys.exe。啟動filesys.exe的意義是讓filesys.exe讀取注冊表數(shù)據(jù)。
2. 內(nèi)核等待事件SYSTEM/FSReady被觸發(fā),這個事件是由filesys.exe在做完一系列工作后觸發(fā)。這一系列的工作內(nèi)容如下:
2.1 先檢測這是一次冷啟動還是熱啟動,如果是冷啟動,那么初始化對象存儲內(nèi)存區(qū)域。
2.2 調(diào)用OEMIoControl函數(shù),I/O控制代碼為IOCTL_HAL_INIT_RTC,也就是初始化RTC。
2.3 初始化數(shù)據(jù)庫子系統(tǒng)和API、文件系統(tǒng)API、消息隊列API。
2.4 如果操作系統(tǒng)鏡像(nk.bin)包括RAM文件系統(tǒng),那么讀取Initobj.dat文件內(nèi)容后創(chuàng)建一個RAM文件系統(tǒng)。
2.5 初始化注冊表(在內(nèi)存中形成注冊表)。
2.6 如果此時device.exe沒有啟動,那么讀取HKEY_LOCAL_MACHINESystemStorageManager下“Dll”的值(這個值為存儲管理器所在的.dll的文件名)并加載到內(nèi)存。加載之后創(chuàng)建一個線程專用于初始化存儲管理器,初始化之后此線程結(jié)束。
2.7 初始化NLS(national language support)。關(guān)于NLS請參見我的文章《CE下中文輸入法編輯器》。
2.8 為數(shù)據(jù)庫引擎設(shè)置本地ID。
2.9 讀取Initdb.ini文件,安裝在對象存儲中的數(shù)據(jù)庫。
2.10 觸發(fā)SYSTEM/FSReady事件,之后filesys.exe處于等待狀態(tài),等待內(nèi)核發(fā)通知給它。
3. 此時注冊表已經(jīng)存在于內(nèi)存當(dāng)中,內(nèi)核開始讀取如下位置數(shù)據(jù):
HKEY_LOCAL_MACHINELoaderSystemPath HKEY_LOCAL_MACHINESYSTEMOOMcbLow and cpLow HKEY_LOCAL_MACHINESYSTEMKERNELInjectDLL HKEY_LOCAL_MACHINEMUIEnable and SysLang HKEY_CURRENT_USERMUICurLang |
4. 內(nèi)核設(shè)置低內(nèi)存處理(out of memory)。低內(nèi)存處理是指當(dāng)前可用的內(nèi)存非常少時,內(nèi)核所做的解決方案(CE幫助文檔中有詳細(xì)說明)。
5. 內(nèi)核在做好了上述工作后通知filesys.exe,由filesys.exe做其余工作。filesys.exe所做的工作內(nèi)容如下:
5.1 讀取HKEY_LOCAL_MACHINESystemEvents 下包含的所有事件對象名稱并一一創(chuàng)建。
5.2 讀取HKEY_LOCAL_MACHINEInit 下包括的所有應(yīng)用程序名稱并一一啟動。如果device.exe在列表中并且此時它已經(jīng)啟動了,那么觸發(fā)SYSTEM/BOOTPHASE2事件,這會使device.exe重新讀取注冊表數(shù)據(jù)來完成最后的驅(qū)動程序初始化。
5.3 初始化時間區(qū)域(time zone)。
評論