TaskBasics示例讲解
目录
- TaskBasics示例讲解
- 结构说明
项目打开请查看【BaseFunction精讲】。
结构说明
-
TaskBasics:应用层程序,主要用于人机交互、数据显示、内核层数据交互等;
- TaskBasics.h : 数据定义
- TaskBasics.cpp:用户应用层源码
-
TaskBasics_64: 内核层程序(实时层程序),主要用于实时数据处理;
- TaskBasics.h : 数据定义
- TaskBasics_dll.cpp : 内核层源码
-
其余文件说明请查看【BaseFunction精讲】中的结构说明。
ps : 内核层中的数据、结构体需要一字节对齐,需要以MT方式构建
TaskBasics.h : 应用层与内核层共用一个头文件
/* Copyright (c) 2007-2024 by Kithara Software GmbH. All rights reserved. */
//##############################################################################################################
//
// 文件: TaskBasics.h
//
// 模块: Task 模块
//
// 描述: 用户应用程序和内核 DLL 之间共享定义的示例,展示如何使用任务模块调度实时任务。
//
// 创建人: r.gro 2007-11-26
//
//##############################################################################################################
/*=====================================================================*\
| *** 免责声明 *** |
| |
| 本代码仅是示例程序,您可以随意使用,我们不承担任何法律责任! |
| |
\*=====================================================================*/
//##############################################################################################################
//
// 目的:
//
// 此示例展示了任务模块的基本功能。
// 我们创建了三个具有不同优先级的任务,它们相互恢复和挂起。
// 每个任务都会将字符放入数据管道。
// 最后显示数据管道的内容,以展示单个任务的执行顺序。
//
//##############################################################################################################
#ifndef __SMP_TASKBASICS_H
#define __SMP_TASKBASICS_H
#include "../_KitharaSmp/_KitharaSmp.h"
//--------------------------------------------------------------------------------------------------------------
// SharedData 是一个用户定义的参数结构,用于在内核 DLL 和用户应用程序之间使用共享内存进行信息交换。
// 您可以自由定义任何与系统位大小无关的参数。
//--------------------------------------------------------------------------------------------------------------
struct SharedData {
KSHandle hKernel; // 内核操作句柄
KSHandle hTaskA; // 任务 A
KSHandle hTaskB; // 任务 B
KSHandle hTaskC; // 任务 C
KSHandle hCallbackA; // 任务 A 回调
KSHandle hCallbackB; // 任务 B 回调
KSHandle hCallbackC; // 任务 C 回调
KSHandle hPipe; // 数据传输的管道
};
#endif // __SMP_TASKBASICS_H
TaskBasics.cpp
/* Copyright (c) 2007-2024 by Kithara Software GmbH. All rights reserved. */
//##############################################################################################################
//
// 文件: TaskBasics.cpp
//
// 模块: Task 模块
//
// 描述: 用户应用程序和内核 DLL 之间共享定义的示例,展示如何使用任务模块调度实时任务。
//
// 创建人: r.gro 2007-11-26
//
//##############################################################################################################
/*=====================================================================*\
| *** 免责声明 *** |
| |
| 本代码仅是示例程序,您可以随意使用,我们不承担任何法律责任! |
| |
\*=====================================================================*/
//##############################################################################################################
//
// 目的:
//
// 此示例展示了任务模块的基本功能。
// 我们创建了三个具有不同优先级的任务,它们相互恢复和挂起。
// 每个任务都会将字符放入数据管道。
// 最后显示数据管道的内容,以展示单个任务的执行顺序。
//
//##############################################################################################################
//--------------------------------------------------------------------------------------------------------------
// 为了在主程序(用户层)和内核 DLL 之间共享数据结构的定义,我们使用了一个通用的头文件。
//--------------------------------------------------------------------------------------------------------------
#include "TaskBasics.h"
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// 别忘了输入你的序列号(6位客户编号),这是打开驱动程序所必需的。
//
// 如果你使用Demo版本,也可以使用“DEMO”代替。
// 如果你使用Beta版本,也可以使用“BETA”代替。
//
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// 如上说所,定义的客户号
const char _pCustomerNumber[] = "DEMO";
// 主程序入口
void runSample() {
outputTxt("***** Kithara example program 'TaskBasics' *****");
// 错误码定义,KSError 是 Kithara API 所有函数的返回类型,通过 【KSError】 可以查询接口的返回错误信息。
KSError ksError;
//------------------------------------------------------------------------------------------------------------
// 打开驱动程序的第一步,所有KRTS程序必须进行的操作。
// 只要该函数调用成功后,我们可以使用其他函数。如果打开失败,则无法调用其他函数。
// 此函数接受您的客户编号作为参数,其中包含 Kithara(如果适用可以为“DEMO”或“BETA”)。
//------------------------------------------------------------------------------------------------------------
ksError = KS_openDriver(
_pCustomerNumber); // 客户序列号
if (ksError != KS_OK) {
outputErr(ksError, "KS_openDriver", "Unable to open the driver!");
return;
}
//------------------------------------------------------------------------------------------------------------
// 创建共享内存
// 为实时层中的DLL和此用户层应用程序之间的通信。
//------------------------------------------------------------------------------------------------------------
KSHandle hSharedMemory;
ksError = KS_createSharedMemEx(
&hSharedMemory, // 返回创建的共享内存句柄
"", // 共享内存的名称
sizeof(SharedData), // 共享内存的大小
KSF_NO_FLAGS); // 无标记,此选项可以进行一些特殊设定
if (ksError != KS_OK) {
outputErr(ksError, "KS_createSharedMemEx", "Unable to create shared memory!");
KS_closeDriver();
return;
}
// 要访问共享内存,应用程序需要使用刚创建的共享内存的句柄来获取指向分配的共享内存的指针。
SharedData* pApp = NULL; // 自定义的共享内存结构体
ksError = KS_getSharedMemEx(
hSharedMemory, // 共享内存的句柄
(void**)&pApp, // 指向共享内存的结构的指针
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK) {
outputErr(ksError, "KS_getSharedMemEx", "Unable to map shared memory!");
KS_closeDriver();
return;
}
// 确定操作系统的位数大小以决定是加载32位还是64位内核DLL。
KSSystemInformation systemInfo; // 获取系统信息的结构体
systemInfo.structSize = sizeof(KSSystemInformation); // 不要忘记设备结构体大小
ksError = KS_getSystemInformation(
&systemInfo, // 结构体指针用于获取结构体数据
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK) {
outputErr(ksError, "KS_getSystemInformation", "Unable to get system information to distinguish bitsize!");
KS_closeDriver();
return;
}
//------------------------------------------------------------------------------------------------------------
// 想要在内核级别上使用DLL中的函数,必须加载dll,在调用里面的函数!
// 注意!加载程序必须找到DLL,因此它应该放在搜索路径的目录中!
// 因为我们想要在共享内存中传递加载的内核的句柄,所以我们不使用加载内核时的初始化函数,
// 而是在填充内核句柄和初始化内核所需的所有信息之后,显式调用初始化函数。
//------------------------------------------------------------------------------------------------------------
ksError = KS_loadKernel(
&pApp->hKernel, // 返回内核操作句柄
systemInfo.isSys64Bit ? // 根据系统位数加载内核Dll
"TaskBasics_64.dll" :
"TaskBasics_32.dll",
NULL, // 需要支持的函数名称(未使用)
NULL, // 函数参数 (未使用)
KSF_KERNEL_EXEC); // 内核空间中加载(实时层运行)
if (ksError != KS_OK) {
outputErr(ksError, "KS_loadKernel", "Unable to load DLL! Is the DLL in the search path?");
KS_closeDriver();
return;
}
//------------------------------------------------------------------------------------------------------------
// 显式调用初始化函数。
// 调用内核的【_initFunction】函数,并传递共享内存的句柄,这样内核就可以从句柄中检索到共享内存的指针,
// 并根据共享内存中存储的信息进行所有必要的资源分配。
// 更为详细的内核初始化操作可以查看内核层【_initFunction】函数
//------------------------------------------------------------------------------------------------------------
ksError = KS_execKernelFunctionEx(
pApp->hKernel, // 内核句柄
"_initFunction", // 函数名称
hSharedMemory, // 共享内存的句柄
KS_INVALID_HANDLE, // 上下文
KSF_NO_FLAGS); // 未使用
if (ksError != KS_OK) {
outputErr(ksError, "KS_execKernelFunctionEx", "Unable to initialize the kernel DLL!");
KS_closeDriver();
return;
}
// 等待内核层初始化完成
waitTime(1 * s);
outputTxt(" ");
outputTxt("The tasks completed their work.");
char pBuf[10]; // 管道中的9个字符 + 终止字符的1个
int length;
// 获取管道中的数据
ksError = KS_getPipe(
pApp->hPipe, // 管道句柄
pBuf, // 缓冲区,用于将管道数据写入
9, // 从管道获取的数据大小
&length, // 写入接收到的数据长度
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK) {
outputErr(ksError, "KS_getPipe", "Unable to read data from the pipe!");
KS_closeDriver();
return;
}
pBuf[length] = '\0'; // 分隔字符串,以供输出
outputTxt("Expected output : ABBBCBBAC");
outputTxt("Actual output : ", false);
outputTxt(pBuf);
outputTxt(" ");
//------------------------------------------------------------------------------------------------------------
// 清理内核DLL中分配的资源。
//------------------------------------------------------------------------------------------------------------
ksError = KS_execKernelFunctionEx(
pApp->hKernel, // 内核句柄
"_exitFunction", // 内核层退出函数
KS_INVALID_HANDLE, // 传参
KS_INVALID_HANDLE, // 上下文
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK) {
outputErr(ksError, "KS_execKernelFunctionEx", "Error while deallocating resources on kernel level!");
KS_closeDriver();
return;
}
//------------------------------------------------------------------------------------------------------------
// 使用共享句柄卸载内核DLL。
// 尽管KS_closeDriver()释放了所有分配的资源(如共享内存和加载的内核),
// 明确释放您分配的资源是很好的习惯。
//------------------------------------------------------------------------------------------------------------
// 释放内核
ksError = KS_freeKernel(
pApp->hKernel); // 内核句柄
if (ksError != KS_OK)
outputErr(ksError, "KS_freeKernel", "Unable to unload the kernel!");
// 清理共享内存
ksError = KS_freeSharedMemEx(
hSharedMemory, // 共享内存句柄
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK)
outputErr(ksError, "KS_freeSharedMemEx", "Unable to remove shared memory!");
// 关闭设备清理所有资源
ksError = KS_closeDriver();
if (ksError != KS_OK)
outputErr(ksError, "KS_closeDriver", "Unable to close the driver!");
waitTime(500 * ms);
outputTxt(" ");
outputTxt("End of program 'TaskBasics'.");
}
TaskBasics_dll.cpp
/* Copyright (c) 2007-2024 by Kithara Software GmbH. All rights reserved. */
//##############################################################################################################
//
// 文件: TaskBasics_dll.cpp
//
// 模块: Task 模块
//
// 描述: 用户应用程序和内核 DLL 之间共享定义的示例,展示如何使用任务模块调度实时任务。
//
// 创建人: r.gro 2007-11-26
//
//##############################################################################################################
/*=====================================================================*\
| *** 免责声明 *** |
| |
| 本代码仅是示例程序,您可以随意使用,我们不承担任何法律责任! |
| |
\*=====================================================================*/
//##############################################################################################################
//
// 目的:
//
// 此示例展示了任务模块的基本功能。
// 我们创建了三个具有不同优先级的任务,它们相互恢复和挂起。
// 每个任务都会将字符放入数据管道。
// 最后显示数据管道的内容,以展示单个任务的执行顺序。
//
//##############################################################################################################
//--------------------------------------------------------------------------------------------------------------
// 为了在主程序(用户层)和内核 DLL 之间共享数据结构的定义,我们使用了一个通用的头文件。
//--------------------------------------------------------------------------------------------------------------
#include "TaskBasics.h"
// 共享内存用于在内核层 DLL 和用户层的应用程序之间共享数据。
SharedData* _pSys = NULL;
// 前置声明,在此文件末尾定义的不同任务的3个回调函数。
KSError __stdcall callBackA(void* /*pArgs*/, void* /*pContext*/);
KSError __stdcall callBackB(void* /*pArgs*/, void* /*pContext*/);
KSError __stdcall callBackC(void* /*pArgs*/, void* /*pContext*/);
//--------------------------------------------------------------------------------------------------------------
// 这是初始化函数。
// 它在加载内核后被调用,并将共享内存的句柄作为参数传递。
//
// 注意!请记住,所有函数都应声明为 'extern "C"'!
// 否则它们的名字可能无法被加载器找到。
// 必须通过 '__declspec(dllexport)' 导出它们。
//--------------------------------------------------------------------------------------------------------------
extern "C" KSError __declspec(dllexport) __stdcall _initFunction(void* pArgs, void* /*pContext*/) {
KSError ksError;
// 共享内存的指针通过 KS_execKernelFunctionEx() 作为 'pArgs' 参数传递。
_pSys = (SharedData*)pArgs;
// 创建管道,为了从内核级别向用户级别传输数据。
ksError = KS_createPipe(
&_pSys->hPipe, // 返回一个管道操作句柄
"MyTaskBasicsPipe", // 名称
sizeof(char), // 单个数据大小,此处字符大小
9, // 数据个数
KS_INVALID_HANDLE, // 对象信号(此处未使用)
KSF_NO_FLAGS); // 无标记
if (ksError != KS_OK)
return ksError;
//------------------------------------------------------------------------------------------------------------
// 现在,我们为3个任务创建3个回调函数。
// 回调函数使用标志KSF_DIRECT_EXEC创建,以便在内核级别执行。
//------------------------------------------------------------------------------------------------------------
ksError = KS_createCallBack(
&_pSys->hCallbackA, // 返回一个回调操作句柄
callBackA, // 回调函数
NULL, // 参考参数到回调(未使用)
KSF_DIRECT_EXEC, // 在内核中执行
0); // 优先级(仅用于用户空间)
if (ksError != KS_OK)
return ksError;
ksError = KS_createCallBack(
&_pSys->hCallbackB, // 返回一个回调操作句柄
callBackB, // 回调函数
NULL, // 参考参数到回调(未使用)
KSF_DIRECT_EXEC, // 在内核中执行
0); // 优先级(仅用于用户空间)
if (ksError != KS_OK)
return ksError;
ksError = KS_createCallBack(
&_pSys->hCallbackC, // 返回一个回调操作句柄
callBackC, // 回调函数
NULL, // 参考参数到回调(未使用)
KSF_DIRECT_EXEC, // 在内核中执行
0); // 优先级(仅用于用户空间)
if (ksError != KS_OK)
return ksError;
//------------------------------------------------------------------------------------------------------------
// 创建3个任务
// 这些任务创建时带有KSF_DONT_START标志,因此它们只有在被明确触发后才会开始执行。
//------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------
// 创建优先级为200的任务A。
//------------------------------------------------------------------------------------------------------------
ksError = KS_createTask(
&_pSys->hTaskA, // 返回任务操作句柄
_pSys->hCallbackA, // 回调句柄
200, // 优先级
KSF_DONT_START); // 不立即执行
if (ksError != KS_OK)
return ksError;
//------------------------------------------------------------------------------------------------------------
// 创建优先级为250的任务B。
//------------------------------------------------------------------------------------------------------------
ksError = KS_createTask(
&_pSys->hTaskB, // 返回任务操作句柄
_pSys->hCallbackB, // 回调句柄
250, // 优先级
KSF_DONT_START); // 不立即执行
if (ksError != KS_OK)
return ksError;
//------------------------------------------------------------------------------------------------------------
// 创建优先级为150的任务C。
//------------------------------------------------------------------------------------------------------------
ksError = KS_createTask(
&_pSys->hTaskC, // 返回任务操作句柄
_pSys->hCallbackC, // 回调句柄
150, // 优先级
KSF_DONT_START); // 不立即执行
if (ksError != KS_OK)
return ksError;
//------------------------------------------------------------------------------------------------------------
// 触发任务A,进而触发其他任务。
//------------------------------------------------------------------------------------------------------------
ksError = KS_triggerTask(
_pSys->hTaskA); // 任务 A 句柄
if (ksError != KS_OK)
return ksError;
return KS_OK;
}
//--------------------------------------------------------------------------------------------------------------
// 这是清理函数,用于删除3个任务、3个回调和数据管道。
//--------------------------------------------------------------------------------------------------------------
extern "C" KSError __declspec(dllexport) __stdcall _exitFunction(void* /*pArgs*/, void* /*pContext*/) {
if (_pSys == NULL) // 共享内存未映射!
return KSERROR_FUNCTION_NOT_AVAILABLE; // _initFunction未被调用?
KSError ksError;
// 删除任务。
ksError = KS_removeTask(
_pSys->hTaskA); // 任务 A
if (ksError != KS_OK)
return ksError;
ksError = KS_removeTask(
_pSys->hTaskB); // 任务 B
if (ksError != KS_OK)
return ksError;
ksError = KS_removeTask(
_pSys->hTaskC); // 任务 C
if (ksError != KS_OK)
return ksError;
// 移除回调
ksError = KS_removeCallBack(
_pSys->hCallbackA); // 任务回调 A
if (ksError != KS_OK)
return ksError;
ksError = KS_removeCallBack(
_pSys->hCallbackB); // 任务回调 B
if (ksError != KS_OK)
return ksError;
ksError = KS_removeCallBack(
_pSys->hCallbackC); // 任务回调 C
if (ksError != KS_OK)
return ksError;
//------------------------------------------------------------------------------------------------------------
// 移除数据管道
//------------------------------------------------------------------------------------------------------------
ksError = KS_removePipe(
_pSys->hPipe); // 管道句柄
if (ksError != KS_OK)
return ksError;
return KS_OK;
}
// callBackA 是任务 A 执行的回调,它在创建后不会启动,运行优先级为 200。
KSError __stdcall callBackA(void* /*pArgs*/, void* /*pContext*/) {
const char chr = 'A';
// 向管道中填充数据
KS_putPipe(
_pSys->hPipe, // 管道句柄
&chr, // 要放入管道中的数据包地址
1, // 一项
NULL, // 传输的字节数(未使用)
KSF_NO_FLAGS); // 无标记
KS_triggerTask( // 触发执行任务 B
_pSys->hTaskB);
KS_putPipe(_pSys->hPipe, &chr, 1, NULL, 0);
return KS_OK;
}
// callBackB 是任务 B 执行的回调,它在创建后不会启动,运行优先级为 250。
KSError __stdcall callBackB(void* /*pArgs*/, void* /*pContext*/) {
const char chr = 'B';
KS_putPipe(_pSys->hPipe, &chr, 1, NULL, 0);
KS_triggerTask( // 触发执行任务 C
_pSys->hTaskC);
KS_putPipe(_pSys->hPipe, &chr, 1, NULL, 0);
KS_suspendTask( // 暂停任务 A
_pSys->hTaskA);
KS_putPipe(_pSys->hPipe, &chr, 1, NULL, 0);
KS_suspendTask( // 暂停任务 B
KS_INVALID_HANDLE); // 当前任务的处理手柄,KS_INVALID_HANDLE
KS_putPipe(_pSys->hPipe, &chr, 1, NULL, 0);
KS_resumeTask( // 继续任务 A
_pSys->hTaskA);
KS_putPipe(_pSys->hPipe, &chr, 1, NULL, 0);
return KS_OK;
}
// callBackC 是任务 C 执行的回调,它在创建后不会启动,运行优先级为 150。
KSError __stdcall callBackC(void* /*pArgs*/, void* /*pContext*/) {
const char chr = 'C';
KS_putPipe(_pSys->hPipe, &chr, 1, NULL, 0);
KS_resumeTask( // 继续任务 B
_pSys->hTaskB);
KS_putPipe(_pSys->hPipe, &chr, 1, NULL, 0);
return KS_OK;
}
//--------------------------------------------------------------------------------------------------------------
// 需要实现 DllMain 函数,该函数在 DLL 加载时不会被执行。
//
// 对于初始化,请定义一个特殊的 init 函数,并在调用 KS_loadKernel()时将其名称作为参数传递给它,
// 或者在加载内核的句柄以后在加载的 DLL 调用函数(如本例所示)时使用,
// 请不要在加载内核时执行的 init 函数,而是在加载内核后自己明确地调用它,并根据需要传递参数,如本例所示。
//--------------------------------------------------------------------------------------------------------------
#define WIN32_LEAN_AND_MEAN
#pragma pack(push, 8)
#include <windows.h>
#pragma pack(pop)
BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD reason, LPVOID pReserved) {
return TRUE;
}