应用程序调用流驱动程序的变量地址转换
因为每个应用程序都会占用一个Slot,所以他们的全局变量的地址不会发生冲突。
例如:一个应用程序(PJ1.exe)被加载到Slot8的位置即地址范围为:0x10000000-0x11ffffff,该程序的CODE段与DATA段等都加载到该位置。程序定义一个全局变量时存储地址也在该范围内。若定义一个全局数组UCHAR Buff[100];假设该数组存储的地址范围为0x10010000-0x10010063。
不同的进程被加载到不同的Slot,设备驱动程序device.exe也被加载到一个Slot3,地址范围为0x06000000-0x07ffffff。所以被设备驱动程序加载的流驱动(通常为DLL文件)也被加载到该地址范围。
因为CE将当前正在运行的程序都映射为Slot0,所以应用程序运行时的地址范围为0x00000000-0x01ffffff。则Buff数组存储的地址范围为0x00010000-0x00010063。所以应用程序在运行时变量地址读取和写入都是在Slot的范围内。同理,device.exe其运行地址也为0x00000000-0x01ffffff。
下面说一下当应用程序通过文件系统流驱动接口访问device.exe的接口时怎样传递数据。程序只是说明变量地址的变换,语法可能不正确。
// 应用程序
void fun (void)
{
UCHAR Buff[100]; // 加载地址0x10010000-0x10010063,运行地址0x00010000-0x00010063
DeviceIoControl(L"IIC0",...,Buff,...); // Buff为保存从驱动程序返回的数据,传递地址为运行地址0x00010000。
...
}
// 驱动程序
BOOL IIC_IoControl (...,PUCHAR pUCHAR,...) // 经过操作系统处理后,pUCHAR指针存储的地址为应用程序Buff加载地址即0x10010000。
{
UCHAR iic_rece[100]; // 加载地址0x06010000-0x06010063,运行地址0x00010000-0x00010063
...
memcpy(pUCHAR,iic_rece,100); // 所以驱动程序可以改变应用程序中变量的值。
...
}
也就是说,当应用程序传递给驱动程序为指针时,系统会自动转换应用程序变量的地址。而当应用程序传递给驱动程序为指向指针的变量时,驱动程序必须自己转换得到的地址到应用程序加载地址空间。
举例:
type struct REDATA {
PUCHAR pbuff;
} ReData;
// 应用程序
void fun (void)
{
UCHAR Buff[100]; // 加载地址0x10010000-0x10010063,运行地址0x00010000-0x00010063
ReData ReStruct; // ReStruct加载地址0x0x10020000,运行地址0x00020000
ReData.pbuff = Buff; // pbuff存储的地址为0x00010000
DeviceIoControl(L"IIC0",...,&ReStruct,...); // ReStruct为保存从驱动程序返回的数据,传递地址为运行地址0x00020000。
...
}
// 驱动程序
BOOL IIC_IoControl (...,ReData *pStruct,...) // 经过操作系统处理后,pStruct指针存储的地址为应用程序ReStruct加载地址即0x0x10020000。
{
UCHAR iic_rece[100]; // 加载地址0x06010000-0x06010063,运行地址0x00010000-0x00010063
...
memcpy(pStruct->pbuff,iic_rece,100); // 程序出错,因为pStruct->pbuff存储的地址为应用程序Buff的运行地址0x00010000,所以改变的是驱动程序的地址。
// 这时驱动程序可以调用MapPtrToProcess函数将pStruct->pbuff的地址修改为应用程序变量Buff的加载地址。
pStruct->pbuff = MapPtrToProcess(pStruct->pbuff,GetCallerProcess());
// 这是pStruct->pbuff存储的地址为应用程序Buff的加载地址0x10010000,可以执行memcpy函数。
...
}