Autosar Os Task 调度

看球不费电
发布于 2023-11-14 14:42
浏览
0收藏

Tasks

前面有基础的概念文章,需要的请到主页点击历史文章进行查看,本文深入说Task中的一个点 调度

之前有关的文章

《Autosar Os MCU 多核 启动》

《Autosar Os 任务调度》

1. 任务的基础

调度,如果没有逻辑上面的需求与设计,那么一般可以参考下面两种方式来制定。

  • 运行时间较短,也就是很快结束的任务,优先级越高
  • 需要运行频率很快的任务,优先级越高

在Autosar os 里面有三种调度方式。

  • 允许抢占的方式

Autosar Os Task 调度-鸿蒙开发者社区

  • 非抢占的方式

Autosar Os Task 调度-鸿蒙开发者社区

  • 合作式调度

Autosar Os Task 调度-鸿蒙开发者社区

2. 中断

我们这里调度用使用schedule table 来说明。说到调度就不得不说中断,为什么说中断呢,因为每一次激活task (schedule table 激活task) 都是一个 os 的二类中断来激活的。

在Autosar Os 中 中断分为两类。所以 任务,二类中断,一类中断的优先级可以按照下面来分布。

Autosar Os Task 调度-鸿蒙开发者社区

说任务呢,怎么扯到这了。因为我们一般把OS Level 的那个优先级的二类中断化作 OS 的中断,来驱动schedule table。

所以中断这里怎么到任务呢。还记得上一篇 Autosar 多核启动里提到的 OS 的中断向量表吗?

我们来分析一下源码。

Os_InterruptVectorTable0:\n\
 nop\n\
.align 5 # Slot 1\n\
 bisr 101\n\
 j      Os_CrossCoreISR0\n\
.align 5 # Slot 2\n\
 bisr 255\n\
 j      DefaultInterruptHandler\n\
.align 5 # Slot 3\n\
 mfcr %d15, 0xFE00\n\
 bisr 101\n\
 mov %d5, %d15\n\
 mov %d4,0\n\
 j      Os_ISRWrapper0\n\

在OS 的中断向量表类似下面这样。每个core都会有自己的table. 这里拿core0 举例。

Slot 1,2,3,4,5,6 指的是 对应的优先级的序号。

j 后面指的是 os 处理函数。也就是说 只要不是DefaultInterruptHandler, 都是被os 配置了的。

然后Os_ISRWrapper0 这里面会执行OS 对这个中断的处理,随后调用 真正的中断服务函数。如下图。

Autosar Os Task 调度-鸿蒙开发者社区

所以:

  • 配置一个定时器中断
  • 中断服务函数是os 的驱动
  • os的驱动这个服务函数 来驱动schedule table
  • schedule table 来active 对应的tasks

这里面涉及到的 中断服务列表全局变量。

CONST(Os_ISRType, OS_CONST_FAST) Os_const_isrs[];


运行至此Os 的“counter” 就会跟着 定时器来跑起来了。

FUNC(StatusType, OS_CODE) Os_IncrementCounter(CounterType CounterID)

上面说到的wrapper 里面 os 具体执行了什么,我们后面专门说OS 的中断会说,这里只说 标题写的task.

注意,这里Os counter 不是字面 仅仅一个counter的意思,是一个很大的结构体。


FUNC(StatusType, OS_CODE) Os_IncrementCounter(CounterType CounterID) {
 StatusType api_retval = E_OK;
 const Os_CoreConfiguration *os_current_core_const = &Os_const_coreconfiguration[OS_MFCR(0xfe1c)];
 Os_ControlledCoreType *os_current_controlled_core = os_current_core_const->controlled; /* [$UKS 238] */
 
 /* Error if the counter is not valid */
 if (!(Os_IsCounterValid(CounterID))) { api_retval = E_OS_ID;  goto api_error_exit; } /* [$UKS 241] */
 if (os_current_core_const != CounterID->core) { api_retval = E_OS_CORE;  goto api_error_exit; } /* [$UKS 1555] */
 if ((0U == (CounterID->access & Os_AppAccess))) { api_retval = E_OS_ACCESS;  goto api_error_exit; } /* [$UKS 853] */

 api_retval += CounterID->advincr(); /* [$UKS 240] [$UKS 239] */
 goto api_exit;
api_error_exit:
 Os_ErrorLog(api_retval, OSServiceId_IncrementCounter, (Os_BiggestType)CounterID, (Os_BiggestType)0, (Os_BiggestType)0); /* [MISRA 2012 Rule 11.4/11.6] */ /*lint !e9078 !e923 !e9030 !e9087 !e931 !e826 !e571 */
api_exit:
 return api_retval;
}

这里能看出来最终函数会运行到

CounterID->advincr();

那么这个是什么呢?是下面的函数。这里

UNC(StatusType, OS_CODE) Os_IncrementCounter_Os_TickCounter(void) {
.
  .
  .
  .
  .
  .
   if ((current_std->state == SCHEDULETABLE_RUNNING) && (now == current_std->match)) {
    (void)Os_PerformEP(current_stc, current_std, (TickType)(65536U), os_current_core_const, os_current_controlled_core);
  }
   current_std++;
   current_stc++;
} }
 if ((0U == (previous_imask & 0xFFU) ) && (Os_ControlledCoreInfo[0U].ReadyTasks.p0 > Os_ControlledCoreInfo[0U].RunningTPMask.t0) /*lint !e931 PC-lint thinks there are side effects here */) {
   Os_Dispatch();
}

}

这里可以看出 优先级大于的时候就会走到调度函数。

Os_Dispatch();

终于形成回环了,看最前面的几张截图,通过优先级的调度。我们继续分析调度函数。


FUNC(void, OS_CODE_CORE0) Os_Dispatch0(void)

3.  Os_Dispatch 调度

到这里我们做个总结。


下面我们继续分析调度函数。

首先通过前面的一张图。

Autosar Os Task 调度-鸿蒙开发者社区

在抢占的时候,会有一个上下文切换,如果是抢占,这里对应的一个栈空间的递增,所以我们先对栈空间进行判断。

spnow = Os_GetSP();

FUNC(Os_StackValueType, OS_CODE) Os_GetSP(void) {
 Os_StackValueType ret;
 uint32 cx = OS_MFCR(0xFE38) + 2U; /* FCX + 1 is stack position, then add return */
 ret.ctx = ((cx & 0xFFFF) << 6) + ((cx & 0xF0000U) << 12); /* Convert to address */
 __asm__ volatile ("mov.d %0, %%a10": "=d" (ret.sp));
 return ret;
}

通过CPU 寄存器 0xFE38 来获取当前的栈指针。

随后进行判断栈空间是否满足。如果有问题,就出现我们常见的 trap了。

Os_Cbk_StackOverrunHook(spoverrun, OS_BUDGET);

一切都没问题,继续往下走,

对当前任务的栈空间进行准备使用。

然后开启即将运行的任务,在运行任务之前 我们在配置OS 的时候,会有配置一些Hook 。

运行任务,和退出任务时 都有对应的Hook. 这些Hook 就是在这里使用的。

.
.
.
PreTaskHook();   /* [$UKS 178] */
     
     Os_Cbk_TaskStart(Os_RunningTask); /* [$UKS 1802] [$UKS 1803] [$UKS 1804] [$UKS 1805] */
     OS_MTCR(0xFE2C, 0x8000U); OS_SEQ_POINT();  /* [$UKS 93] Allow interrupts precedence */
       Os_RunningTask->entry_function();
   
.
.
.
   Os_Cbk_TaskEnd(Os_RunningTask); /* [$UKS 1806] [$UKS 1807] [$UKS 1808] [$UKS 1809]*/
   
  {
     PostTaskHook();   /* [$UKS 177] */
  }
.
.
.

所以我们很多的操作都可以在下面函数进行操作。任务的监控,时间的分析等等。

Os_Cbk_TaskStart(Os_RunningTask);
Os_Cbk_TaskEnd(Os_RunningTask);


真正的任务运行就到了

Os_RunningTask->entry_function();

到这里 一个任务的调度 终于调度起来了。

继续总结一下本文写道的调度的约束。并不是所有情况都涉及到的。涉及到下面。

  1. core 0 的任务
  2. 抢占式 调度
  3. 不涉及一类中断
  4. schedule table 触发

附:

OS 用到的大部分用到的全局变量配置结构体

CONST(Os_CoreConfiguration, OS_CONST_FAST) Os_const_coreconfiguration[2U] = {

{
   &Os_ControlledCoreInfo[0U],
   &Os_AnyCoreInfo[0U],
   &Os_CoreState[0U],
   Os_Dispatch0,
   
   0U
},

{
   &Os_ControlledCoreInfo[1U],
   &Os_AnyCoreInfo[1U],
   &Os_CoreState[1U],
   Os_Dispatch1,
   
   1U
}
}

看一下具体指的是什么。后面函数里面会用到。先举个例子说一下,后面用上的继续说。

这里面包含

  • controlled
  • any
  • state
  • dispatch
  • core_id

typedef struct Os_CoreConfiguration_s {
 CONSTP2VAR(Os_ControlledCoreType, OS_VAR, OS_CONST) controlled;
 CONSTP2VAR(Os_AnyCoreType, OS_VAR, OS_CONST) any;
 CONSTP2VAR(OS_VOLATILE Os_CoreStateType, OS_VAR, OS_CONST) state;
 Os_VoidVoidFunctionType dispatch;
 CoreIdType core_id;
} Os_CoreConfiguration;

controlled;

typedef struct Os_ControlledCoreType_s {
OsTrapInfoType TrapInfo;//
Os_ErrorInformationType ErrorInformation;
OS_VOLATILE Os_Lockable lock_taskaccess;
OS_VOLATILE Os_psetType ReadyTasks ;
OS_VOLATILE ISRType RunningISR;
OS_VOLATILE TaskType RunningTask;
OS_VOLATILE Os_tpmaskType RunningTPMask ;
OS_VOLATILE SpinlockIdType CurrentSpinlock;
Os_MeterInfoRefType CurrentMeteredObject;
Os_MeterInfoType IdleMeter;
uint8 AppAccess;
OS_VOLATILE ApplicationType AppOverride;
Os_StackSizeType GetStackValueAdjust;
boolean InErrorHook;
OS_VOLATILE TaskType ChainTaskRef;
Os_StackSizeType GetStackUsageAdjust;
boolean InProtectionHook;
boolean CoreIsActive;
boolean InShutdownHook;
} Os_ControlledCoreType;
  • trapInfo

trap 信息,在前一个文章说到过,在启动os 之前 os的中断和trap 都会进行初始化。举个例子。程序里出现一个数除以0,则就会进入一个trap. 很多种错误都会进入trap.

trap 有以下几种分类,我这边经常遇到的trap 指令,上下文,内存访问。

  • MMU (Memory Management Unit)
  • Internal Protection
  • Instruction Error
  • Context Management
  • System Bus and Peripherals
  • Assertion Trap•
  • System Call
  • Non-Maskable Interrupt (NMI)




文章转载自公众号:汽车与基础软件

分类
标签
已于2023-11-14 14:42:26修改
收藏
回复
举报
回复
    相关推荐