你需要知道关于KRTS共享内存的知识
在 32 位和 64 位 Windows 操作系统中,将使用所谓的虚拟内存管理。一方面,通过这种机制,可以确保保护不同的地址空间免受未经授权的访问。另一方面,对于每个应用程序,都提供了 2 GB 的逻辑地址空间(32 位)或 8 TB(64 位)。
应用程序中用于数据和代码的地址始终是虚拟地址。它们将通过操作系统的内存管理转换为物理内存地址。
由于每个应用程序都提供了一个单独的地址空间,因此一个进程通常不可能访问另一个进程的内存。此外,您通常无法从应用程序访问内核内存,因为内核级别的内存受到保护。
为了实现快速数据交换,必须单独请求用于多个应用程序之间以及应用程序与内核级别之间数据交换的内存区域。基础模块中的共享内存功能就用于此目的。
硬盘重定位通常不实时: 尽管 Windows API 包含提供共享内存的机制,但通常不会针对硬盘上的重定位提供保护。因此,通常无法实时存储测量值。
此外,普通的共享内存(“内存映射文件”)只能从应用程序级别访问,可用于不同应用程序之间的数据交换。它不适合在内核级别使用。Kithara RealTime Suite提供的共享内存不受此限制。
内核层和应用层的不同虚拟地址
在内存调用中,必须使用来自应用程序级别(ring 3)或内核级别(ring 0)访问的不同地址。这种区别是必要的,因为应用程序地址范围和系统地址范围并不相同。因此,用于共享内存的 KRTS函数一次提供两个不同的地址
-
pAppPtr 应用程序级别(ring 3)上下文的指针和
-
pSysPtr 内核级别(ring 0)上下文的指针。
注意: 请确保始终使用地址空间提供的地址的指针,否则将提示“访问违规”或蓝屏死机!!!
两个虚拟地址都将作为“void”指针传输。在类型转换为用户特定的数据类型后,它们才能被使用,就像“普通”指针。因此,在共享内存中可以非常容易和快速地读取或写入。
管理共享内存
在内核模式下,只能访问位于堆栈(局部变量)或所谓的共享内存中的数据。对于后面的引用参数 _pArgs_将运行在内核级的回调函数,以提供共享内存指针。
确定名称标识内存
通常,共享内存将通过特定名称进行标识。原理是,需要具有已知和商定名称的此类存储器。一旦再次以商定的名称请求此内存,您就会收到返回同一内存区域的指针。当然,地址在不同的应用程序中是不同的!多个应用程序在同一内存上创建自己的“视图”。
注意: 所述功能提供的内存受到保护,不会发生硬盘重定位(“交换”)!
创建共享内存
将使用函数 KS_createSharedMem 创建共享内存。必须给出此内存大小,名称是可选的。 _pAppPtr_为应用程序级别(ring 3)上下文提供,_pSysPtr_为内核级别(ring 0)上下文提供。
Data* pAppPtr; // 用于从应用程序访问
Data* pSysPtr; // 用于从内核访问
Error error = KS_createSharedMem(
(void**)&pAppPtr, // 应用指针地址
(void**)&pSysPtr, // 系统指针地址
“SharedMemoryName”, // 共享内存名称
sizeof(Data), // 共享内存大小
0); // 标志
if(error)
// Todo
若要释放共享内存,可以使用函数 KS_freeSharedMem。
对于基于句柄的版本,可以使用函数 KS_createSharedMemEx 创建共享内存。
KSHandle hSharedMem; // 共享内存的句柄
Error error = KS_createSharedMemEx(
&hSharedMem, // 共享内存句柄的地址
“SharedMemoryName”, // 共享内存名称
sizeof(Data), // 共享内存大小
0); // 标志
if(error)
{
// Todo
error = KS_getSharedMemEx(
hSharedMem, // 共享内存句柄
&pSysPtr, // 系统指针的地址
KSF_KERNEL_EXEC); // 标志,这里是 KSF_KERNEL_EXEC
// 系统指针的地址
if(error)
{// Todo
}
释放句柄 hSharedMem 可用函数 KS_freeSharedMemEx ,也可用函数 KS_getSharedMemEx 获取的实际内存指针。
Windows 服务创建的共享内存
如果:
- 您打算创建必须由多个应用程序访问的共享内存,
- 您打算创建必须比应用程序持续时间更长的共享内存,
则建议在 Windows 服务中创建共享内存。
注意: 由 Windows 服务创建的共享内存必须由具有不同名称:
// 在服务中:
Data* pAppPtr; // 用于从应用程序访问
Data* pSysPtr; // 用于从内核访问
Error error = KS_createSharedMem(
(void**)&pAppPtr, // 应用指针地址
(void**)&pSysPtr, // 系统指针地址
“SharedMemoryName”, // 共享内存名称
sizeof(Data), // 共享内存大小
0); // 标志
if(error)
// Todo
// 在应用程序中:
Data* pAppPtr; // 用于从应用程序访问
Data* pSysPtr; // 用于从内核访问
Error error = KS_createSharedMem(
(void**)&pAppPtr, // 应用指针地址
(void**)&pSysPtr, // 系统指针地址
“Global\\SharedMemoryName”, // 在共享内存名称前加上 "Global\
sizeof(Data), // 共享内存大小
0); // 标志
if(error)
// Todo
多个应用程序中的共享内存
共享内存不仅可用于应用程序级和内核级之间的数据交换,还可用于多个应用程序之间的数据交换。在这种情况下,每个应用程序必须至少调用一次函数 KS_createSharedMemEx (KS_getSharedMemEx 是不够的!您可以确定共享内存是否已使用,并利用内存通常以 0 初始化的优势。另一种可能的方法是使用 count 变量,该变量将在每个“KS_createSharedMemEx”递增,并显示正在运行的应用程序的数量。使用共享内存后,每个应用程序都必须调用 KS_freeSharedMemEx。
项目实例
书接上回,我们只需要对上次学习的程序稍加修改就能感受的共享内存的魅力
目录结构
SharedData.h
#pragma once
#include <KrtsDemo.h>
// 内核层数据需要1字节对齐
#pragma pack(1)
typedef struct SharedData {
uint data;
}SharedData;
#pragma pack()
KitharaDemo.cpp
#include <iostream>
#include "../KitharaDll/SharedData.h"
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
const char customerNumber[256] = "DEMO";
SharedData* app_data_ptr_ = nullptr; // 应用层共享内存指针
SharedData* sys_data_ptr_ = nullptr; // 内核层共享内存指针
// 应用层不能使用内核层指针!!!
void OutputErr(KSError error, const char* pFuncName, const char* pComment)
{
if (error == KS_OK)
return;
const char* pError;
KS_getErrorString(error, &pError, KSLNG_DEFAULT);
printf("ERROR (%08X = \'%s\') - %s: %s\n", error, pError, pFuncName, pComment);
}
int main()
{
KSError error;
error = KS_openDriver(customerNumber); // 所有Kithar项目的第一步
if (error != KS_OK)
{
error = 1;
OutputErr(error, "KS_openDriver", "Unable to open the driver!");
}
else
{
printf("Hello Kithara! \n");
}
// 创建共享内存
error = KS_createSharedMem(reinterpret_cast<void**>(&app_data_ptr_), reinterpret_cast<void**>(&sys_data_ptr_), "KitharaDemo", sizeof(SharedData), 0);
if (error != KS_OK)
{
OutputErr(error, "KS_createSharedMem", "failed to allocate shared memory");
KS_closeDriver();
return 0;
}
// 加载内核DLL
KSHandle kermel_handle;
error = KS_loadKernel(&kermel_handle, "KitharaDll.dll", "_initFunction", sys_data_ptr_, KSF_KERNEL_EXEC | KSF_SAVE_FPU);
if (error != KS_OK)
{
OutputErr(error, "KS_loadKernel", "load dll failed!");
KS_freeKernel(kermel_handle);
KS_freeSharedMem(app_data_ptr_);
KS_closeDriver();
return 0;
}
app_data_ptr_->data = 42;
error = KS_execKernelFunction(kermel_handle, "_getGlobalCounter", NULL, NULL, KSF_NO_CONTEXT);
if (error != KS_OK)
{
OutputErr(error, "KS_execKernelFunction", "get args failed");
KS_freeKernel(kermel_handle);
KS_freeSharedMem(app_data_ptr_);
KS_closeDriver();
return 0;
}
printf("this is kernel args: %d \n", app_data_ptr_->data);
// 资源内核
error = KS_freeKernel(kermel_handle);
if (error != KS_OK)
{
OutputErr(error, "KS_freeKernel", "");
}
// 资源共享内存
error = KS_freeSharedMem(app_data_ptr_);
if (error != KS_OK)
{
OutputErr(error, "KS_freeSharedMem", "");
KS_closeDriver();
return 0;
}
// 关闭驱动
error = KS_closeDriver();
if (error != KS_OK)
{
OutputErr(error, "KS_closeDriver", "");
KS_closeDriver();
return 0;
}
Sleep(1000);
return 0;
}
KitharaDll.cpp
#include "SharedData.h"
#include <cstdint>
SharedData* sys_data_ptr_ = nullptr;
// 初始化函数
extern "C" __declspec(dllexport) KSError __stdcall _initFunction(void* pArgs) {
sys_data_ptr_ = (SharedData*)pArgs;
return KS_OK;
}
// 访问内核 DLL 的全局值
extern "C" __declspec(dllexport) KSError __stdcall _getGlobalCounter(void* pArgs) {
sys_data_ptr_->data = 606;
return KS_OK;
}
#define WIN32_LEAN_AND_MEAN
// Windows 头文件
#include <windows.h>
BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD reason, LPVOID pReserved) {
return TRUE;
}
更多示例可查看:
smp\EtherCATBasics