【问题标题】:The code for the first task switches in a RTOSRTOS 中第一个任务切换的代码
【发布时间】:2017-08-17 09:00:27
【问题描述】:

我最近正在阅读有关微控制器(尤其是 AVR)中的实时操作系统的信息,我正在尝试为 (AVR) 编写一个简单的操作系统。我的问题是关于上下文切换...

因此,taskA 执行并被 ISR 中断,该 ISR 决定现在应该运行另一个任务(即 taskB),然后加载 taskB 的上下文(寄存器文件、堆栈指针、状态寄存器),并在退出 ISR 时,因为“堆栈指针”设置在正确的位置(就在 taskB 的程序计数器上方,当 taskB 被 ISR 中断时保存在堆栈中),代码将为 taskB 执行。到目前为止,这是我阅读互联网所理解的。

我的问题是:一切如何开始?如果这是第一次 taskA 被中断,那么为 taskB 加载了什么?堆栈上没有 taskB 的程序计数器,因此没有要加载的堆栈指针。如何编码?

【问题讨论】:

  • 它像任何其他操作系统一样由引导加载程序启动。
  • 也许这个问题并不清楚,但我正在尝试编写自己的 RTOS ...我不明白如何编写该部分 :)
  • 为什么投反对票?
  • “为什么投反对票?” 因为你的问题太宽泛了。您需要编写一个小型引导加载程序来初始化 ISR 并跳转到程序的主入口点。
  • @user0042 :这不是他要问的;他在询问任务/线程启动,而不是操作系统启动。与任何引导加载程序无关。无论如何,这不是引导加载程序启动main() 的方式。像 AVR 这样的微控制器不可能有一个引导加载程序,而不是已经在掩码 ROM 中的简单引导加载程序。您也许应该避免对您根本不理解的问题投反对票。我的任何衡量标准都不算太宽泛。

标签: c++ c operating-system embedded


【解决方案1】:

通常是这样的:

线程 A 调用 OS 'CreateThread()' API,(假设参数有效:)。

新线程 B 将需要一个堆栈,因此调用内存管理为调用中传递的堆栈大小(或一些默认值)提供足够的虚拟内存。

然后,此堆栈顶部会加载操作系统设计认为任何线程使用线程 API 访问所需的所有线程私有内容。

将线程代码的代码起始地址、标志寄存器或其他任何东西加载到B堆栈中,以使B堆栈的底部看起来像线程B之前已经存在并被中断到内核中。

然后可以第一次启动新线程 B,就像它不是第一次一样,例如。通过加载堆栈指针和中断返回。

[注意 - 假设堆栈向下增长,如 x86]

【讨论】:

    【解决方案2】:

    在创建任务时会构建初始上下文(在任务控制块中 - TCB - 用于任务),程序计数器设置为任务入口点的地址,堆栈指针设置为任务堆栈的起始地址。这些初始值是第一次计划任务时加载的值。

    总之,初始任务上下文是综合,而不是保存之前的执行。

    通常初始上下文是在createTask 函数中构建的,而初始任务的上下文由startSheduler 函数加载。

    【讨论】:

    • 先生,谢谢您的关注……这实际上是我所做的。当我创建一个新任务时,我正在为新任务堆栈分配 64 个(应该足够)字节的内存(使用 malloc() ... :D ),然后我将堆栈指针设置为指向该内存位置由 malloc 返回(应该是那个或那个 + 64?? ),然后我将处理程序的函数指针推入“新堆栈”,然后将已修改的(因为 PUSH)堆栈指针保存在 TCB 结构中。 .. 可以吗?
    • 也许我应该将堆栈指针改回它第一次输入“createNewTask(...)”时的值?!
    • @CMarius :所以不是一个讨论论坛。你问了一个关于 RTOS 的通用问题。如果您对_您的_具体实施有其他疑问,请发布一个新问题。评论不适用于正在进行的冗长讨论。
    【解决方案3】:

    在 main() 中设置好任务/线程后,您需要调用 OS.start() 或类似方法。根据需要,运行任务调度程序以找到准备好运行的最高优先级任务。

    这个调度器需要区分一个从未运行过的任务(因此没有线程上下文从堆栈中弹出)和一个已经被抢占的任务。

    void scheduler(void)
    {
       //find the next ready task
       ...code...
    
       if(theReadyTask.hasPreviouslyRun)
       {
           //the task was preempted and needs its context restored
           ...code to load load the registers off of the stack...
       }
       //whether it has run before or not we need to change the stack pointer so the return jumps to the function associated with the thread.
       ...code to set stack pointer...
    }
    

    所以一旦一个任务第一次开始,它总会有一个要返回的上下文。但是第一次进去需要特殊处理...

    【讨论】:

    • 没问题。如果对您有用,请随时点赞或接受。
    • 在 AVR uC 的情况下,每个任务的内存是使用 malloc 获得的吗?然后在创建任务时,堆栈指针被加载了 malloc 函数返回的指针?
    • 在嵌入式系统上,不建议使用动态内存分配,原因在网站上有相当广泛的记录。此外,AVR 具有哈佛架构,这意味着程序空间和数据空间是不同的内存。所以你有一个调用堆栈和一个数据堆栈作为两个独立的东西。因此,您正在恢复调用堆栈(以返回正确的位置)和数据堆栈(以确保寄存器的配置与任务被抢占时一样。)
    • 您可能会在研究现有 RTOS 以获得所有概念方面取得更大的成功,然后再尝试自己动手。试试freertos.org/a00098.html
    • 我正在做这种并行和其他几个来源
    【解决方案4】:

    在嵌入式系统中,如 AVR,您经常需要在程序开始时创建任务。这些任务可以创建或终止新任务。

    int main() {
        Task *A = createTask(.....);
        while(1) {
            processTasks();
        }
    }
    

    这里createTask(.....); 函数将任务 A 插入待处理任务的任务队列中,processTasks(); 函数从任务队列中获取一个任务(即 A)并执行它们。 ..... 表示 RTOS 所需的大量参数,例如函数处理程序、TCB、优先级……

    此外main()实际上也是一项任务。

    当然周围有很多东西,这不是一个简单的问题。

    【讨论】:

    • 这并不能真正回答问题;它只是典型的 RTOS 任务创建和调度启动的伪代码。他似乎在问的是,没有现有上下文的任务如何首次开始。 “通过调用 functionX”并没有真正回答这个问题。
    猜你喜欢
    • 2015-04-13
    • 1970-01-01
    • 1970-01-01
    • 2022-09-22
    • 1970-01-01
    • 2020-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多