




已阅读5页,还剩22页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
2对物理内存的直接读写 在PC环境下,Windows是不允许用户态进程直接访问内存的,任何对内存的访问都会引起程序的异常。而在嵌入式设备中,需要直接对内存进行读写,以此来提高处理速度,此外,在ARM体系中,I/O被映射到高端的地址进行访问,只有读写物理地址,I/O的驱动才能高效地运行。Windows CE中有一些API提供了对物理内存的“直接”访问。不过,在访问之前,必须把物理内存映射到虚拟地址中,通过虚拟地址才能读写物理内存。PHYSICAL_ADDRESS描述了Windows CE的物理内存结构体,Windows CE在ceddk.h中定义了PHYSICAL_ADDRESS,其定义如下:n 在ceddk.h中typedefLARGE_INTEGERPHYSICAL_ADDRESS,*PPHYSICAL_ADDRESS;n 在winnt.h中typedefunion_LARGE_INTEGERstructDWORDLowPart; LONGHighPart;LONGLONGQuadPart; LARGE_INTEGER;可见,Windows CE中用64位来代表物理地址,对于大多数32位的CPU而言,只需要把它的HighPart设置为0就可以了。VirtualAlloc()函数是Windows CE中分配连续虚拟地址的API,VirtualCopy()函数将一段物理内存映射到虚拟地址。因此,在进程中访问物理地址,就像访问虚拟地址一样方便,当然,如何选择虚拟地址是需要研究的。/申请虚拟内存LPVOIDVirtualAlloc(LPVOIDlpAddress,/希望的虚拟内存起始地址DWORDdwSize, /以字节为单位的大小DWORDflAllocationType, /申请类型,分为Reserve和CommitDWORDflProtect /访问权限);/把物理内存绑定到虚拟地址空间BOOLVirtualCopy(LPVOIDlpvDest, /虚拟内存的目标地址LPVOIDlpvSrc, /物理内存地址DWORDcbSize, /要绑定的大小DWORDfdwProtect /访问权限);VirtualAlloc对虚拟内存的申请分为两步,保留MEM_RESERVE和提交MEM_COMMIT。其中MEM_RESERVE只是在进程的虚拟地址空间内保留一段,并不分配实际的物理内存,因此保留的虚拟内存并不能被应用程序直接使用。MEM_COMMIT阶段才真正为虚拟内存分配物理内存。下面的代码显示了如何使用VirtualAlloc和VirtualCopy来访问物理内存。因为VirtualCopy负责把一段物理内存和虚拟内存绑定,所以VirtualAlloc执行时只需要对内存保留,没有必要提交。FpDriverGlobals=(PDRIVER_GLOBALS)VirtualAlloc(0,DRIVER_GLOBALS_PHYSICAL_MEMORY_SIZE,MEM_RESERVE,PAGE_NOACCESS);if(FpDriverGlobals=NULL)ERRORMSG(DRIVER_ERROR_MSG,(TEXT(VirtualAllocfailed!rn);return;elseif(!VirtualCopy(PVOID)FpDriverGlobals,(PVOID)(DRIVER_GLOBALS_PHYSICAL_MEMORY_START),DRIVER_GLOBALS_PHYSICAL_MEMORY_SIZE,(PAGE_READWRITE|PAGE_NOCACHE)ERRORMSG(DRIVER_ERROR_MSG,(TEXT(VirtualCopyfailed!rn);return;CEDDK还提供了函数MmMapIoSpace,用来把一段物理内存直接映射到虚拟内存。用MmMapIoSpace申请的内存要用MmUnmapIoSpace释放,此函数的原型如下:PVOIDMmMapIoSpace(PHYSICAL_ADDRESSPhysicalAddress,/起始物理地址ULONGNumberOfBytes,/要映射的字节数BOOLEANCacheEnable/是否缓存);VOIDMmUnmapIoSpace(PVOIDBaseAddress,/MmMapIoSpace返回的起始虚拟地址ULONGNumberOfBytes/);其实,MmMapIoSpace函数内部也是调用VirtualAlloc和VirtualCopy函数来实现物理地址到虚拟地址的映射的。MmMapIoSpace函数的原代码是公开的,可以从%_WINDOWS CEROOT%PUBLICCOMMONOAKDRIVERSCEDDKDDK_MAPddk_map.c得到。从MmMapIoSpace的实现中也可以看出VirtualAlloc和VirtualCopy的用法。PVOIDMmMapIoSpace(INPHYSICAL_ADDRESSPhysicalAddress,INULONGNumberOfBytes,INBOOLEANCacheEnable)PVOIDpVirtualAddress;ULONGLONGSourcePhys;ULONGSourceSize;BOOLbSuccess;SourcePhys=PhysicalAddress.QuadPart&(PAGE_SIZE-1);SourceSize=NumberOfBytes+(PhysicalAddress.LowPart&(PAGE_SIZE-1);pVirtualAddress=VirtualAlloc(0,SourceSize,MEM_RESERVE,PAGE_NOACCESS);if(pVirtualAddress!=NULL) bSuccess=VirtualCopy( pVirtualAddress,(PVOID)(SourcePhys8),SourceSize, PAGE_PHYSICAL|PAGE_READWRITE|(CacheEnable?0:PAGE_NOCACHE);if(bSuccess)(ULONG)pVirtualAddress+=PhysicalAddress.LowPart&(PAGE_SIZE-1); else VirtualFree(pVirtualAddress,0,MEM_RELEASE); pVirtualAddress=NULL; returnpVirtualAddress;此外,Windows CE还供了AllocPhysMem函数和FreePhysMem函数,用来申请和释放一段连续的物理内存。函数可以保证申请的物理内存是连续的,如果函数成功,会返回虚拟内存的句柄和物理内存的起始地址。这对于DMA设备尤为有用。在这里就不详细介绍了,读者可以参考Windows CE的联机文档。7.5.5进程地址空间结构系统中的32位虚拟地址提供了4GB的虚拟内存空间,对于嵌入式应用来说,内存一般很小,因而系统在使用内存方面作了些限制,以提供更高效能的存储空间管理。这些限制包括:大量的系统保留空间,实际上这些地址空间,通常不对应到任何的实体页面;系统处理程序数最多只有32个,每个处理程序的实际可使用内存空间受到限制(32MB);有固定的处理程序共享内存;有ROM地址的对应等。如图7-8所示。Windows CE.NET把XIP DLL单独加载到Slot 1中,这样对于每个进程来说,它总的地址空间就大了一倍,也就是64MB。当这个进程得到CPU使用权时,它的整个地址空间被内核映射到Slot 0,也就是当前进程使用的地址空间,然后开始运行。图中给出的地址实际上是经过映射到Slot 0之后的结构。从图中可以看出,进程首先加载代码段,因为每个进程最低部64KB作为保留区域,所以代码段从0x0001 0000开始,内核为代码段分配足够的虚拟地址空间后,接着为只读数据和可读/可写数据分配空间,接着为资源数据分配空间,之后为默认堆和栈分配空间。非XIP DLL从进程最高地址向下开始加载。7.5.6堆和栈堆是一段连续的较大的虚拟地址空间。应用程序在堆中可以动态地分配、释放所需大小的内存块。利用堆的优点是在一定范围内减小了内存碎块,而且开发者分配内存块前不必了解CPU的类型。因为不同的CPU分页大小不相同,每个内存页可能是1KB、4KB或更多。在堆内分配内存块可以是任意大小的,而直接分配内存就必须以内存页为单位。当应用程序启动时,内核在进程所在的地址空间中为进程分配一个默认192KB大小的虚拟地址空间,但是并不立刻提交物理内存。如果在运行时192KB不能满足需求,那么内核会在进程地址空间中重新查找一个足够大小的空闲的地址空间,然后复制原来堆的数据,最后释放原来的堆所占的地址空间。这是因为默认的堆的高地址处还有栈,所以必须重新分配。栈也是一段连续的虚拟地址空间,和堆相比空间要小得多,它是专为函数使用的。当调用一个函数时(包括线程),内核会产生一个默认的栈,并且内核会立刻提交少量的物理内存(也可以禁止内核立刻提交物理内存)。栈的大小和CPU有关,一般为64KB,并且保留顶部2KB以防止溢出。可以修改栈的大小,具体修改方法在讲解线程的时候已经说过了,这里就不再重复了。一般不会修改栈的大小,如果在编译链接时修改大小,那么所有栈的大小都会改变,这不太合理。实际开发中最好不要在栈中分配很大、很多的内存块,如果分配的内存块超过了默认栈的限制,那么会引起访问非法并且内核会立刻终止进程。最好在进程的堆中分配大的内存块并且在函数返回前释放,或者在创建线程时指定栈的大小。7.5.7分页机制Windows CE内核用分页虚拟内存机制来管理和分配程序内存。虚拟内存系统提供了连续的内存块。每个64KB的内存区域被分成多个1024B或4096B的页。所以应用程序不必进行实际的内存分配管理。对于少于64KB的内存请求,应用程序可以用系统为Windows CE程序提供的本地堆或创建分离的堆来满足应用程序的内存需要。内核也可以为每个新的进程或线程在栈上分配内存。Windows CE操作系统使用KDataStruct数据结构来存放低地址2GB内的数据。代码样例7-9列出KdataStruct的整个数据结构代码如下:在KDataStruct数据结构中,又利用PSECTION aSections64将低地址2GB分割成64个32MB大小的空间,称之为Section。Section再被分割成512个64KB大小的空间,称之为MemBlock,如程序代码4.2所示。MemBlock再被分割成数个页(Page)。如图7-9所示。页的大小(PAGE_SIZE)在不同的系统中略有不同。ARM4处理器的PAGE_SIZE为4096,ARM920的PAGE_SIZE为1024,MIPS及x86处理器的PAGE_SIZE则为4096。若以PAGE_SIZE = 4096,则MemBlock可被分割成16个页。程序代码7-10列出MemBlock整个数据结构,其中aPagesPAGES_PER_BLOCK字段记录虚拟内存中每一个页所对应到的物理内存地址。代码如下:#define BLOCK_MASK 0x1FFtypedefMEMBLOCK *SECTIONBLOCK_MASK+1;/每一个SECTION指向512个BLOCKtypedefSECTION *PSECTION;#define PAGE_SIZE 4096 /* page size */#define PAGES_PER_BLOCK(0x10000 / PAGE_SIZE)struct MemBlock ACCESSLOCKalk; /* 00: key code for this set of pages */ ucharcUses; * 04: # of page table entries sharing this leaf */ucharflags; /* 05: mapping flags */ shortixBase; /* 06: first block in region */ shorthPf; /* 08: handle to pager */ shortcLocks; /* 0a: lock count */ ulongaPagesPAGES_PER_BLOCK; /* MemBlock */7.5.8深入VirtualAlloc内部VirtualAlloc是任何Microsoft Win32操作系统中最基础的内存分配调用函数。它在页级别分配内存。VirtualAlloc调用分配内存的过程分为两个步骤。第一步,保留虚拟内存空间的区域。它只是防止一部分虚拟地址空间被用于其他用途。保留内存空间之后,就可以提交(commit)部分或整个区域,这个过程是指将实际物理内存映射到保留区域。VirtualAlloc函数用于保留内存空间和提交内存。下面显示了VirtualAlloc函数的原型。LPVOID VirtualAlloc (LPVOID lpAddress, DWORD dwSize, DWORD flAllocationType, DWORD flProtect);VirtualAlloc的第一个参数是要分配的内存区域的虚拟地址。如果该参数是NULL,则由系统确定从哪里分配内存区域,并以64KB或者32KB为边界。第二个参数是dwSize,它是要分配或保留的区域的大小。因为该参数是以字节而不是页为单位指定的,所以系统会将所请求的大小自动调整为页大小的整数倍。flAllocationType参数指定分配的类型。可以指定以下标志的组合MEM_COMMIT、MEM_AUTO_COMMIT和MEM_RESERVE。MEM_COMMIT标志用于分配程序使用的内存。MEM_RESERVE用于保留要随后提交的虚拟地址空间。保留页是无法访问的,直到通过指定区域并使用MEM_COMMIT标志进行了另一个VirtualAlloc调用为止。MEM_AUTO_COMMIT标志惟一用于Windows CE并且很好用,但它不是本文的主题。因此,要使用VirtualAlloc来分配可使用的RAM,应用程序必须调用VirtualAlloc两次,一次保留内存空间,再一次则提交物理RAM,或者调用VirtualAlloc一次,这需要在flAllocationType参数中组合使用MEM_RESERVE和MEM_COMMIT标志。组合保留和提交标志方式所使用的代码更少,并且更快、更简单。该技术通常用在Windows XP应用程序中,但用在Windows CE应用程序中不是很好。代码样例7-11演示了存在的问题。代码如下:INT i;PVOID pMem512;for (i = 0; i 512; i+)pMemi = VirtualAlloc (0, PAGE_SIZE, MEM_RESERVE | MEM_COMMIT,PAGE_READWRITE);代码样例7-11似乎是无害的。它分配了512块内存,每块内存的大小为1页。问题是:在Windows CE系统上,该代码总是会失败。原因在于Win32操作系统保留内存区域的方式。Windows CE 应用程序的问题是它们必须位于32MB虚拟内存空间的范围内。在整个应用程序内存空间中该空间的大小只有51264KB,并且它们中的一部分需要用作应用程序代码、本地堆、堆栈和应用程序所加载的每个DLL的区域。通常,在对VirtualAlloc进行大约470次调用之后上面的代码片段将失败。上述问题的解决方案是首先保留足够用于整个分配的较大区域,然后在需要时提交RAM,如代码样例7-12所示。INT i;PVOID pBase, pMem512;pBase = VirtualAlloc (0, 512*PAGE_SIZE, MEM_RESERVE, PAGE_READWRITE);for (i = 0; i bTrustLevel != KERN_TRUST_FULL) /* 这里的pCurProc的原型为: * #define pCurProc (KData.pCurPrc); *#define KData (ArmHigh-kdata) * KDataStruct-pCurPrc * 综合来看也就是ArmHigh-KDataStruct-pCurPrc,也就是当前运行的进程,即要加载DLL的进程。 * ptr to current PROCESS struct 03: level of trust of this exe */ KSetLastError(pCurThread,(DWORD)NTE_BAD_SIGNATURE); return 0; fLbFlags |= LLIB_NO_PAGING | LLIB_NO_MUI;/*设置内核加载标志*/ dllname = lpszFileName+strlenW(lpszFileName); while (dllname != lpszFileName) & (*(dllname-1) != (WCHAR) & (*(dllname-1) != (WCHAR)/) dllname-; /*以上对DLL名字进行了处理*/ DEBUGMSG(ZONE_LOADER1,(TEXT(LoadOneLibrary %s (%s)rn),lpszFileName,dllname); pMod = FindModByName(dllname, wFlags);/*在module chain中寻找准备要加载的module*/ if (pMod) /*Module被找到,也就是说以前被安装过的*/ /*以前的判别主要是判断已经被安装的module是否能被debug, * 如果已经有调试器和这个module绑定的话,就算加载失败,返回NULL。 */ if (FT_OBJSTORE != pMod-oe.filetype) & !ChkDebug (&pMod-oe) KSetLastError(pCurThread,(DWORD)NTE_BAD_SIGNATURE); return 0; if (pCurProc-bTrustLevel = KERN_TRUST_FULL) & (pMod-bTrustLevel = KERN_TRUST_RUN) /*判断当前进程的执行权限和要加载的module本身的权限*/ KSetLastError(pCurThread,(DWORD)NTE_BAD_SIGNATURE); return 0; if (pMod-wFlags != wFlags) KSetLastError(pCurThread,ERROR_BAD_EXE_FORMAT); return 0; /*到这里我们已经找到module,并且它也符合各项加载要求, * 因此将它从Proc0复制到pProc指向的区域中, * 同时还要增加module的引用计数,如果失败,则解除 */ if (!(prevRefCnt = IncRefCount (pMod) & !(wFlags & (LOAD_LIBRARY_IN_KERNEL|LOAD_LIBRARY_AS_DATAFILE) & !CopyRegions (pMod) DecRefCount (pMod); dwErr = ERROR_OUTOFMEMORY; else /*已经成功复制了要加载并且已经存在的dll *在复制完以后,还要做些其他设置 */ if (fLbFlags & LLIB_NO_PAGING) & (pMod-oe.pagemode = PM_FULLPAGING) /*如果是内核加载,则不允许页交换,但是module的权限又是可以页交换的,那么就需要修改*/ o32_lite *optr = pMod-o32_ptr; int i; pMod-oe.pagemode = PM_NOPAGEOUT; /* 如果正在使用内存池的页,则需要把页交还 */ EnterCriticalSection (&PagerCS);/*进入临界区*/ if (INVALID_PG_INDEX != pMod-pgqueue.idxHead) UnloadMod (pMod); FreeAllPagesFromQ (&pMod-pgqueue); /* 释放 */ LeaveCriticalSection (&PagerCS); DEBUGMSG(ZONE_LOADER1,(TEXT(LoadOneLibraryPart2 - change from pageable to not pagedrn); /* 将所有的页换入,并且锁住不允许再交换 */ for (i = 0; i e32.e32_objcnt; i +, optr +) /o32_lite *optr = pMod-o32_ptr; DEBUGMSG(ZONE_LOADER1,(TEXT(LoadOneLibraryPart2 - paging in %8.8lx-%8.8lx, (%a)rn), ZeroPtr (optr-o32_realaddr), ZeroPtr (optr-o32_realaddr) + optr-o32_vsize, (optr-o32_flags & IMAGE_SCN_MEM_WRITE)? WRITE : READ); if (!DoLockPages (LPVOID)ZeroPtr (optr-o32_realaddr), optr-o32_vsize, NULL, LOCKFLAG_QUERY_ONLY | (optr-o32_flags & IMAGE_SCN_MEM_WRITE)? LOCKFLAG_WRITE : LOCKFLAG_READ) dwErr = ERROR_OUTOFMEMORY; pMod-oe.pagemode = PM_FULLPAGING; break; if (dwErr) if (!prevRefCnt) U
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024年三明市清流县第二高级中学专任教师招聘真题
- 大棚蔬菜养护知识培训课件
- 企业宣传册设计委托协议
- 展示柜门相关知识培训课件
- 2024年宣城市人民医院招聘真题
- 汽车制造职业病应急预案(3篇)
- 大撇折硬笔书法课件
- 防控物资保障组应急预案(3篇)
- 2025年金融科技创新产品应用场景合作担保协议
- 2025年餐饮行业员工技能提升培训服务协议
- 2025高级会计师考试试题及答案
- 2025-2030中国特高压电网建设规划与设备需求分析报告
- 2026版赢在微点顶层设计大一轮物理-专题提升二十 测量电阻的其他几种方法
- 民族文化宫2025年公开招聘17人笔试模拟试题含答案详解
- 光传输业务配置课件
- 2025年幼儿园教师专业考试试题及答案书
- 2025年辽宁省地质勘探矿业集团有限责任公司校园招聘笔试备考题库带答案详解
- 初中英语新课程标准测试试题及答案3套
- 旅游区奖惩制度管理办法
- 儿童生长发育监测课件
- 科技项目申报专员系列培训(技术攻关项目)
评论
0/150
提交评论