KRTSTask_0">KRTS中Task的使用
目录
- KRTS中Task的使用
- 任务
- 创建任务
- 处理任务
- 设定优先级
- 设置目标处理器并获取当前上下文信息
- 同步任务
- 创建信号量
- 请求或释放信号量
- 删除信号量
- 移除任务
- 任务状态和过渡
- 特别说明
- 示例代码
任务
多任务模块可在 内核层 和 用户层 上执行任务。在 内核层 上,任务调度是优先级控制的。因此,一个任务永远不会被优先级较低的另一个任务打断。另一方面,优先级较低的任务可能会被优先级较高的任务打断。在内核级别实现的抢占式调度,如下所述的用户级别的协作调度的限制。
在 用户层 任务通过协同调度进行管理。因此,正在运行的任务只能在同步点(如事件或信号量)处切换。另一种选择是,正在运行的任务自行处理,以便让调度程序有机会安排另一个任务。如果任务不是自行处理或在同步对象处等待,则只要任务正在运行,就无法安排其他任务。用户级任务的一个优点是可以进行调试,并且处理起来比标准 Windows 线程要容易得多。
另一种选择是将任务分配给不同的 CPU。这在 多处理器 下是受支持。任务、中断和计时器可以分配给多个 CPU 内核。使用"专用"实时模式,可以专门使用 CPU 内核,而不受 Windows 影响。因此,可以存档极高的实时功能。有关详细信息,请参阅 设置目标处理器。
这些任务由 255 个级别控制。请注意,每个级别可以执行无限数量的任务。调度器每个级别都使用循环算法运行,即任务被组织在一个优先级队列中(FIFO,先进先出)。如果任务运行到结束或被切换,则队列中具有相同优先级的下一个任务将获得执行时间。调度器会考虑每个 CPU 内核的优先级,例如,优先级较低的任务可以在特定的 CPU 内核上运行,而不会被另一个 CPU 内核上优先级较高的任务打断。
为了同步任务,提供了同步对象 信号、互斥锁 和 事件。
任务可以在请求任务信号量时继承优先级。此外,还支持动态优先级,即根据更改其他任务的优先级,可以阻止或启动任务。
与 Windows 线程相比,可以使用函数 KS_triggerTask 轻松重新计划任务。在执行的代码结束时,任务可以再次重新触发自身。
有一些有趣的 实时 功能与任务结合使用。计时器作为任务执行,多个计时器可以同时控制多个任务。调度程序通过优先级控制代码的执行。
对于定时器,不使用基础频率,即将定时器编程到下一个所需的时间点。同时,时钟和计时器保持同步 - 不会发生漂移。系统会自动为每个硬件选择最佳定时器源。
实时计时器支持高达 100 kHz 的频率,每个计时器都在每个 CPU 上独立处理。循环定时器和一次性定时器可以使用。
结合任务,_步骤编程_的概念可能很有趣。该应用程序分为几个已完成的步骤。每个步骤都分配给一个任务。如果完成一项任务,则对计时器进行编程以启动下一个任务。
实时计时器的另一个功能是在流程上调整计时器,例如将计时器周期增加少量。
最后但并非最不重要的一点是,可以同时启动多个定时器。这可以通过函数 KS_startTimerDelayed 来完成。可以提供绝对启动时间或相对启动延迟。要同时启动多个定时器,可以为多个定时器设置绝对启动时间。
创建任务
要创建任务,可以使用函数 KS_createTask。参数需要回调句柄和优先级。使用标志"KSF_DONT_START",在调用 KS_triggerTask 之前,任务不会启动。
处理任务
任务操作包括触发、挂起、恢复、等待、休眠、退出和终止。
对于任务操作,函数 KS_triggerTask、KS_suspendTask、KS_resumeTask、KS_yieldTask、KS_sleepTask, KS_exitTask 和 KS_killTask 有规定。函数"KS_triggerTask"无法中断正在运行的任务。任务将从状态 休眠 或 暂停 |休眠 触发。
此外,任务可以暂停并再次恢复。一个任务可以让位于同一优先级的另一个任务。如果不存在具有相同优先级的任务,则使用刚刚准备就绪的最高任务。
调用函数 ‘KS_sleepTask’ 调度器将在给定的时间段内延迟任务。
有两种方法可以结束任务:退出和终止。如果调用了 KS_exitTask,则任务会立即退出,但可以再次触发。函数"KS_killTask"最终会终止任务。无法再次触发该任务。
使用函数 KS_getTaskState,可以查询任务状态之一(休眠、准备、运行、等待 和 暂停)。要设置设置任务的堆栈大小。
设定优先级
函数 KS_setTaskPrio 设置任务优先级。从 1…31 开始的优先级称为空闲,从 32…255 开始的优先级称为正常。具有空闲优先级的任务可能会被Kithara系统任务中断。如果使用"专用"实时模式,并且至少为 Windows 保留一个核心,否则可以忽略此设置。
如果使用所有系统时间的高优先级任务,例如Windows无法获得最短时间,则将注册BSOD!解决这个问题的一种方法是使用空闲的优先级。不需要所有系统时间的循环任务也没问题。最后但并非最不重要的一点是,可以使用几乎没有延迟的 步骤编程 。
设置目标处理器并获取当前上下文信息
使用函数 KS_setTargetProcessor,可以设置当前线程或任务的目标处理器。调用此函数后在任务或线程内创建的每个资源(例如中断、回调、计时器、任务等)都将被分配给指定的逻辑处理器。
这样一来,计算负载甚至不能被划分为不同的任务,而且还可以分配给不同的CPU内核。此选项适用于普通 CPU 和"专用"CPU。
目标处理器的计数从第一个共享Windows CPU 开始,然后是"专用"CPU。在 Windows 中配置共享Windows CPU 和"专用"CPU 的数量(请参阅 设置专用 CPU)。
如果任务或线程正在运行,则可以使用函数 KS_getCurrentProcessor 查询其当前使用的处理器号。使用标志"KSF_NUM_PROCESSORS",可以只获取实时处理器的数量。
要获取有关当前执行上下文的信息,可以使用函数 KS_getCurrentContext。它提供结构 KSContextInformation。在当前级别旁边,将传递有关任务的信息,如任务句柄、优先级、当前逻辑处理器编号和堆栈信息。
同步任务
如果多个任务共享一个公共资源,则通常需要任务同步(读取器/写入器问题)。提供了三个同步对象:事件、互斥锁 和 信号。事件 和互斥锁已经在另一个教程中处理过。让我们看一下信号量。
信号量只能由实时任务请求。与 互斥锁 相比,信号量没有所有权,即信号量可以被任何正在运行的任务解锁。
创建信号量
可以使用函数 KS_createSemaphore 创建信号量。作为参数,需要最大计数和初始计数。最大计数指定了在信号量阻塞之前所需的请求数(要共享的资源)。最大计数必须大于 1。使用初始计数,可以设置起始值。该值必须小于或等于最大计数。
请求或释放信号量
信号量有两个操作:请求和释放。要执行这些操作,可以使用函数 KS_requestSemaphore 和 KS_releaseSemaphore。要请求信号量,需要信号量句柄和超时。超时以 100 ns 为单位给出。
如果信号量是空闲的,则内部计数器将减少 1,任务将继续运行。否则,任务将被阻塞,直到信号量再次空闲。对于请求信号量,_优先继承_的概念开始发挥作用。如果任务已请求信号量的优先级低于请求信号量的任务,则请求任务将从已请求信号量的任务中继承较高的优先级。因此,请求任务可以以继承的更高优先级运行。
要释放信号量,只需要一个信号量句柄。函数"KS_releaseSemaphore"增加内部计数器,直到达到最大计数。如果其他任务正在等待请求信号量,则优先级最高的任务将设置为 准备。
删除信号量
要删除信号量并释放其所有资源,必须使用函数 KS_removeSemaphore。
移除任务
要删除任务并释放其所有资源,必须使用函数 KS_removeTask。
必须删除使用 KS_createTask 创建的每个任务。对于用 KS_killTask 终止的任务也是如此。
任务状态和过渡
下图显示了所有任务状态(休眠、准备、运行、等待 和 暂停)以及每个函数导致的转换。
特别说明
Task 中执行的代码,最高允许使用 C++17 的标准,支持几乎所有的STL库,也包括像SML这种三方库的使用。
示例代码
核心代码:
// 设置任务专用的CPUKSError error = KS_setTargetProcessor(kernel_data_ptr_->task_cpu,KSF_NO_FLAGS);if (error != KS_OK) { KS_printK("KS_setTargetProcessor failed!\n"); }error = KS_createCallBack(&kernel_data_ptr_->task_callback_handle, TaskCallback, nullptr, KSF_DIRECT_EXEC | KSF_SAVE_FPU, 0);if (error != KS_OK) { return error; }error = KS_setTaskStackSize(0x100000, KSF_NO_FLAGS);if (error != KS_OK) { return error; }error = KS_createTask(&kernel_data_ptr_->task_handle, kernel_data_ptr_->task_callback_handle, 172, KSF_CUSTOM_STACK_SIZE);if (error != KS_OK) { return error; }
在任务回调中的代码,理论上支持所有C++17 的标准语法,标准库,以及部分第三方的库。
// 这是实时任务将运行的函数,并对执行 Task 操作。只有实时任务才应调用 Task 函数。
KSError __stdcall TaskCallback(void * /*pArgs*/, void * /*pContext*/)
{TaskHandle task_handle;// 处理循环,此循环仅在发出中止信号时停止。while (true){// 等待图像接收或停止的通知。if (kernel_data_ptr_->abort != 0) { break; }task_handle.SetCount(1);// 填充图像信息到共享内存中kernel_data_ptr_->count = task_handle.Count();KS_sleepTask(REFRESH_TIME*10*1000);}return KS_OK;
}
完整代码示例:
请查阅task_stl分支