查询式协作多任务系统

良许Linux

共 4366字,需浏览 9分钟

 ·

2024-06-17 07:33


前言

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

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

本文将介绍使用标准库头文件中的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板子上的查询式协作多任务系统:
下载链接(点击阅读原文):

https://gitee.com/cot_package/cot_os

扩展

setjmplongjmp除了实现一个多任务系统外,其实还可以有其他的用法,比如实现C++中的try catch抛出异常处理功能。

由于其特性问题,在实际代码中,特别是应用程序代码上,建议少用setjmplongjmp,否则会影响代码阅读,当然,不排除封装成一个特殊的功能外



春招已经开始啦,大家如果不做好充足准备的话,春招很难找到好工作。


送大家一份就业大礼包,大家可以突击一下春招,找个好工作!


浏览 26
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报