36、Linux 内核下的多任务处理与 JTK 库的设计实现

Linux 内核下的多任务处理与 JTK 库的设计实现

1. 任务优先级继承与 GNULLI 测试

在多任务处理中,当前任务会继承锁的优先级上限。例如,ATCB 锁具有最高的(非中断优先级)上限,这使得当前任务继承足够高的优先级,除非新优先级为中断优先级,否则不会被抢占。在这种情况下,若不是提升当前任务之外的任务优先级到中断优先级,就无需在就绪队列中移动受影响的任务或调用调度器,改变任务优先级只需简单的赋值语句。

为确保 GNULLI 内核模块实现中所有操作的功能正确性,设计了一组功能测试。之后,在 GNARL 上下文之外,通过直接调用对 GNULLI 原语进行了简单的性能测试。主要性能测试包含六个独立的周期性任务,这些任务具有谐波周期和速率单调优先级(任务周期越短,优先级越高),每个任务的截止时间等于其周期,速率分别为 320、160、80、40、20 和 10 次/秒。每个任务的模拟工作负载是重复简单赋值操作,通过改变该操作的重复次数可调整任务执行时间,使用一个名为负载水平的参数来控制这一变化,能同时线性调整所有六个任务的执行时间,且保持任务间负载的相对分布不变。从所有任务都可调度的低负载水平开始,逐渐增加负载水平并重复测试,直至达到最大负载水平,超过此值任务将不可调度,此时计算利用率极限。理论上,由于周期是谐波的,这组测试可获得 100%的利用率极限,在 90MHz 的奔腾 PC 上当前实现的结果为 97%,意味着任务的调度开销为 3%。而基于 Linux 原生线程库的 Ada 任务实现测试则失败了,即使在最低负载水平下,任务也无法满足硬实时要求,原因是 Linux 操作系统会在任意时间抢占应用任务的处理器,而内核模块 GNULLI 通过以比 Linux 操作系统内核和设备驱动更高的优先级运行实时任务,解决了这一问题。此外,还对锁和解锁原语进行了性能测试,在 90MHz 的 PC 上,当前实现中一对锁/解锁调用的平均执行时间为 1.06 微秒。

将上述低级别任务实现与 GNARL 的受限版本合并后,可编译和执行使用 Ada 任务语法编写的测试。在 166MHz 的奔腾 PC 上进行测试,测量了三种操作的平均执行时间:
| 操作 | FSU 线程 | Linux 线程 | RT - Linux 线程 |
| — | — | — | — |
| 简单受保护过程调用(无入口) | 2.3 µs | 3.1 µs | 1.1 µs |
| 受保护过程调用(有一个入口) | 3.5 µs | 4.7 µs | 1.4 µs |
| 一对受保护入口调用(类似会合) | 4.6 µs | 9.7 µs | 3.1 µs |

2. JTK 库的设计动机与目标

在将 Ada 编译器移植到新架构时,移植 Ada 任务实现是一项艰巨任务。许多编译器(如 GNAT)通过实现运行在明确定义接口之上的架构无关任务系统来简化此问题,GNAT 的这个接口称为 GNULLI(GNU 低级接口),它运行在给定架构的原生线程(通常是 POSIX 线程)之上。然而,这种方法存在一些问题,如抽象反转、Ada 和 POSIX 任务模型不匹配以及性能损失等。

因此,设计了 JTK(Jose’s Tasking Kernel)库,它提供简单的任务支持,大致基于 POSIX 线程,完全用 Ada 编写,可在传统操作系统(提供用户级线程)上使用,也可分层在裸机上。其设计动机源于以下几点:
- 使用 GNARL 在 POSIX 线程之上的负担和开销过高,导致许多抽象反转,GNAT 任务实现真正使用的 POSIX 线程功能非常有限。
- 某些线程实现的细节难以了解,可能与 GNAT 的需求冲突,如 Ada ATC(异步控制转移)在某些架构中的实现问题。
- 用 Ada 编写的库更易于理解,特别是与同样完全用 Ada 编写的 GNARL 一起使用时。
- 在每个架构上基于原生线程实现 GNAT 任务会导致可移植性问题,移植工作需要花费大量精力去理解线程实现的工作原理。
- 某些嵌入式系统中可能没有易于使用的线程支持,实现完整的线程功能成本过高。

JTK 的主要目标包括:
- 避免不必要的功能,以提高 GNARL 的性能和可预测性。
- 针对 GNAT 任务系统的特定使用提供所需功能,由于 GNARL 承担了任务处理的大部分工作,JTK 的实现可以非常简单。
- 通过使用已知且简单的低级任务层,更轻松地界定运行时执行时间。
- 获得易于理解的代码。
- 增强库的可移植性。
- 减少内核空间线程实现所需的耗时系统调用数量。

3. JTK 库的内部架构

JTK 处理管理低级任务所需的所有内部信息,库代码的关键部分称为内核。它使用单线程内核,在内核中,低级任务使用的结构必须在处理异步事件(信号)时防止被不一致地修改,采用的技术是整体监视器,通过一个标志来指示内核是否正在使用(某些 POSIX 线程实现也使用此技术)。如果在使用内核时收到信号,信号管理将延迟到内核释放。

JTK 提供基于优先级驱动的调度器,具有抢占功能,每个优先级都有一个 FIFO 队列来决定下一个执行的任务。它还提供两种基本同步方法:互斥和条件同步。互斥方面,提供了两个互斥对象,一个作为简单的二进制信号量操作,另一个使用即时优先级上限协议,提供读写锁定的可能性。条件同步方面,JTK 实现了条件变量,供定时器和受保护对象使用。

目前 JTK 实现了两种调度策略:
- 简单调度:任务运行直到被阻塞或有更高优先级的任务准备好。
- 轮转(抢占式)调度:每个任务有一个关联的时间片,如果有多个相同优先级的任务,每个任务仅在其对应的时间片内运行。这两种使用库的方式分别实现,以避免在不使用轮转支持的基本调度器时产生过多开销,实时系统通常不使用轮转调度,可绕过额外处理。低级任务和调度器被定义为带标签的对象,便于用新的调度策略扩展实现。

graph TD;
    A[JTK内核] --> B[单线程内核];
    B --> C[整体监视器];
    C --> D[信号延迟处理];
    A --> E[优先级驱动调度器];
    E --> F[FIFO队列];
    A --> G[同步方法];
    G --> H[互斥];
    G --> I[条件同步];
    H --> J[二进制信号量];
    H --> K[即时优先级上限协议];
    I --> L[条件变量];
    E --> M[简单调度];
    E --> N[轮转调度];
4. JTK 库的实现细节 - 定时器支持

JTK 与 POSIX 线程实现在某些方面有很大差异,这受 GNAT 语义的影响。JTK 实现的低级任务旨在成为轻量级实体,抽象级别不与 Ada 任务重叠,避免了其他任务实现带来的开销和语义限制。

以定时器支持为例,GNARL 和 POSIX 线程都提供定时器及相关处理设施。GNARL 的一个重要方面是它可以被修改为一次只发出一个定时器请求(使用定时器服务器任务),因此不需要底层库考虑多个同时请求。而 POSIX 线程支持多个定时器请求,需要管理等待列表和处理重叠请求,这会带来很大的开销,且这些重叠定时器 GNARL 永远不会使用。JTK 知道这项工作由 GNARL 完成,因此不再实现。GNARL 负责对任务的不同请求进行排序,并向操作系统或底层内核发出请求,所以不需要请求同时的定时器操作。JTK 库的最终目的是分层在专门设计以满足其需求的内核之上,这样内核无需为低级时间管理提供自己的队列,实现得到了简化。

5. JTK 库在裸机上的实时内核设计

为了能在裸机 PC 上使用 GNAT 和 JTK,正在开发一个新的实时内核。这个内核的设计充分考虑了 JTK 的特性和需求,旨在为 JTK 提供一个高效、稳定的运行环境。

在这个实时内核中,主要关注以下几个方面:
- 资源管理 :对裸机的硬件资源进行有效的管理,包括内存、CPU 时间片等。合理分配资源,确保各个任务能够正常运行,避免资源竞争和冲突。
- 中断处理 :设计高效的中断处理机制,及时响应硬件中断。当中断发生时,能够快速保存当前任务的上下文,处理中断事件,然后恢复任务的执行。
- 任务调度 :结合 JTK 的优先级驱动调度器,实现更加灵活和高效的任务调度。根据任务的优先级和状态,合理安排任务的执行顺序,确保高优先级任务能够及时得到执行。

以下是实时内核设计的主要流程:
1. 硬件初始化 :对裸机的硬件进行初始化,包括设置时钟、内存映射等。
2. 内核初始化 :初始化实时内核的各个模块,如任务管理、资源管理、中断处理等。
3. JTK 集成 :将 JTK 库集成到实时内核中,确保 JTK 能够正常运行。
4. 任务创建 :创建需要运行的任务,并将其加入到任务调度队列中。
5. 任务调度 :根据任务的优先级和状态,进行任务调度,确保任务能够按照预期执行。

graph TD;
    A[硬件初始化] --> B[内核初始化];
    B --> C[JTK集成];
    C --> D[任务创建];
    D --> E[任务调度];
6. JTK 库的性能结果

为了验证 JTK 库的可行性和性能,进行了一系列的性能测试。测试结果表明,JTK 库在性能方面表现出色,能够满足 GNARL 的需求。

在之前提到的使用 Ada 任务语法编写的测试中,RT - Linux 线程在三种操作的平均执行时间上都优于 FSU 线程和 Linux 线程,这显示了 JTK 库在性能上的优势。具体数据如下表所示:
| 操作 | FSU 线程 | Linux 线程 | RT - Linux 线程 |
| — | — | — | — |
| 简单受保护过程调用(无入口) | 2.3 µs | 3.1 µs | 1.1 µs |
| 受保护过程调用(有一个入口) | 3.5 µs | 4.7 µs | 1.4 µs |
| 一对受保护入口调用(类似会合) | 4.6 µs | 9.7 µs | 3.1 µs |

另外,在对锁和解锁原语的性能测试中,在 90MHz 的 PC 上,当前实现中一对锁/解锁调用的平均执行时间为 1.06 微秒,这也表明 JTK 库在同步操作方面具有较高的效率。

这些性能结果证明了 JTK 库在提高 GNARL 性能和可预测性方面的有效性,为其在实际应用中的推广提供了有力支持。

7. JTK 库的实现状态与可用性

目前,JTK 库已经完成了基本的实现,提供了优先级驱动的线程模型、信号处理设施、互斥和同步等待原语等功能。其长期目标是提供所有 GNAT 需要的设施,以满足完整的 Annex D 语义。

JTK 库的代码易于理解和扩展,通过将低级任务和调度器定义为带标签的对象,可以很方便地添加新的调度策略。同时,由于其使用 Ada 编写,与 GNARL 的集成更加自然和高效。

关于 JTK 库的可用性,它可以在传统操作系统(提供用户级线程)上使用,也可以分层在裸机上。这使得它具有更广泛的应用场景,无论是在嵌入式系统还是其他实时系统中,都能够发挥其优势。

总的来说,JTK 库为 GNAT 运行时系统提供了一种新的低级别任务支持方案,通过避免不必要的功能、提高性能和可预测性、增强可移植性等方面的优势,有望在实时系统开发中得到更广泛的应用。

内容概要:本文围绕六自由度机械臂的人工神经网络(ANN)设计展开,重点研究了正向逆向运动学求解、正向动力学控制以及基于拉格朗日-欧拉法推导逆向动力学方程,并通过Matlab代码实现相关算法。文章结合理论推导仿真实践,利用人工神经网络对复杂的非线性关系进行建模逼近,提升机械臂运动控制的精度效率。同时涵盖了路径规划中的RRT算法B样条优化方法,形成从运动学到动力学再到轨迹优化的完整技术链条。; 适合人群:具备一定机器人学、自动控制理论基础,熟悉Matlab编程,从事智能控制、机器人控制、运动学六自由度机械臂ANN人工神经网络设计:正向逆向运动学求解、正向动力学控制、拉格朗日-欧拉法推导逆向动力学方程(Matlab代码实现)建模等相关方向的研究生、科研人员及工程技术人员。; 使用场景及目标:①掌握机械臂正/逆运动学的数学建模ANN求解方法;②理解拉格朗日-欧拉法在动力学建模中的应用;③实现基于神经网络的动力学补偿高精度轨迹跟踪控制;④结合RRTB样条完成平滑路径规划优化。; 阅读建议:建议读者结合Matlab代码动手实践,先从运动学建模入手,逐步深入动力学分析神经网络训练,注重理论推导仿真实验的结合,以充分理解机械臂控制系统的设计流程优化策略。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值