首页 KMDTut第9章共享内存

KMDTut第9章共享内存

举报
开通vip

KMDTut第9章共享内存 09共享内存.pdf 共享内存 董岩 译 9.1 SharingMemory驱动程序源代码 9.1.1 DriverEntry函数 9.1.2 DispatchControl函数 9.1.3 Memory Descriptor List 9.1.4 Cleanup函数 9.2 SharingMemory应用程序源代码 源代码:KmdKit\examples\basic\MemoryWorks\SharingMemory 在上一个例子SharedSection中,我们使用section进行通讯,驱动程序被硬性...

KMDTut第9章共享内存
09共享内存.pdf 共享内存 董岩 译 9.1 SharingMemory驱动程序源代码 9.1.1 DriverEntry函数 9.1.2 DispatchControl函数 9.1.3 Memory Descriptor List 9.1.4 Cleanup函数 9.2 SharingMemory应用程序源代码 源代码:KmdKit\examples\basic\MemoryWorks\SharingMemory 在上一个例子SharedSection中,我们使用section进行通讯,驱动程序被硬性限制在具体进程的地址上下文中,即驱动程 序所使用的虚拟地址位于此进程的地址空间中。我们在本例中使用的方法将没有这个缺点。对于驱动程序来说,这种方法 更为自然些。 9.1 SharingMemory驱动程序源代码 我们首先从驱动程序开始分析。 ;@echo off ;goto make ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; ; SharingMemory - 示例程序,驱动程序使用用户进程的一块内存向用户进程中传递数据 ; ; ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: .386 .model flat, stdcall option casemap:none ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; В К Л Ю Ч А Е М Ы Е Ф А Й Л Ы ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: include \masm32\include\w2k\ntstatus.inc include \masm32\include\w2k\ntddk.inc include \masm32\include\w2k\ntoskrnl.inc include \masm32\include\w2k\hal.inc includelib \masm32\lib\w2k\ntoskrnl.lib includelib \masm32\lib\w2k\hal.lib include \masm32\Macros\Strings.mac include ..\common.inc include seh0.inc ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; Н Е И З М Е Н Я Е М Ы Е Д А Н Н Ы Е ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: .const CCOUNTED_UNICODE_STRING "\\Device\\SharingMemory", g_usDeviceName, 4 CCOUNTED_UNICODE_STRING "\\DosDevices\\SharingMemory", g_usSymbolicLinkName, 4 ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; Н Е И Н И Ц И А Л И З И Р О В А Н Н Ы Е Д А Н Н Ы Е ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: .data? g_pSharedMemory PVOID ? g_pMdl PVOID ? g_pUserAddress PVOID ? 第 1 页 09共享内存.pdf g_fTimerStarted BOOL ? ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; К О Д ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: .code ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; UpdateTime ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: UpdateTime proc local SysTime:LARGE_INTEGER invoke KeQuerySystemTime, addr SysTime invoke ExSystemTimeToLocalTime, addr SysTime, g_pSharedMemory ret UpdateTime endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; TimerRoutine ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: TimerRoutine proc pDeviceObject:PDEVICE_OBJECT, pContext:PVOID invoke UpdateTime ret TimerRoutine endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; Cleanup ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Cleanup proc pDeviceObject:PDEVICE_OBJECT .if g_fTimerStarted invoke IoStopTimer, pDeviceObject invoke DbgPrint, $CTA0("SharingMemory: Timer stopped\n") .endif .if ( g_pUserAddress != NULL ) && ( g_pMdl != NULL ) invoke MmUnmapLockedPages, g_pUserAddress, g_pMdl invoke DbgPrint, $CTA0("SharingMemory: Memory at address %08X unmapped\n"), g_pUserAddress and g_pUserAddress, NULL .endif .if g_pMdl != NULL invoke IoFreeMdl, g_pMdl invoke DbgPrint, $CTA0("SharingMemory: MDL at address %08X freed\n"), g_pMdl and g_pMdl, NULL .endif .if g_pSharedMemory != NULL invoke ExFreePool, g_pSharedMemory invoke DbgPrint, $CTA0("SharingMemory: Memory at address %08X released\n"), g_pSharedMemory and g_pSharedMemory, NULL .endif ret Cleanup endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; DispatchCleanup ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: DispatchCleanup proc pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP invoke DbgPrint, $CTA0("\nSharingMemory: Entering DispatchCleanup\n") 第 2 页 09共享内存.pdf invoke Cleanup, pDeviceObject mov eax, pIrp mov (_IRP PTR [eax]).IoStatus.Status, STATUS_SUCCESS and (_IRP PTR [eax]).IoStatus.Information, 0 fastcall IofCompleteRequest, pIrp, IO_NO_INCREMENT invoke DbgPrint, $CTA0("SharingMemory: Leaving DispatchCleanup\n") mov eax, STATUS_SUCCESS ret DispatchCleanup endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; DispatchCreateClose ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: DispatchCreateClose proc pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP mov eax, pIrp mov (_IRP PTR [eax]).IoStatus.Status, STATUS_SUCCESS and (_IRP PTR [eax]).IoStatus.Information, 0 fastcall IofCompleteRequest, pIrp, IO_NO_INCREMENT mov eax, STATUS_SUCCESS ret DispatchCreateClose endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; DispatchControl ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: DispatchControl proc uses esi edi pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP local dwContext:DWORD invoke DbgPrint, $CTA0("\nSharingMemory: Entering DispatchControl\n") mov esi, pIrp assume esi:ptr _IRP mov [esi].IoStatus.Status, STATUS_UNSUCCESSFUL and [esi].IoStatus.Information, 0 IoGetCurrentIrpStackLocation esi mov edi, eax assume edi:ptr IO_STACK_LOCATION .if [edi].Parameters.DeviceIoControl.IoControlCode == IOCTL_GIVE_ME_YOUR_MEMORY .if [edi].Parameters.DeviceIoControl.OutputBufferLength >= sizeof PVOID invoke ExAllocatePool, NonPagedPool, PAGE_SIZE .if eax != NULL mov g_pSharedMemory, eax invoke DbgPrint, \ $CTA0("SharingMemory: %X bytes of nonpaged memory allocated at address %08X\n"), \ PAGE_SIZE, g_pSharedMemory invoke IoAllocateMdl, g_pSharedMemory, PAGE_SIZE, FALSE, FALSE, NULL .if eax != NULL mov g_pMdl, eax invoke DbgPrint, \ $CTA0("SharingMemory: MDL allocated at address %08X\n"), g_pMdl invoke MmBuildMdlForNonPagedPool, g_pMdl _try 第 3 页 09共享内存.pdf invoke MmMapLockedPagesSpecifyCache, g_pMdl, UserMode, MmCached, \ NULL, FALSE, NormalPagePriority .if eax != NULL mov g_pUserAddress, eax invoke DbgPrint, \ $CTA0("SharingMemory: Memory mapped into user space at address %08X\n"), \ g_pUserAddress mov eax, [esi].AssociatedIrp.SystemBuffer push g_pUserAddress pop dword ptr [eax] invoke UpdateTime invoke IoInitializeTimer, pDeviceObject, TimerRoutine, addr dwContext .if eax == STATUS_SUCCESS invoke IoStartTimer, pDeviceObject inc g_fTimerStarted invoke DbgPrint, $CTA0("SharingMemory: Timer started\n") mov [esi].IoStatus.Information, sizeof PVOID mov [esi].IoStatus.Status, STATUS_SUCCESS .endif .endif _finally .endif .endif .else mov [esi].IoStatus.Status, STATUS_BUFFER_TOO_SMALL .endif .else mov [esi].IoStatus.Status, STATUS_INVALID_DEVICE_REQUEST .endif assume edi:nothing .if [esi].IoStatus.Status != STATUS_SUCCESS invoke DbgPrint, $CTA0("SharingMemory: Something went wrong\:\n") invoke Cleanup, pDeviceObject .endif push [esi].IoStatus.Status assume esi:nothing fastcall IofCompleteRequest, esi, IO_NO_INCREMENT invoke DbgPrint, $CTA0("SharingMemory: Leaving DispatchControl\n") pop eax ret DispatchControl endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; DriverUnload ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: DriverUnload proc pDriverObject:PDRIVER_OBJECT invoke IoDeleteSymbolicLink, addr g_usSymbolicLinkName mov eax, pDriverObject invoke IoDeleteDevice, (DRIVER_OBJECT PTR [eax]).DeviceObject 第 4 页 09共享内存.pdf ret DriverUnload endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; В Ы Г Р У Ж А Е М Ы Й П Р И Н Е О Б Х О Д И М О С Т И К О Д ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: .code INIT ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; DriverEntry ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING local status:NTSTATUS local pDeviceObject:PDEVICE_OBJECT mov status, STATUS_DEVICE_CONFIGURATION_ERROR and g_pSharedMemory, NULL and g_pMdl, NULL and g_pUserAddress, NULL and g_fTimerStarted, FALSE invoke IoCreateDevice, pDriverObject, 0, addr g_usDeviceName, FILE_DEVICE_UNKNOWN, \ 0, TRUE, addr pDeviceObject .if eax == STATUS_SUCCESS invoke IoCreateSymbolicLink, addr g_usSymbolicLinkName, addr g_usDeviceName .if eax == STATUS_SUCCESS mov eax, pDriverObject assume eax:ptr DRIVER_OBJECT mov [eax].MajorFunction[IRP_MJ_CREATE*(sizeof PVOID)], offset DispatchCreateClose mov [eax].MajorFunction[IRP_MJ_CLEANUP*(sizeof PVOID)], offset DispatchCleanup mov [eax].MajorFunction[IRP_MJ_CLOSE*(sizeof PVOID)], offset DispatchCreateClose mov [eax].MajorFunction[IRP_MJ_DEVICE_CONTROL*(sizeof PVOID)], offset DispatchControl mov [eax].DriverUnload, offset DriverUnload assume eax:nothing mov status, STATUS_SUCCESS .else invoke IoDeleteDevice, pDeviceObject .endif .endif mov eax, status ret DriverEntry endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: end DriverEntry :make set drv=SharingMemory \masm32\bin\ml /nologo /c /coff %drv%.bat \masm32\bin\link /nologo /driver /base:0x10000 /align:32 /out:%drv%.sys /subsystem:native /ignore:4078 %drv%.obj del %drv%.obj move %drv%.sys .. echo. pause 9.1.1 DriverEntry函数 mov [eax].MajorFunction[IRP_MJ_CREATE*(sizeof PVOID)], offset DispatchCreateClose 第 5 页 09共享内存.pdf mov [eax].MajorFunction[IRP_MJ_CLEANUP*(sizeof PVOID)], offset DispatchCleanup mov [eax].MajorFunction[IRP_MJ_CLOSE*(sizeof PVOID)], offset DispatchCreateClose mov [eax].MajorFunction[IRP_MJ_DEVICE_CONTROL*(sizeof PVOID)], offset DispatchControl 除了通常的IRP_MJ_CREATE、IRP_MJ_CLOSE和IRP_MJ_DEVICE_CONTROL之外,这里还要处理IRP_MJ_CLEANUP。当用户模式代 码调用了CloseHandle时,驱动程序开始发出IRP_MJ_CLEANUP,通知系统设备驱动将要关闭句柄。在此之后句柄真正关闭, 驱动收到IRP_MJ_CLOSE。在本例中我们希望释放掉之前使用的资源,所以需要处理IRP_MJ_CLEANUP。 9.1.2 DispatchControl函数 invoke ExAllocatePool, NonPagedPool, PAGE_SIZE .if eax != NULL mov g_pSharedMemory, eax 获得了控制代码IOCTL_GIVE_ME_YOUR_MEMORY,我们来从非分页内存中分配一个内存页。驱动应该将这部分内存映射到用户 进程的地址空间中,在接收到请求的进程上下文中,即在我们的应用程序的地址空间中。使用非分页内存以及使用一个内 存页的原因后面会介绍到。 ExAllocatePool返回系统空间中的地址,也就是说驱动程序是与当前上下文无关的。现在需要将这块内存映射到这个进程 的地址空间中去,使之被共享。我们的驱动程序是单层的,所以对IRP_MJ_DEVICE_CONTROL的处理我们想放在我们应用程序 的地址上下文中。在我们将分配的一个内存页映射到进程地址空间之前必须先分配MDL(Memory Descriptor List。我还真 不知道怎么翻译成俄语) 9.1.3 Memory Descriptor List MDL是一个结构体,用于描述一片内存区域中的物理内存页。 MDL STRUCT Next PVOID ? _Size SWORD ? MdlFlags SWORD ? Process PVOID ? MappedSystemVa PVOID ? StartVa PVOID ? ByteCount DWORD ? ByteOffset DWORD ? MDL ENDS PMDL typedef PTR MDL 更准确地讲,MDL结构体是一个首部(header)。紧随首部之后的是许多物理页的页号(page frame number, PFN)。但是 MDL所描述的内存区域在虚拟地址空间中是连续不间断的,而它们所占据的物理页所在的物理内存却可能是按任意的顺序排 列。正是因为如此再加上页的数量较大,我们需要维护一个表来 记录 混凝土 养护记录下载土方回填监理旁站记录免费下载集备记录下载集备记录下载集备记录下载 该内存区中所有的物理页。同时这也用在了直接内存 访问(Direct Memory Access, DMA)中。在我们这里物理页总共就一个。 invoke IoAllocateMdl, g_pSharedMemory, PAGE_SIZE, FALSE, FALSE, NULL .if eax != NULL mov g_pMdl, eax 函数IoAllocateMdl前两个参数定义了虚拟地址和内存区的大小,是建立MDL所必须的。如果MDL不与IRP相关联(我们这里 正是这样),则第三个参数就为FALSE。第四个参数定义是否需要减少进程的份额,并只用于位于驱动程序链最上层的驱动 程序或是单层的驱动程序(我们这里正是这样)。每一个进程都要获得一定份额的系统资源。当进程为自己分配资源时, 这个份额就会减小。如果份额用完,就不能再为其分配相应的资源。我们可不想减少这个用于分配内存的进程份额,所以 第四个参数设为FALSE。最后一个参数定义了一个非必要的指向IRP的指针,通过这个指针MDL可以与IRP关联。例如,对于 直接I/O,I/O管理器为用户缓冲区建立MDL,并将其地址送至IRP.MdlAddress。我们弄这个MDL可不是用来搞I/O的,所以就 没有什么IRP,最后一个参数也就设为NULL。 函数IoAllocateMdl为MDL分配内存并初始化首部。 invoke MmBuildMdlForNonPagedPool, g_pMdl MmBuildMdlForNonPagedPool填充物理页号并更新MDL首部的某些范围。 _try 如果我们将要调用的函数MmMapLockedPagesSpecifyCache的参数AccessMode为UserMode且调用失败,系统会抛出一个异常 (这是DDK公开说明的),这个异常我们能够处理,我们来建立SEH-frame。 invoke MmMapLockedPagesSpecifyCache, g_pMdl, UserMode, MmCached, \ NULL, FALSE, NormalPagePriority 将MDL所描述的内存映射到我们应用程序的地址空间中。 MDL的第一个参数为描述所要映射的内存区域的MDL。第二个参数定义了是否要从用户模式下访问这块内存。第三个参数定 义了这块内存被处理器缓存的方式。如果第四个参数为NULL,则系统会自己从用户空间中挑选虚拟地址。第五个参数定义 第 6 页 09共享内存.pdf 了如果万一系统不能完成请求,是否要出现BSOD,但是这只用在第二个参数为KernelMode时。我们可不想让系统死掉,于 是将这个参数赋值为FALSE。最后一个参数定义了成功调用MmMapLockedPagesSpecifyCache的重要性。 在Windows NT4下,函数MmMapLockedPagesSpecifyCache并未实现,代之以MmMapLockedPages,形式如下: invoke MmMapLockedPages, g_pMdl, UserMode MmMapLockedPages在Windows的后续版本中也是存在的,且只是对MmMapLockedPagesSpecifyCache,但是不能使用 MmMapLockedPagesSpecifyCache的后面四个参数。 借助于MDL,在用户地址空间中只能映射锁定的内存,即位于非分页池中的内存(对于使用分页内存的所有情况我并不全都 知道)。这是使用非分页内存的第一个理由。 映射的内存不能少于一页,所以我们需要完整的一个内存页,但是实际上总共只用其中的几个字节。 .if eax != NULL mov g_pUserAddress, eax mov eax, [esi].AssociatedIrp.SystemBuffer push g_pUserAddress pop dword ptr [eax] MmMapLockedPagesSpecifyCache返回我们的内存页映射到用户空间中的地址。我们将这个地址传递到应用程序中。从这一 刻起该内存页就成为共享的了,并且驱动程序对其的使用不依赖于当前的地址上下文,而用户进程也能以自己的地址来访 问。 为了直观起见,函数UpdateTime将把当前系统时间放在我们的内存页中。 UpdateTime proc local SysTime:LARGE_INTEGER invoke KeQuerySystemTime, addr SysTime invoke ExSystemTimeToLocalTime, addr SysTime, g_pSharedMemory ret UpdateTime endp KeQuerySystemTime取得的是格林威治时间。再用ExSystemTimeToLocalTime将其转换为本地时间。 invoke IoInitializeTimer, pDeviceObject, TimerRoutine, addr dwContext 初始化Timer,Timer将与设备对象建立关联。DEVICE_OBJECT结构体中有一个Timer域,其中有指向IO_TIMER结构体的指 针。函数IoInitializeTimer的第一个参数定义了Timer要和哪一个设备对象关联。第二个参数是一个指向系统启用Timer时 要调用的函数的指针。TimerRoutine函数将调用UpdateTime,在我们的内存页中更新系统时间。TimerRoutine运行在IRQL = DISPATCH_LEVEL(DDK中有记载)。这就是我们使用非分页内存的第一个也是最主要的原因。IoInitializeTimer的最后 一个参数是一个指向任意数据的指针。这个指针将被传递到TimerRoutine中。我们这里不需要指定这个值,所以只是随便 虚构一个变量。 .if eax == STATUS_SUCCESS invoke IoStartTimer, pDeviceObject inc g_fTimerStarted 启动Timer。现在函数TimerRoutine大约每秒被调用一次。这个时间间隔是不能修改的。 .if [esi].IoStatus.Status != STATUS_SUCCESS invoke Cleanup, pDeviceObject .endif 如果上述各阶段有一个发生问题,就要收回资源。 9.1.4 Cleanup函数 Cleanup proc pDeviceObject:PDEVICE_OBJECT .if g_fTimerStarted invoke IoStopTimer, pDeviceObject .endif .if ( g_pUserAddress != NULL ) && ( g_pMdl != NULL ) invoke MmUnmapLockedPages, g_pUserAddress, g_pMdl and g_pUserAddress, NULL .endif 第 7 页 09共享内存.pdf .if g_pMdl != NULL invoke IoFreeMdl, g_pMdl and g_pMdl, NULL .endif .if g_pSharedMemory != NULL invoke ExFreePool, g_pSharedMemory and g_pSharedMemory, NULL .endif ret Cleanup endp 这里进行的工作都是很显然的,不用过多解释。唯一的奥妙在于将内存映射到用户空间和还原操作是借助于 MmUnmapLockedPages函数实现的,应该在进程定义的地址上下文中进行,这是很自然的。 9.2 SharingMemory应用程序源代码 ;@echo off ;goto make ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; ; Клиент драйвера SharingMemory ; ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: .386 .model flat, stdcall option casemap:none ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; В К Л Ю Ч А Е М Ы Е Ф А Й Л Ы ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: include \masm32\include\windows.inc include \masm32\include\kernel32.inc include \masm32\include\user32.inc include \masm32\include\advapi32.inc includelib \masm32\lib\kernel32.lib includelib \masm32\lib\user32.lib includelib \masm32\lib\advapi32.lib include \masm32\include\winioctl.inc include \masm32\Macros\Strings.mac include ..\common.inc ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; E Q U A T E S ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: IDD_MAIN equ 1000 IDC_TIME equ 1001 IDI_ICON equ 1002 TIMER_ID equ 100 ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; Н Е И Н И Ц И А Л И З И Р О В А Н Н Ы Е Д А Н Н Ы Е
本文档为【KMDTut第9章共享内存】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_928689
暂无简介~
格式:pdf
大小:125KB
软件:PDF阅读器
页数:13
分类:互联网
上传时间:2011-09-01
浏览量:25