基础知识:
进程:进程是内核分配资源的基本单位。每个进程都有自己的地址空间,其中用户区一般包括文本区,数据区,堆栈区。
线程:线程是内核调度的基本单位。
一个进程可以有多个线程,而且有且仅有一个主线程。这些线程都是使用进程的地址空间以及资源。所以同一个进程中的不同线程间主要是注意互斥问题。而不同进程间由于地址空间的不用,又如何共享的数据或者资源呢,又或者如何使用其他进程的数据或者资源呢?
不同进程的虚拟地址空间:
对于32位的机器,每个程序都会有4G的虚拟地址空间,从低地址到高地址依次是:NULL区,用户区,隔离区,核心区。用户私有的数据都在用户区。进程实际可以得到的物理内存要远小于其虚拟地址空间。进程的虚拟地址空间是为每个进程所私有的,在进程内运行的线程对内存空间的访问都被限制在调用进程之内,而不能访问属于其他进程的内存空间。这样,在不同的进程中可以使用相同地址的指针来指向属于各自调用进程的内容而不会由此引起混乱。
http://www.blogjava.net/bacoo/archive/2008/11/20/241586.html关于虚拟地址空间,这个blog已经写得非常详细了,可以参考下。相关的书籍是<Windows 核心编程>
不同进程间的数据共享(一下的主要内容是参考http://blog.sina.com.cn/s/blog_51396f890100o3bx.html):
方法一:使用内存映射文件。
使用函数CreateFileMapping和MapViewOfFile,CreateFileMapping创建一个新的文件映射内核对象。MapViewOfFile将一个文件映射对象映射到当前应用程序的地址空间。具体的例子如下:
下面说明创建一个名为MySharedMem的长度为4096字节的有名映射文件:
HANDLE hMySharedMapFile=CreateFileMapping((HANDLE)0xFFFFFFFF),
NULL,PAGE_READWRITE,0,0x1000,"MySharedMem");
并映射缓存区视图:
LPSTR pszMySharedMapView=(LPSTR)MapViewOfFile(hMySharedMapFile,
FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);
其他进程访问共享对象,需要获得对象名并调用OpenFileMapping函数。
HANDLE hMySharedMapFile=OpenFileMapping(FILE_MAP_WRITE,
FALSE,"MySharedMem");
一旦其他进程获得映射对象的句柄,可以象创建进程那样调用MapViewOfFile函数来映射对象视图。用户可以使用该对象视图来进行数据读写操作,以达到数据通讯的目的。
当用户进程结束使用共享内存后,调用UnmapViewOfFile函数以取消其地址空间内的视图:
if (!UnmapViewOfFile(pszMySharedMapView))
{ AfxMessageBox("could not unmap view of file"); }
不过,通过约定字符串的方法似乎不太优雅。
一个优雅的方法是,用 DuplicateHandle 在新进程中复制一份 FileMapping 对象出来,然后想办法把 Handle 通知新进程,比如用消息的方式传递过去。
如果需要共享内存的两个进程是父子关系,那么我们可以不用消息传递的方式来通知 FileMapping 的 Handle 。父进程可以用继承 Handle 的方式直接把 FileMapping 的
Handle 传递到子进程中。当然,在 CreateFileMapping 时就应该设置可以被继承的属性。
大约是这样:
SECURITY_ATTRIBUTES sa;
sa.nLength=sizeof(sa);
sa.lpSecurityDescriptor=NULL;
sa.bInheritHandle=TRUE;
handle=CreateFileMapping(INVALID_HANDLE_VALUE,&sa,PAGE_READWRITE,0,size,NULL);
这样,在 CreateProcess 的时候,如果 bInheritHandles 参数为 TRUE ,所有有可被继承属性的内核对象都会被复制到子进程中。
方法二:利用共享内存DLL
共享数据DLL允许进程以类似于Windows 3.1 DLL共享数据的方式访问读写数据,多个进程都可以对该共享数据DLL进行数据操作,达到共享数据的目的。
在WIN32中为建立共享内存,必须执行以下步骤:
首先创建一个有名的数据区。这在Visual C++中是使用data_seg pragma宏。使用data_seg pragma宏必须注意数据的初始化:
#pragma data_seg("MYSEC")
char MySharedData[4096]={0};
#pragma data_seg()
然后在用户的DEF文件中为有名的数据区设定共享属性。
LIBRARY TEST
DATA READ WRITE
SECTIONS
.MYSEC READ WRITE SHARED
这样每个附属于DLL的进程都将接受到属于自己的数据拷贝,一个进程的数据变化并不会反映到其他进程的数据中。
在DEF文件中适当地输出数据。以下的DEF文件项说明了如何以常数变量的形式输出MySharedData。
EXPORTS
MySharedData CONSTANT
最后在应用程序(进程)按外部变量引用共享数据。
extern _export"C"{char * MySharedData[];}
进程中使用该变量应注意间接引用。
m_pStatic=(CEdit*)GetDlgItem(IDC_SHARED);
m_pStatic->GetLine(0,*MySharedData,80);
方法三:用于传输只读数据的WM_COPYDATA
SendMessage(hwnd,WM_COPYDATA,wParam,lParam);
方法四:直接调用ReadProcessMemory和WriteProcessMemory函数实现进程间通讯
通过调用ReadProcessMemory以及WriteProcessMemory函数用户可以按类似与Windows3.1的方法实现进程间通讯,在发送进程中分配一块内存存放 数据,可以调用GlobalAlloc或者VirtualAlloc函数实现:
pApp->m_hGlobalHandle=GlobalAlloc(GMEM_SHARE,1024);
可以得到指针地址:
pApp->mpszGlobalHandlePtr=(LPSTR)GlobalLock
(pApp->m_hGlobalHandle);
在接收进程中要用到用户希望影响的进程的打开句柄。为了读写另一进程,应按如下方式调用OpenProcess函数:
HANDLE hTargetProcess=OpenProcess(
STANDARD_RIGHTS_REQUIRED|
PROCESS_VM_REDA|
PROCESS_VM_WRITE|
PROCESS_VM_OPERATION,//访问权限
FALSE,//继承关系
dwProcessID);//进程ID
为保证OpenProcess函数调用成功,用户所影响的进程必须由上述标志创建。
一旦用户获得一个进程的有效句柄,就可以调用ReadProcessMemory函数读取该进程的内存:
BOOL ReadProcessMemory(
HANDLE hProcess, // 进程指针
LPCVOID lpBaseAddress, // 数据块的首地址
LPVOID lpBuffer, // 读取数据所需缓冲区
DWORD cbRead, // 要读取的字节数
LPDWORD lpNumberOfBytesRead
);
使用同样的句柄也可以写入该进程的内存:
BOOL WriteProcessMemory(
HANDLE hProcess, // 进程指针
LPVOID lpBaseAddress, // 要写入的首地址
LPVOID lpBuffer, // 缓冲区地址
DWORD cbWrite, // 要写的字节数
LPDWORD lpNumberOfBytesWritten
);
如下所示是读写另一进程的共享内存中的数据:
ReadProcessMemory((HANDLE)hTargetProcess,
(LPSTR)lpsz,m_strGlobal.GetBuffer(_MAX_FIELD),
_MAX_FIELD,&cb);
WriteProcessMemory((HANDLE)hTargetProcess,
(LPSTR)lpsz,(LPSTR)STARS,
m_strGlobal.GetLength(),&cb);