3、任务划分原则:时间触发与事件触发任务划分、安全关键任务与非安全任务隔离、基于数据流依赖的任务分区策略

好,咱们进入第三个核心话题——任务划分。说实话,这部分内容我当年刚接触多核制动控制时,踩过不少坑。任务怎么切、怎么分,直接决定了系统稳不稳、安不安全。

我个人的习惯是,拿到一个制动控制需求,先别急着写代码。先画一张任务关系图。把时间触发、事件触发、安全关键、非安全任务,还有数据流依赖,统统理清楚。这一步做好了,后面事半功倍。

3.1 时间触发与事件触发任务划分

先说说时间触发和事件触发。这两种任务,说白了就是「定时干活」和「有事才干」。

时间触发任务,我习惯叫它「心跳任务」。比如制动压力闭环控制,每1ms必须执行一次。这种任务不能早也不能晚,必须准时。为什么?因为制动系统的控制周期是固定的,你早1ms或者晚1ms,控制效果都会变差。我在做某个量产项目时,就遇到过因为时间触发任务被其他任务抢占,导致控制周期抖动,结果制动踏板感觉忽软忽硬。嗯,那感觉真不好。

事件触发任务,则是「有信号来了再干活」。比如CAN总线收到一条制动请求报文,或者某个传感器检测到轮速异常。这类任务不需要固定周期,但要求响应快。你想想看,如果轮速传感器检测到抱死趋势,你还要等下一个1ms周期才去处理,那ABS就来不及了。

划分原则其实很简单:

  • 固定周期、确定性要求高的 → 时间触发
  • 突发性、响应要求快的 → 事件触发

但这里有个坑。我曾经在一个项目里,把所有的任务都设计成时间触发。结果呢?CPU负载率飙升到85%,而且很多事件来了之后,要等到下一个周期才能处理,响应延迟很大。后来我改成混合模式——核心控制用时间触发,故障诊断和通信用事件触发,负载率降到了45%,响应也快了。

核心要点:时间触发任务要独占一个核或高优先级,事件触发任务要设置合理的优先级和中断屏蔽策略。千万别让事件触发任务把时间触发任务的周期打乱。

3.2 安全关键任务与非安全任务隔离

这一条,我把它叫做「生死隔离」。安全关键任务,比如制动压力控制、轮速计算、ABS/ESC逻辑,这些任务一旦出错,车就停不下来。非安全任务,比如诊断日志记录、OTA升级、用户界面交互,这些任务出错了,最多是功能不可用,不会要命。

隔离怎么做?我建议分三个层面:

  1. 核隔离:安全关键任务独占一个或多个核。非安全任务跑在另外的核上。我在一个项目中,把安全关键任务放在Core0和Core1,非安全任务放在Core2和Core3。这样即使非安全任务崩溃了,也不会影响制动控制。
  2. 内存隔离:安全关键任务使用受保护的内存区域,非安全任务不能访问。MPU(内存保护单元)一定要用起来。我曾经见过一个案例,非安全任务的一个野指针,把安全关键任务的变量给覆盖了,结果制动压力计算错误。还好是在台架上发现的,要是上路就麻烦了。
  3. 时间隔离:安全关键任务的时间片不能被非安全任务抢占。使用时间分区调度,比如ARINC 653那种方式。每个分区有固定的时间窗口,安全关键分区的时间窗口是硬保证的。

警告:千万不要在安全关键任务中调用非安全任务的函数,或者使用非安全任务分配的内存。我曾经见过一个工程师,为了省事,在安全关键任务里直接调用了诊断模块的打印函数。结果诊断模块的内存被破坏了,打印函数崩溃,连带安全关键任务也挂了。这种「省事」是要命的。

3.3 基于数据流依赖的任务分区策略

这一条,我把它叫做「谁依赖谁,谁就挨着谁」。数据流依赖,说白了就是任务A的输出是任务B的输入。如果A和B不在同一个核上,那通信开销就大了。如果A和B在同一个核上,那可以通过共享内存快速传递数据。

我个人的经验是,先画出数据流图。比如制动控制系统中:

  • 轮速传感器采集任务 → 轮速计算任务 → ABS控制任务 → 制动压力控制任务
  • 制动踏板传感器采集任务 → 制动意图识别任务 → ESC控制任务 → 制动压力控制任务

你看,制动压力控制任务被多个上游任务依赖。那它应该和谁放在一起?我建议把数据流密集的、通信频繁的任务放在同一个核上。比如轮速采集、轮速计算、ABS控制这三个任务,数据流是串行的,放在同一个核上,用共享内存传递数据,延迟最小。

但这里有个权衡。如果所有依赖链上的任务都放在同一个核上,那这个核的负载率会很高。我做过一个项目,把整个制动控制链都放在Core0上,结果Core0负载率到了90%,而Core1只有20%。后来我把一些计算量大的非安全任务挪到Core1上,Core0的负载率降到了60%,整体性能反而更好了。

小技巧:我建议用「数据流依赖矩阵」来分析。把每个任务作为行和列,如果任务A的输出是任务B的输入,就在(A,B)位置标记为1。然后统计每个任务的总依赖数。依赖数高的任务,尽量放在同一个核上。依赖数低的,可以分散到不同核上。

举个例子,假设我们有5个任务:

任务 依赖数 建议核
轮速采集 3 Core0
轮速计算 3 Core0
ABS控制 4 Core0
制动压力控制 5 Core0
诊断日志 1 Core1

你看,前四个任务依赖数高,放在Core0上,通信效率高。诊断日志依赖数低,放在Core1上,不影响核心控制。

最后总结一下。任务划分这件事,没有标准答案。但记住三条原则:时间触发和事件触发要分清、安全关键和非安全要隔离、数据流依赖要就近。这三条做到了,你的多核制动控制系统就成功了一大半。

嗯,今天就聊到这里。下一章咱们聊聊任务优先级和调度策略,那又是一个有意思的话题。