# cotOs **Repository Path**: MagicBude/cot_os ## Basic Information - **Project Name**: cotOs - **Description**: 一个轻量级的协程调度系统,使用了标准库头文件 中的 setjmp 和 longjmp两个函数,构建了一个简单的查询式协作多任务系统,支持独立栈和共享栈两种任务。 - **Primary Language**: C - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-06-17 - **Last Updated**: 2025-06-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 查询协作式多任务系统 - 注释增强版 > 本项目是基于[大橙子疯](https://blog.csdn.net/qq_24130227)的[轻量级协程调度系统](https://gitee.com/cot_package/cot_os),在原有代码基础上增加了更详细的注释和使用说明,便于学习和理解。 ## 简介 cot_os 是一个轻量级的协程调度系统,使用标准库头文件 `` 中的 `setjmp` 和 `longjmp` 两个函数,构建了一个简单的查询式协作多任务系统,支持**独立栈**和**共享栈**两种任务模式。 > - 涉及到获取和设置栈的地址操作,因此需要根据不同平台提供获取和设置栈顶的地址操作(一般是汇编语言,涉及到寄存器) > - 该调度系统仅运行在一个实际的线程中,因此本质上属于协程 > - 独立栈任务都有自己独立的运行栈空间,互不干扰;共享栈任务共用一个运行栈空间 ## 特点 - **无抢占调度**:无任务优先级抢占功能,任务切换的时机完全取决于正在运行的任务,体现**协作**特性 - **多栈模式**:支持**独立栈**和**共享栈**两种任务,根据不同的应用场景灵活选择 - **查询式调度**:采用查询式的调度方式,当前任务切换时,查询下个任务是否需要执行 - **易于移植**:移植性强,只需修改设置栈和获取当前栈地址的宏即可 ### 与时间片轮转法的比较 - 无需使用定时器作为任务调度 - 每个任务都可以使用`while`循环,用于执行任务并保持程序运行,结构清晰 - 每个任务都可以随时阻塞等待,甚至可以在嵌套的子函数中阻塞等待 - 通过阻塞等待,无需使用状态机等复杂方式来优化缩减每个任务的执行时长 ### 与RTOS的比较 - 没有任务优先级抢占式功能,临界资源(中断除外)和优先级反转问题不存在 - 允许用户根据需要自由地切换到下一个就绪任务 - 通过自主调度和管理任务,提高工作效率 - 没有操作系统的复杂性 ## 主要API | 函数名 | 描述 | |--------|------| | `cotOs_Init` | 初始化协程OS系统 | | `cotOs_Start` | 启动协程调度系统 | | `cotOs_CreatTaskWithStack` | 创建独立栈任务 | | `cotOs_CreatTask` | 创建共享栈任务 | | `cotOs_Wait` | 任务延时等待 | | `cotOs_Join` | 等待指定任务结束 | | `cotOs_ConditionInit` | 初始化条件变量 | | `cotOs_ConditionWait` | 等待条件变量 | | `cotOs_ConditionNotify` | 通知条件变量 | ## 功能设计 ### 基本概念 运行栈空间:程序运行中发生函数调用等情况需要使用的栈内存空间。 ### 独立栈任务(有栈任务) 每个独立栈任务**都拥有**自己独立的运行栈空间,可以随时随地阻塞等待,保存上下文后切换到下一个任务执行。 > 独立栈任务在切换下一个任务时,不会操作运行栈,只对上下文切换 ### 共享栈任务(无栈任务) 每个共享栈任务**都没有**自己独立的运行栈空间,虽然也能阻塞等待,但是仅限于在任务入口函数中使用,禁止在任务的子函数(嵌套函数)中阻塞等待;并且在该任务入口函数中不建议定义过多变量。 > - 每个任务有自己的独立备份栈(用来备份运行栈的栈顶部分数据);运行栈通常比备份栈要大很多 > - 共享栈任务在切换时会将当前运行栈数据拷贝到内存备份,下次执行时再从内存拷贝回运行栈恢复 > - 该类型任务适合于轻量的任务处理,一般都是调用封装好的函数即可 **注**:这里的共享栈任务和常规实现有差异,常规实现是使用堆申请内存保存栈的数据,用多少申请多少进行保存,而这里的实现仅保存了栈的一部分数据。 ### 任务管理 #### 任务创建 1. 在调度系统启动前,至少要先创建一个任务,否则直接退出 2. 可以在任务中创建新的任务,不管是独立栈任务还是共享栈任务 > - 独立栈任务中可以创建新的独立栈任务和共享栈任务 > - 共享栈任务中也可以创建新的任务,且可以使用同一个共享栈 3. 独立栈任务和共享栈任务一共可创建最多32个任务(需修改宏配置) #### 任务销毁 - 没有提供专门的销毁函数,任务入口函数主动退出则自动将任务销毁 - 可以通过`cotOs_Join`函数在其他任务中等待某任务退出 #### 任务阻塞 当前任务阻塞提供两种方式: - **时间阻塞**:需要阻塞多长时间,等时间满足后继续执行(`cotOs_Wait`) - **事件阻塞**:通过条件变量阻塞,事件触发后继续执行(`cotOs_ConditionWait`) #### 任务同步 可以使用条件变量进行任务间同步,一个任务通过`cotOs_ConditionNotify`通知,另一个任务通过`cotOs_ConditionWait`等待。 ## 使用示例 ### 基本任务创建与退出 ```c #include "cot_os.h" uint8_t g_task1Stack[1024 * 2]; uint8_t g_task2Stack[1024 * 2]; uint8_t g_task3Stack[1024 * 2]; /* 执行完成就退出的任务 */ void taskfunc3(int arg) { /* 任务代码 */ cotOs_Wait(1000); /* 更多任务代码 */ cotOs_Wait(1000); /* 函数结束后任务自动销毁 */ } void taskfunc1(int arg) { /* 不管taskfunc1是独立栈任务还是共享栈任务,都支持创建子任务 */ cotOs_CreatTaskWithStack(taskfunc3, "taskfunc3", g_task3Stack, sizeof(g_task3Stack), 0); /* 创建独立栈任务 */ cotOs_CreatTask(taskfunc3, "taskfunc3", 0); /* 创建共享栈任务 */ while (1) { /* 任务代码 */ cotOs_Wait(1000); } } void taskfunc2(int arg) { while (1) { /* 任务代码 */ cotOs_Wait(10); } } int main(void) { /* 初始化cot_os系统 */ cotOs_Init(GetTimerMs); #if 0 /* 创建独立栈任务 */ cotOs_CreatTaskWithStack(taskfunc1, "taskfunc1", g_task1Stack, sizeof(g_task1Stack), 0); cotOs_CreatTaskWithStack(taskfunc2, "taskfunc2", g_task2Stack, sizeof(g_task2Stack), 0); #else /* 创建共享栈任务 */ cotOs_CreatTask(taskfunc1, "taskfunc1", 0); cotOs_CreatTask(taskfunc2, "taskfunc2", 0); #endif /* 启动任务调度 */ cotOs_Start(); return 0; /* 如果所有任务都退出,会执行到这里 */ } ``` ### 独立栈与共享栈的使用区别 ```c #include "cot_os.h" uint8_t g_task1Stack[1024 * 2]; void func1_1(void) { /* 在嵌套函数中使用等待函数(独立栈任务可以这样做) */ cotOs_Wait(1000); /* 更多代码 */ cotOs_Wait(1000); } /* 独立栈任务 */ void taskfunc1(int arg) { int arr[10]; /* 可以直接定义变量使用 */ while (1) { func1_1(); /* 可以在嵌套函数中使用阻塞等待 */ /* 更多代码 */ cotOs_Wait(1000); } } void func2_1(void) { /* 在共享栈任务的嵌套函数中不能使用等待函数! */ /* cotOs_Wait(1000); */ /* 错误用法,会导致栈内容丢失! */ } /* 共享栈任务 */ void taskfunc2(int arg) { int arr[10]; /* 共享栈任务的入口函数依然可以定义变量使用,不会丢失数据 */ while (1) { func2_1(); /* 禁止在嵌套函数中使用阻塞等待 */ /* 更多代码 */ cotOs_Wait(10); /* 只能在入口函数中使用等待函数 */ } } int main(void) { cotOs_Init(GetTimerMs); /* 创建独立栈任务 */ cotOs_CreatTaskWithStack(taskfunc1, "taskfunc1", g_task1Stack, sizeof(g_task1Stack), 0); /* 创建共享栈任务 */ cotOs_CreatTask(taskfunc2, "taskfunc2", 0); cotOs_Start(); return 0; } ``` ### 任务同步与等待 ```c #include "cot_os.h" uint8_t g_task1Stack[1024 * 2]; uint8_t g_task2Stack[1024 * 2]; uint8_t g_task3Stack[1024 * 2]; /* 条件变量用于任务同步 */ CotOSCondition_t g_eventCv; /* 执行完成就退出的任务 */ void taskfunc3(int arg) { /* 任务代码 */ cotOs_ConditionWait(&g_eventCv); /* 等待条件变量通知 */ /* 条件满足后继续执行的代码 */ } void taskfunc1(int arg) { /* 创建一个子任务并保存其任务ID */ cotOsTask_t task = cotOs_CreatTaskWithStack(taskfunc3, "taskfunc3", g_task3Stack, sizeof(g_task3Stack), 0); while (1) { /* 任务代码 */ cotOs_Wait(1000); if (/* 某些条件 */) { /* 等待 taskfunc3 任务运行结束后才退出 taskfunc1 */ cotOs_Join(task); break; /* 退出循环,任务结束 */ } } } void taskfunc2(int arg) { while (1) { /* 任务代码 */ cotOs_Wait(10); if (/* 某些条件 */) { cotOs_ConditionNotify(&g_eventCv); /* 通知 taskfunc3 继续执行 */ } } } int main(void) { /* 初始化条件变量 */ cotOs_ConditionInit(&g_eventCv); cotOs_Init(GetTimerMs); /* 创建任务 */ cotOs_CreatTaskWithStack(taskfunc1, "taskfunc1", g_task1Stack, sizeof(g_task1Stack), 0); cotOs_CreatTaskWithStack(taskfunc2, "taskfunc2", g_task2Stack, sizeof(g_task2Stack), 0); cotOs_Start(); return 0; } ``` ## 应用场景选择 ### 独立栈任务(有栈任务)适用于 - **重量级任务**:提供更多控制,适用于需要精确管理任务状态的情况和计算密集型任务 - **内存可预测**:创建时分配栈空间,可以更好地控制内存使用,适合需要可预测内存行为的场景 - **递归调用**:更容易处理递归调用,因为每个任务都有独立的栈空间 - **嵌套等待**:可以在任意嵌套函数中使用等待函数 ### 共享栈任务(无栈任务)适用于 - **轻量级任务**:通常更轻量,适用于大量小任务的场景 - **内存受限**:适用于内存受限的环境,因为不需要为每个任务分配各自的栈空间(备份栈除外) - **简单逻辑**:适合只在任务入口函数中使用等待函数的简单任务