协程
絮絮叨叨
这篇博客是Ctrl OS开发的第一篇博客,在后面的时间中将会将整个开发过程和相关知识整理成博客。
协程是Ctrl OS的系统层支持,后面所有的功能都是基于协程为基础进行开发,它是基础也是核心,所以第一篇来详细介绍一下协程相关知识,并确定所需要定义的接口。
什么是协程
协程的定义
Wiki的定义:协程是一种程序组件,是由子例程(过程、函数、例程、方法、子程序)的概念泛化而来的,子例程只有一个入口点且只返回一次,而协程允许多个入口点,可以在指定位置挂起和恢复执行。
这是wiki百科关于协程的定义,很抽象。我对他的简单理解为:
协程是由用户控制程序的运行流的一个组件,它由用户(开发者)控制CPU对程序的执行和释放。
在协程中,程序可由用户决定在某处挂起,跳转执行另一段程序,并且可重新恢复到挂起处。
与进程和线程的比较
单独看文字的定义和描述,总是很难理解什么是协程。进行不同维度的对比相对容易认识什么是协程。
运行调用层次
- 一个协程可以包含多个线程,一个线程可以包含多个协程。
- 进程是资源分配的最小单元,线程是CPU调度的最小单元。协程是一种特殊的函数。
- | 进程 | 线程 | 协程 |
---|---|---|---|
切换者 | 操作系统 | 操作系统 | 用户 |
切换时间 | 根据操作系统自己的切换策略,用户不感知切换时间 | 根据操作系统自己的切换策略,用户不感知 | 用户 |
切换内容 | 页全局目录 内核栈 硬件上下文 | 内核栈 硬件上下文 | 硬件上下文 用户栈 |
切换内容的保存 | 保存于内核栈 | 保存于内核栈 | 用户变量 |
切换过程 | 用户态->内核态->用户态 | 用户态->内核态->用户态 | 用户态 |
切换效率 | 低 | 中 | 高 |
在上表中可看出,进程和线程都是处于系统内核的操作,而协程属于用户的操作。
使用协程的一个好处就是所有操作都是用户控制的,没有进入内核态(或者说没有内核态),而不用去考虑由于内核切换抢占导致的资源共享问题。
由于在任何时候都是只有一个任务在执行,并且只有用户切换任务时才会进行跳转,所以不用考虑是否为原子操作和锁的问题。
在之前做项目时有一个需求:在某个状态下中断后,不再返回中断前的函数,使用协程可以很好的解决这个问题。
协程API
对于一个协程必须提供的API为:
/*协程函数集*/
typedef struct _co_ops_t
{
void (*init)(void); /*初始化协程*/
co_t (*create)(co_func_t ); /*创建一个协程*/
void (*resume)(co_t , arg_t); /*启动一个协程*/
void (*yeild)(void); /*挂起一个协程*/
co_t (*cur_co)(void); /*获取当前协程*/
void (*exit)(co_t); /*退出指定协程*/
void (*destroy)(co_t); /*销毁协程相关资源*/
arg_t (*get_arg)(co_t); /*获取指定协程参数*/
void (*set_arg)(co_t , arg_t); /*设置指定协程参数*/
}co_ops_t;
github
项目地址:Ctrl OS