PX4项目学习::(四)中间件::任务管理与调度

1、PX4模块运行方式

PX4项目采用两种方式运行:

  1. Tasks

  2. Work queue tasks

    Tasks: 模块在自己的任务中运行,具有自己的堆栈和进程优先级。
    Work queue tasks:多个任务在同一堆栈上运行,与队列中的其他模块共享相同的堆栈和工作队列线程优先级。
    

工作队列的优点是 RAM 占用更少,减少任务切换,缺点是队列任务不能休眠,也不能轮询消息。对于运行时间比较长的任务应该使用Tasks或者在一个独立的工作队列中

工作队列(work Queue)用于执行周期性任务,如传感器驱动程序。

2、PX任务启动方式

px4_task_spawn_cmd() 用来启动任务:

independent_task = px4_task_spawn_cmd(
    "commander",                    // Process name
    SCHED_DEFAULT,                  // Scheduling type (RR or FIFO)
    SCHED_PRIORITY_DEFAULT + 40,    // Scheduling priority
    3600,                           // Stack size of the new task or thread
    commander_thread_main,          // Task (or thread) main function
    (char * const *)&argv[0]        // Void pointer to pass to the new task
                                    // (here the commandline arguments).
    );

Task

使用工作队列功能需要使用任务队列的任务需要继承ModuleBase,这个类提供了队列需要的方法,其继承关系如图所示:
在这里插入图片描述

由上图我们可以知道:

  1. 任务模块通过xxx_main()函数启动,这个函数是系统执行该任务的入口函数
  2. ModuleBase类封装了任务管理所需要的基础功能,如:start、stop、status、help、info等
  3. ModuleBase通过px4_task_spawn_cmd函数启动任务这个函数封装了pthread的接口
  4. Task模块通过Run函数运行任务,这里需要一个while循环,用于周期性执行

工作队列

使用工作队列功能需要继承ScheduledWorkItem,这个类提供了队列需要的方法,其继承关系如图所示:

在这里插入图片描述

队列执行时序图:
在这里插入图片描述

PX4的任务队列调用稍微复杂一些,由上述时序图,工作队列的调度设计一个高精度定时中断hrt和一个队列管理线程WorkQueueManager,它的设计目的是提供一个高精度的周期性任务调度方法。

在hrt定时器中对插入周期任务的队列检测是否到达执行时间,如果需要运行则将需要执行的周期任务添加进WorkQueue中,在高优先级线程WorkQueueRunner检测是否有插入需要运行的任务,在该线程中执行该任务。

这样设计的优点:

  1. 使用hrt的高精度定时中断可以确保周期调度的精度,这个中断运行周期为1MHz,理论最小调度分辨率为1us,远高于使用rtos的调度分辨率,它取决于rtos的时间片轮长度,一般情况是1ms
  2. 多个工作队列共享一个WorkQueueRunner线程,因此队列任务是共享堆栈的,可以减小RAM的使用
  3. 多个工作队列在一个线程中是顺序执行的,可以减少任务切换的次数
  4. 工作队列任务时间过长可能会造成线程中其他队列任务延时,因此队列任务不能运行执行时间过长的任务

参考文献

PX4-4-任务调度_AcmeUav的博客-CSDN博客
PX4模块设计之十三:WorkQueue设计
Nuttx WorkQueue

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个驱动程序获取CmpTraceRoutine函数地址的示例代码,仅供参考。 ```cpp #include <ntddk.h> NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { UNREFERENCED_PARAMETER(RegistryPath); // 获取ntoskrnl.exe模块的基址 PVOID ntoskrnlBase = GetKernelBase(); if (ntoskrnlBase == NULL) { return STATUS_UNSUCCESSFUL; } // 获取CmpTraceRoutine函数的地址 PVOID cmpTraceRoutine = GetFunctionAddress(ntoskrnlBase, "CmpTraceRoutine"); if (cmpTraceRoutine == NULL) { return STATUS_UNSUCCESSFUL; } // 使用CmpTraceRoutine函数 // ... return STATUS_SUCCESS; } PVOID GetKernelBase() { NTSTATUS status; ULONG size = 0; PVOID base = NULL; // 获取系统信息的长度 status = ZwQuerySystemInformation(SystemModuleInformation, NULL, 0, &size); if (status != STATUS_INFO_LENGTH_MISMATCH) { return NULL; } // 分配足够大的内存缓冲区 PVOID buffer = ExAllocatePoolWithTag(NonPagedPoolNx, size, 'KDBG'); if (buffer == NULL) { return NULL; } // 获取系统信息 status = ZwQuerySystemInformation(SystemModuleInformation, buffer, size, &size); if (NT_SUCCESS(status)) { PSYSTEM_MODULE_INFORMATION modules = (PSYSTEM_MODULE_INFORMATION)buffer; base = modules->Module[0].ImageBase; } // 释放内存缓冲区 ExFreePoolWithTag(buffer, 'KDBG'); return base; } PVOID GetFunctionAddress(PVOID ModuleBase, PCHAR FunctionName) { PIMAGE_NT_HEADERS ntHeaders = RtlImageNtHeader(ModuleBase); if (ntHeaders == NULL) { return NULL; } // 获取导出表RVA和大小 PIMAGE_DATA_DIRECTORY exportDirectory = &ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; if (exportDirectory->VirtualAddress == 0 || exportDirectory->Size == 0) { return NULL; } // 获取导出表的指针 PIMAGE_EXPORT_DIRECTORY exportTable = (PIMAGE_EXPORT_DIRECTORY)((ULONG_PTR)ModuleBase + exportDirectory->VirtualAddress); // 获取导出表中函数名称的指针表和地址表 PDWORD nameTable = (PDWORD)((ULONG_PTR)ModuleBase + exportTable->AddressOfNames); PDWORD addressTable = (PDWORD)((ULONG_PTR)ModuleBase + exportTable->AddressOfFunctions); PWORD ordinalTable = (PWORD)((ULONG_PTR)ModuleBase + exportTable->AddressOfNameOrdinals); // 在导出表中查找函数名称 for (DWORD i = 0; i < exportTable->NumberOfNames; i++) { PCHAR name = (PCHAR)((ULONG_PTR)ModuleBase + nameTable[i]); if (_stricmp(name, FunctionName) == 0) { WORD ordinal = ordinalTable[i]; ULONG_PTR address = (ULONG_PTR)ModuleBase + addressTable[ordinal]; return (PVOID)address; } } return NULL; } ``` 这个驱动程序使用`ZwQuerySystemInformation`函数获取系统模块的信息,然后遍历模块列表找到`ntoskrnl.exe`模块的基址。接着,使用`RtlImageNtHeader`函数读取模块的PE头,然后在导出表中查找`CmpTraceRoutine`函数的地址。最后,使用`CmpTraceRoutine`函数执行相关操作。 请注意,这个示例代码仅供参考,如果要在实际环境中使用,请根据具体情况进行适当修改和优化,并遵循内核编程的最佳实践和安全原则,以确保操作的正确性和安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值