进程之间通讯问题

来源:互联网 发布:资产配置知乎 编辑:程序博客网 时间:2024/05/22 17:37

 

1 映射数据文件

第一步:调用CreateFileForMapping函数。在Windows CE.NET中推荐使用这个函数替代CreateFile函数。CreateFileForMapping函数由内核执行并创建文件,它也可以打开由 CreateFile函数创建的文件。其参数同CreateFile相似。参数1指定文件路径,注意文件路径的格式是没有盘符的,参数2指定访问方式(读 或写),参数3指定共享模式,参数4指定安全属性(必须设置为NULL),参数5指定是创建还是打开文件,参数6指定文件属性,参数7忽略。具体参数细节 参见Windows CE.NET帮助。函数返回创建或者打开的文件的句柄。

第二步:调用CreateFileMapping函数。这 个函数创建一个无名的或者有名的内存映射文件对象。参数1为文件句柄。这个值由CreateFileForMapping函数返回。参数2为安全属性(必 须设置为NULL),参数3指定要映射的文件的保护属性(只读或者读写),参数4和参数5共同用于指定要映射的文件的大小。文件的容量过大将导致32位整 数也不能表示,所以这里用64位变量表示,其中参数4为高32位数,参数5为低32位数。最后一个参数指定内存映射文件的名称。这里可以设置为NULL 表示不需要名字。

第三步:调用MapViewOfFile函数。这个函数用于保留一段足够的地址空间,并且将永久存储器上的文件数据映射到这 个地址空间。映射后这段地址空间又叫做文件视图,映射范围可以是全部文件,也可以是部分文件。这里需要注意的是如果文件很大,那这个函数将在全局地址空间 内分配地址空间。参数1指定内存映射文件对象的句柄,这个值由CreateFileMapping函数返回。参数2CreateFileMapping 函数中参数3很相似,都是用于限定访问权限。参数3和参数4共同用于指定映射区域的开始位置。其中参数3为高32位数,参数4为低32位数。参数5指定映 射区域的大小。需要注意的是参数3和参数4指定的64位数开始位置可以不是64KB的倍数。而其它Windows操作系统就必须限制以64KB为单位。另 外还要注意的是帮助文档中说不能保证一个文件的映射视图是连续的,并建议为了防止访问非法,应该加入结构化异常处理机制。这个可能性我认为很小,一般对于 大于2MB的虚拟地址空间的申请,内核都会在全局地址空间中分配。全局地址空间(0x4200 00000x7FFF FFFF)近1GB的空间应该足够用了。毕竟WindowsCE下的文件都很小。不过在代码中加入结构化异常处理也不是坏事。我们应该养成凡是读写文件数据时都加入结构化异常处理的习惯。

第四步:进行读/写操作。MapViewOfFile函数如果成功执行,那么返回映射视图的首地址。这时就可以把视图当成是一个缓冲区,开始读或写操作了。

第五步:执行结束工作。先调用UnmapViewOfFile函数撤销文件映射视图。参数只有一个,指定视图首地址。然后调用CloseHandle函数关闭内存映射文件对象,参数为句柄。最后再次调用CloseHandle函数,关闭打开的文件的句柄。

5.2 进程之间通信

进程之间有时需要通信。系统提供的进程之间的通信机制比如COM、剪贴板等,在底层 实现上都是利用内存映射文件技术。其实进程之间通信的思路很简单,在这里我顺便讲一下。在其它Windows操作系统中,每个进程独自占有4GB的地址空 间,高2GB是内核的地址空间,而低2GB是进程的地址空间。一个进程所能访问的所有低2GB地址都是自己的地址空间,当访问内核地址空间时就会受到内核 的限制。这样一个进程当然无法访问其它进程了。为解决进程间通信的问题,内存映射文件技术被利用作为解决方案。原来内存映射文件只映射类似磁盘一类的存储 器上的文件。而为了更快速地在进程之间通信,内存映射文件还可以提交物理内存。实现方法是通过访问同一个内存映射文件对象(映射到物理内存),两个进程或 多个进程就能够访问到同一块物理内存,这样一个进程写到物理内存的数据,其它进程就能够看到了。而Windows CE虽然每个进程只占有32MB的地址空间,而且所有进程全部处于4GB的地址空间中,但是彼此还是不能够随意访问的。在Windows CE下除了使用内存映射文件技术外,还有一种方法也很适合使用,就是利用对象存储。对象存储本身使用RAM文件系统,用普通的操作文件的API就可以创 建、读取存在于对象存储区域内的文件。/Windows 目录就存在于对象存储区域内。我们可以利用在/Windows目录下创建文件来实现进程间通信。这种方法既实现简单,只需调用几个文件API函数,又可以 减少通信时间,因为/Windows目录存在于物理内存中,数据I/O当然很快了。利用对象存储来实现进程之间的通信是我自己想出来的,MSDN或其它文 档并没有这方面的说明。需要注意的就是对象存储区域的大小。另外从实现的代码量上看也不如内存映射文件技术。

下面讲解如何利用内存映射文件实现进程之间的通信。假设进程A和进程B需要通信,那么进程A需要先创建一个内存映射文件(之前不必调用 CreateFileForMapping函数来创建文件,因为不需要创建文件)。这个内存映射文件可以是在永久存储器中,也可以是在内存中。为了减小通 信时间,最好提交物理内存。进程A在调用CreateFileMapping函数时,参数1指定为INVALID_HANDLE_VALUE,这表示这个 内存映射文件对象将要把物理内存提交到地址空间中。最后一个参数一定要指定一个名字。进程B也同样调用CreateFileMapping函数,而且参数 相同。内核会根据名字来判断是否已经存在一个内存映射文件对象,如果创建了就返回原来的对象的句柄。接下去就不用细说了。参照5.1去执行就可以了。要注 意的是进程B调用CreateFileMapping函数后要按如下代码检验函数执行结果:

HANDLE  hMap;
hMap = CreateFileMapping(INVALID_HANDLE_VALUE,
                        NULL,
                        PAGE_READWRITE,
                        0,
                        1000,
                        L"abc");
if (hMap == NULL || GetLastError() !=ERROR_ALREADY_EXISTS)
{
        MessageBox(L"create filemappingfail");
        return;
}