如何使用C语言构建一个多任务协作系统

前言

在计算机科学领域,任务调度和协作是关键的概念。虽然传统的操作系统提供了各种任务调度算法和机制,但有时我们需要更灵活、个性化的任务管理方式。

即使采用定时器实现时间片论法任务调度,但是也必须等单个完整的任务执行完成后才能执行下一个完整的任务。

本文将介绍使用标准库头文件<setjmp.h>中的setjmplongjmp函数构建一个简单的查询式协作多任务系统,无需使用定时器进行任务切换。

setjmplongjmp是C语言标准库头文件<setjmp.h>中提供的函数。它们的功能是实现非局部跳转,可以在程序的不同位置之间进行跳转,类似于goto语句的扩展。这种非局部跳转的能力为我们构建查询式协作多任务系统提供了基础。

介绍

setjmp和longjmp

setjmp函数用于保存当前程序状态,创建一个可以供后续longjmp函数跳转的上下文环境。在调用setjmp时,程序会记录当前的程序计数器、寄存器和堆栈等状态信息,并将这些信息保存在一个jmp_buf结构中。同时,setjmp函数返回0作为普通调用的返回值,并将jmp_buf作为标识符存储起来。

不同平台的jmp_buf的类型定义不一样,大概占用不到30个字节,因为不同平台的相关寄存器等不一样,因此占用的大小也不同。

longjmp函数则实现了对保存的上下文环境的跳转操作。通过传递之前由setjmp函数保存的jmp_buf标识符,longjmp函数会将程序的状态还原到对应的上下文环境,并且会返回到setjmp处继续执行。

协作式

在协作式多任务调度下,当前任务需要通过主动放弃时间片提供给其他任务运行,而并非是被其他任务抢占,因此这里面并没有所谓的优先级概念之分。

虽然协作式没有所谓的优先级概念之分,但是可以通过一定的方式也能实现一个简单的优先级,比如当前任务主动放弃时间片后,查询更高优先级的任务运行。

时间片论法任务调度只能等任务运行完成才会给下一个任务时间片运行,并不存在主动放弃时间片的功能。

实现思路

了解到setjmplongjmp的功能和原理后,我们能不能通过它们来构建一个任务调度算法和机制呢?
虽然setjmp可以记录当前的程序计数器、寄存器和堆栈等状态信息,但是实现多任务切换时堆栈里面的数据是会发生变化的。

jmp_buf只记录堆栈指针,不记录堆栈指针指向的数据内容。

因此,如果要实现多任务切换,则需要为每个任务分配一定的堆栈预留空间,由于不使用堆,因此可以只考虑栈分配即可。

当前任务主动放弃时间片后,不断查询满足条件需要执行的其他任务。

代码实现

创建任务,使用了setjmp函数。

int cotOs_Creat(OsTask_cb pfnOsTaskEnter, size_t stack)
{
    size_t oldsp;

    if (sg_OsInfo.taskNum >= COT_OS_MAX_TASK || sg_OsInfo.pfnGetTimerMs == NULL)
    {
        return -1;
    }

    COT_OS_GET_STACK(oldsp);
    COT_OS_SET_STACK(sg_OsInfo.stackTop);

    if (0 == setjmp(sg_OsInfo.tcb[sg_OsInfo.taskNum].env))
    {
        COT_OS_SET_STACK(oldsp);
        sg_OsInfo.tcb[sg_OsInfo.taskNum].pfnOsTaskEnter = pfnOsTaskEnter;
        sg_OsInfo.taskNum++;
        sg_OsInfo.stackTop -= stack;
    }
    else
    {
        sg_OsInfo.tcb[sg_OsInfo.taskId].pfnOsTaskEnter();
    }

    return 0;
}

放弃时间片,使用了longjmp,这里集成了时间等待功能,即放弃时间片的时长(即使时长减至0也要等待其他任务主动放弃时间片才会运行)

void cotOs_WaitFor(uint32_t time)
{
    uint32_t timer = sg_OsInfo.pfnGetTimerMs();
    setjmp(sg_OsInfo.tcb[sg_OsInfo.taskId].env);

    if (!(sg_OsInfo.pfnGetTimerMs() - timer > time))
    {
        sg_OsInfo.taskId++;

        if (sg_OsInfo.taskId >= sg_OsInfo.taskNum)
        {
            sg_OsInfo.taskId = 0;
        }

        longjmp(sg_OsInfo.tcb[sg_OsInfo.taskId].env, 1);
    }
}

除了继承时间等待功能外,还定义了一个等待条件的主动放弃放弃时间片功能,即条件不满足时主动放弃时间片(即使条件满足后也要等待其他任务主动放弃时间片才会运行)

#define cotOs_WaitFor_Cond(cond)   do{\
        extern jmp_buf *cotOs_GetTaskEnv1(void);\
        setjmp((*cotOs_GetTaskEnv1()));\
        if (!(cond)){\
            extern void cotOs_RunNextTask(void);\
            cotOs_RunNextTask();\
        }\
    }while (0)

代码链接

目前已完成在STM32板子上:

查询式协作多任务系统

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大橙子疯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值