Add feature to bind CoTask and CoTimer to the specified CoScheduler Improve functional safety TODO: 1. Compatibility with coroutine API highly intensive IO, sockets, etc 2. Fix running fail on arm64
18 KiB
目录
概述
协程模块是一种轻量级的用户态线程管理机制,在程序开发中扮演着重要角色,主要用于资源受限的嵌入式设备、网络编程、异步IO、实时数据处理等需要高效并发的场景。 相比传统线程具有更低的资源消耗(无需内核态切换)和更快的上下文切换速度,通过协程模块,开发者能够实现高效的任务切换和并发执行,从而提升程序的性能和响应速度。
coroutine 是一个高性能、轻量级的C语言协程库,采用非对称有栈协程设计。该库提供了完整的协程生命周期管理,支持多任务调度、定时器管理和事件同步机制,提供简洁的创建、调度与同步API接口,可无缝集成到嵌入式系统、服务器程序等多种环境中。并且移植起来也比较简单,只需要在目标平台上编译并链接该模块即可(已支持主流的芯片内核架构)。
设计理念
- 简单易用:提供直观的API接口
- 移植方便:移植所需资源依赖少
- 高效性能:低开销的上下文切换
- 稳定可靠:错误处理和资源管理
核心特性
1. 架构特性
- 非对称有栈协程:每个协程拥有独立的栈空间
- 多调度器支持:可配置多个独立的协程调度器
- 混合内存管理:支持静态预分配和动态内存管理
- 跨平台兼容:支持x86、x64、ARM、MIPS等多种CPU架构(尚未完全测试完)
2. 协程状态管理
协程调度器状态定义
#define COSCHEDULER_STATE_INTI (0) // 初始化状态
#define COSCHEDULER_STATE_EXIT (1) // 退出状态
#define COSCHEDULER_STATE_START (2) // 启动状态
#define COSCHEDULER_STATE_RUNNING (3) // 运行状态
#define COSCHEDULER_STATE_SCHEDULE (4) // 调度状态
协程任务状态定义
#define COTASK_STATE_INTI (0) // 初始化状态
#define COTASK_STATE_READY (1) // 就绪状态
#define COTASK_STATE_RUNNING (2) // 运行状态
#define COTASK_STATE_SUSPEND (3) // 挂起状态
#define COTASK_STATE_DELETED (4) // 删除状态
3. 内存管理配置
静态资源限制配置
#define COROUTINE_SCHEDULER_NUMBER 1 // 调度器数量
#define COROUTINE_STATIC_TASK_MAX_NUMBER 8 // 静态任务最大数量
#define COROUTINE_STATIC_TIMER_MAX_NUMBER 8 // 静态定时器最大数量
#define COROUTINE_STATIC_STACK_MAX_NUMBER 8 // 静态栈最大数量
#define COROUTINE_STACK_DEFAULT_SIZE 10240 // 默认栈大小
架构设计
1. 整体架构
+----------------------------------------------------+
| Architecture |
+------------------------------------+---------------+
| | +---------+ |
| +--------+ +--------+ | | CoTimer | |
| | | | | | +---------+ |
| | | CoEvent | | CoEvent +---------+ |
| | CoTask |<=======>| CoTask |<=======>| CoTimer | |
| | | | | | +---------+ |
| | | | | | +---------+ |
| +--------+ +--------+ | | CoTimer | |
+------------------------------------+ +---------+ |
| CoScheduler |
+----------------------------------------------------+
2. 协程上下文切换机制
- 使用setjmp/longjmp实现快速上下文保存和恢复
- 每个协程维护独立的栈空间和环境
- 支持协程挂起、恢复和删除操作
API接口详解
1. 调度器管理接口
CoScheduler_Init - 初始化协程调度器
函数原型:
#define CoScheduler_Init(CoSchedulerId, tick, tickInterval, ...)
函数说明:
初始化指定的协程调度器,设置时钟函数和时钟间隔。此函数必须在调用任何其他协程函数之前执行。
套用 int CoScheduler_InitP(uint32_t CoSchedulerId, CoTick_t tick, uint32_t tickInterval, CoSchedulerInitPara *pPara); 函数,提供更多的初始化参数配置。
参数详解:
CoSchedulerId[必需]:调度器ID,范围0到(COROUTINE_SCHEDULER_NUMBER-1)tick[必需]:时钟函数指针,用于获取当前时间戳tickInterval[必需]:时钟间隔,表示每个tick对应的纳秒数lock[可选]:锁函数指针,用于多线程环境下的资源保护unlock[可选]:解锁函数指针,用于多线程环境下的资源释放malloc[可选]:内存分配函数指针free[可选]:内存释放函数指针
返回值:
COROUTINE_E_OK(0):初始化成功COROUTINE_E_INVALID_PARAMETER(-1):无效参数COROUTINE_E_INVALID_TICK(-2):无效时钟函数COROUTINE_E_INVALID_TICK_INTERVAL(-3):无效时钟间隔COROUTINE_E_INVALID_LOCK(-4):无效锁函数COROUTINE_E_INVALID_MHOOK(-5):无效内存钩子函数
使用示例:
// 基本使用:使用微秒时钟,1000us间隔
CoScheduler_Init(0, GetTimerUsec, 1000);
// 多线程支持:带锁函数
CoScheduler_Init(0, GetTimerUsec, 1000, .lock=thread_lock, .unlock=thread_unlock);
// 完整配置:带内存管理函数
CoScheduler_Init(0, GetTimerUsec, 1000,
.lock=thread_lock,
.unlock=thread_unlock,
.malloc=malloc,
.free=free
);
CoScheduler_Start - 启动协程调度器
函数原型:
int CoScheduler_Start(uint32_t CoSchedulerId);
函数说明: 启动指定的协程调度器,开始执行协程任务调度。
参数:
CoSchedulerId:调度器ID
返回值:
- 0:启动成功
- 错误码:启动失败
使用流程:
// 完整的调度器启动流程
CoScheduler_Init(0, GetTimerUsec, 1000);
testCoroutine = CoTask_Create(test, g_StackTest, sizeof(g_StackTest));
CoScheduler_Start(0);
CoScheduler_Exit - 退出协程调度器
函数原型:
int CoScheduler_Exit(uint32_t CoSchedulerId);
函数说明: 退出指定的协程调度器,停止所有协程任务并清理资源。
参数:
CoSchedulerId:调度器ID
返回值:
COROUTINE_E_OK:退出成功
2. 协程任务管理接口
CoTask_Create - 创建协程任务
函数原型:
#define CoTask_Create(entry, ...)
函数说明:
创建一个新的协程任务,可以指定栈空间、参数和调度器。
套用 CoTask_t CoTask_CreateP(CoTaskEntry_t entry, CoTaskCreatePara *pPara); 函数,提供更多的初始化参数配置。
参数详解:
entry[必需]:协程入口函数指针,类型为void (*)(void*)pStack[可选]:栈空间地址,如果为NULL则自动分配stackSize[可选]:栈大小,未指定时使用默认值arg[可选]:传递给协程的参数指针schedulerId[可选]:调度器ID,负值表示自动分配
返回值:
CoTask_t:成功创建的协程任务句柄NULL:创建失败
使用示例:
// 使用默认参数创建协程
CoTask_t testCoroutine = CoTask_Create(test);
// 指定栈空间创建协程
CoTask_t testCoroutine = CoTask_Create(test, g_StackTest, sizeof(g_StackTest));
// 指定栈大小创建协程
CoTask_t testCoroutine = CoTask_Create(test, .stackSize=4096);
// 带参数创建协程
int arg = 4096;
CoTask_t testCoroutine = CoTask_Create(test, .arg=&arg);
CoTask_Delete - 删除协程任务
函数原型:
int CoTask_Delete(CoTask_t CoTask);
函数说明: 删除指定的协程任务,释放相关资源。
参数:
CoTask:要删除的协程任务句柄
返回值:
- 0:删除成功
COROUTINE_E_INVALID_PARAMETER:无效参数
CoTask_Self - 获取当前协程任务
函数原型:
CoTask_t CoTask_Self(void);
函数说明: 获取当前正在运行的协程任务句柄。
返回值:
CoTask_t:当前协程任务句柄NULL:不在协程上下文中
CoTask_SchedulerId - 获取协程调度器ID
函数原型:
int CoTask_SchedulerId(void);
函数说明: 获取当前协程任务所属的调度器ID。
返回值:
- 调度器ID:0到(COROUTINE_SCHEDULER_NUMBER-1)
3. 协程等待机制接口
CoTask_Wait - 协程等待函数
函数原型:
#define CoTask_Wait(...)
函数说明:
使当前协程任务等待指定的事件或超时。
套用 uint32_t CoTask_WaitP(CoTaskWaitPara *pPara); 函数,提供更多的等待参数配置。
参数详解:
pEvent[可选]:等待的事件指针ms[可选]:等待的毫秒数tick[可选]:等待的tick数
返回值:
uint32_t:触发的事件标志位
使用示例:
void *test(void *arg) {
while (1) {
// 放弃调度器访问权,类似yield
CoTask_Wait();
// 等待1000ms
CoTask_Wait(.ms=1000);
// 等待事件发生
uint32_t evs = CoTask_Wait(.pEvent=&g_Event);
if (evs & 0x01) {
printf("event 0x01 triggered\n");
}
// 组合等待:事件+超时
evs = CoTask_Wait(.pEvent=&g_Event, .ms=5000);
}
}
套用 CoTask_Wait 可以形成以下常用方法:
#define CoTask_WaitMs(m) CoTask_Wait(.ms=m)
#define CoTask_WaitTick(t) CoTask_Wait(.tick=t)
#define CoTask_WaitEvent(e) CoTask_Wait(.pEvent=e)
#define CoTask_WaitEventMs(e, m) CoTask_Wait(.pEvent=e, .ms=m)
#define CoTask_WaitEventTick(e, t) CoTask_Wait(.pEvent=e, .tick=t)
4. 定时器管理接口
CoTimer_Create - 创建协程定时器
函数原型:
#define CoTimer_Create(entry, ...)
函数说明:
创建一个周期性的协程定时器。
套用 CoTimer_t CoTimer_CreateP(CoTimerEntry_t entry, CoTimerCreatePara *pPara); 函数,提供更多的初始化参数配置。
参数详解:
entry[必需]:定时器入口函数ms[可选]:毫秒间隔tick[可选]:tick间隔
返回值:
CoTimer_t:成功创建的定时器句柄NULL:创建失败
使用示例:
// 创建100ms间隔的定时器
CoTimer_t timer = CoTimer_Create(timer_entry, .ms=100);
CoTimer_Delete - 删除协程定时器
函数原型:
void CoTimer_Delete(CoTimer_t Timer);
函数说明: 删除指定的协程定时器。
参数:
Timer:要删除的定时器句柄
CoTimer_Self - 获取当前定时器
函数原型:
CoTimer_t CoTimer_Self(void);
函数说明: 获取当前正在运行的协程定时器句柄。
返回值:
CoTimer_t:当前定时器句柄NULL:不在定时器上下文中
5. 事件同步接口
CoEvent_Init - 初始化事件
函数原型:
void CoEvent_Init(CoEvent *pEvent);
函数说明: 初始化协程事件,准备用于协程间同步。
参数:
pEvent:事件指针
使用示例:
CoEvent_Init(&g_Event);
此方法为运行时调用,静态初始化可以直接赋值为 COEVENT_STATIC_VALUE。
CoEvent g_Event = COEVENT_STATIC_VALUE;
CoEvent_Notify - 通知事件
函数原型:
void CoEvent_Notify(CoEvent *pEvent, uint32_t evs);
函数说明: 通知指定的事件,唤醒等待该事件的协程任务。
参数详解:
pEvent:事件指针evs:事件标志位,支持多事件同时通知
使用示例:
// 通知单个事件
CoEvent_Notify(&g_Event, 0x01);
// 同时通知多个事件
CoEvent_Notify(&g_Event, 0x01 | 0x02 | 0x04);
6. 监控统计接口
CoTask_StackMaxUsed - 获取协程栈最大使用量
函数原型:
size_t CoTask_StackMaxUsed(CoTask_t CoTask);
函数说明: 获取指定协程任务栈的最大使用量。
参数:
CoTask:协程任务句柄
返回值:
size_t:栈最大使用量(字节)
CoTask_StackCurUsed - 获取协程栈当前使用量
函数原型:
size_t CoTask_StackCurUsed(CoTask_t CoTask);
函数说明: 获取指定协程任务栈的当前使用量。
参数:
CoTask:协程任务句柄
返回值:
size_t:栈当前使用量(字节)
CoScheduler_CurLoad - 获取当前负载
函数原型:
uint16_t CoScheduler_CurLoad(uint32_t CoSchedulerId);
函数说明: 获取指定调度器的当前负载。
返回值:
uint16_t:当前负载百分比(0-10000,表示0.00%-100.00%)
CoScheduler_MaxLoad - 获取最大历史负载
函数原型:
uint16_t CoScheduler_MaxLoad(uint32_t CoSchedulerId);
函数说明: 获取指定调度器的历史最大负载。
返回值:
uint16_t:最大负载百分比
7. 统计计数接口
CoScheduler_TaskCount - 获取协程任务数量
函数原型:
int CoScheduler_TaskCount(uint32_t CoSchedulerId);
函数说明: 获取指定调度器中的协程任务数量。
参数:
CoSchedulerId:调度器ID
返回值:
int:协程任务数量
CoScheduler_TimerCount - 获取定时器数量
函数原型:
int CoScheduler_TimerCount(uint32_t CoSchedulerId);
函数说明: 获取指定调度器中的协程定时器数量。
参数:
CoSchedulerId:调度器ID
返回值:
int:定时器数量
配置选项
编译时配置 (coroutine_cfg.h)
基本配置:
// 调度器配置
#define COROUTINE_SCHEDULER_NUMBER 1 // 调度器数量
// 静态资源限制
#define COROUTINE_STATIC_TASK_MAX_NUMBER 8 // 静态任务最大数量
#define COROUTINE_STATIC_TIMER_MAX_NUMBER 8 // 静态定时器最大数量
#define COROUTINE_STATIC_STACK_MAX_NUMBER 8 // 静态栈最大数量
// 栈配置
#define COROUTINE_STACK_DEFAULT_SIZE 10240 // 默认栈大小
功能开关:
// 栈使用量计算
#define COROUTINE_ENABLE_STACK_CALCULATE 1 // 启用栈使用量计算
// 负载计算
#define COROUTINE_ENABLE_LOADING_CALCULATE 1 // 启用负载计算
// 调试支持
#define COROUTINE_ENABLE_DEBUG 1 // 启用调试功能
使用指南
1. 基本使用流程
初始化阶段
#include "coroutine.h"
// 定义时钟函数
uint64_t GetTimerUsec(void) {
// 返回微秒级时间戳
return ...;
}
int main(void) {
// 初始化调度器0,使用微秒时钟,1000us间隔
CoScheduler_Init(0, GetTimerUsec, 1000);
协程定义
// 协程任务函数
void *test_task(void *arg) {
int *p_count = (int*)arg;
while (1) {
// 协程任务逻辑
(*p_count)++;
// 主动让出CPU
CoTask_Wait();
}
return NULL;
}
创建和启动
// 创建协程任务
int count = 0;
CoTask_t testCoroutine = CoTask_Create(test_task, .arg=&count);
2. 高级使用模式
事件驱动模式
void *event_driven_task(void *arg) {
while (1) {
// 等待事件
uint32_t evs = CoTask_Wait(.pEvent=&g_Event);
if (evs & 0x01) {
// 处理事件0x01
handle_event_01();
}
if (evs & 0x02) {
// 处理事件0x02
handle_event_02();
}
}
}
定时器模式
void *timer_task(void *arg) {
while (1) {
// 每100ms执行一次
CoTask_Wait(.ms=100);
// 定时器逻辑
periodic_work();
}
}
高级功能
1. 栈使用量计算
启用 COROUTINE_ENABLE_STACK_CALCULATE 后,可以监控协程栈的使用情况:
// 获取协程栈最大使用量
size_t maxUsed = CoTask_StackMaxUsed(CoTask);
// 获取协程栈当前使用量
size_t curUsed = CoTask_StackCurUsed(CoTask);
2. 负载计算
启用 COROUTINE_ENABLE_LOADING_CALCULATE 后,可以计算调度器的负载:
// 获取当前负载
uint16_t curLoad = CoScheduler_CurLoad(CoSchedulerId);
// 获取最大历史负载
uint16_t maxLoad = CoScheduler_MaxLoad(CoSchedulerId);
3. 性能监控
// 监控协程调度性能
void monitor_scheduler_performance(void) {
uint16_t curLoad = CoScheduler_CurLoad(0);
uint16_t maxLoad = CoScheduler_MaxLoad(0);
if (curLoad > 8000) { // 80%负载警告
printf("Warning: Scheduler load is high: %d.%02d%%\n",
curLoad/100, curLoad%100);
}
错误处理
错误码定义
#define COROUTINE_E_OK (0) // 成功
#define COROUTINE_E_INVALID_PARAMETER (-1) // 无效参数
#define COROUTINE_E_INVALID_TICK (-2) // 无效时钟函数
#define COROUTINE_E_INVALID_TICK_INTERVAL (-3) // 无效时钟间隔
#define COROUTINE_E_INVALID_LOCK (-4) // 无效锁函数
#define COROUTINE_E_INVALID_MHOOK (-5) // 无效内存钩子函数
性能优化
1. 上下文切换优化
- 使用setjmp/longjmp替代完整的上下文保存
- 最小化寄存器保存和恢复操作
2. 内存管理优化
- 静态预分配减少动态内存分配
- 栈空间复用机制
平台支持
该库通过内联汇编实现了跨平台的栈指针操作:
- x86/x64:使用esp/rsp寄存器
- ARM:使用sp寄存器
- MIPS:使用sp寄存器
- RISC-V:使用sp寄存器
- PowerPC:使用r1寄存器
平台特定实现
// x86架构实现
#if defined(__i386__)
#define COROUTINE_GET_STACK_POINTER(p) \
__asm__ __volatile__("movl %%esp, %0" : "=r"(p))
应用场景
1. 嵌入式系统
- 实时任务调度
- 低功耗设备管理
- 传感器数据处理
2. 网络编程
- 异步I/O处理
- 连接池管理
- 协议栈实现
3. 游戏开发
- 游戏对象更新
- AI行为调度
- 动画系统管理
4. 物联网应用
- 设备通信协议
- 数据处理流水线
- 远程控制接口
总结
coroutine 协程库是一个功能强大、性能优异的C语言协程解决方案。它提供了:
- 完整的协程生命周期管理
- 高效的多任务调度机制
- 灵活的定时器和事件管理
- 完善的错误处理和监控功能