协程概念
首先都知道协程的核心点就是 轻量级
、用户态切换
、速度快
、灵活
、相比较(进程\线程)更节省资源
,但是如何能实现由用户态自己去调度切换呢, 实际上就是在我们每次调用中断的时候,自动保存当前执行函数的调用栈上下文和跳转地址,以便在下次执行的时候自动恢复到上次执行点,那如果要从底层理解的话就是在不断切换/赋值
寄存器 RSP
RIP
两个指令的地址, 最终由这两个指令最终去完成函数调用栈的恢复和执行.
实现一个协程模型
我们都知道协程这个概念了,那如何自己去搞一个呢,比如
Go
语言天生支持,而且实现的协程是目前完成度比较好的,但如果用C
或者PHP
怎么去实现一个这种特性呢,如果是PHP
可能还会有yield
这个关键字能实现类似的功能,但是如果用C
的话就比困难了,因为根本没有这个关键字和功能支持,需要我们自己去写yield
这个关键字,所以需要借助一些底层库去完成, 这里采用的是ucontext
协程库去实现,这也是C
实现协程用得最多的方案,而这个库提供的功能就是保存上下文
,切换上下文
,设置上下文
基于这个库之后我们在加以封装,开发一个调度管理器用以调度就可以了.
实现设计图
协程模型设计图提供公共方法
static void task ((void*)())
注册需要调度的函数
static void yield()
中断并保存上下文
static int run()
开始运行并调度管理注册的函数
实现代码
#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>
#include <unistd.h>
#define RUN_START 1
#define RUN_END 0
static int stack_size = 1024;
static int pid = 1;
typedef void (*funType)(void);
typedef struct env{
funType func;
ucontext_t ucon;
struct env *next, *prev;
int task_id;
int run_status;
}env;
static env* current_list = NULL;
static ucontext_t run_env;
static void exec(){
current_list->func();
current_list->run_status = RUN_END;
setcontext(&run_env);
}
static void task(funType func){
env* node = (env*)malloc(sizeof(env));
node->task_id = pid++;
node->func = func;
node->run_status = RUN_START;
if(current_list){
current_list->prev->next = node;
node->next = current_list;
node->prev = current_list->prev;
current_list->prev = node;
}else{
node->next = node;
node->prev = node;
current_list = node;
}
getcontext(&node->ucon);
node->ucon.uc_stack.ss_sp = malloc(stack_size);
node->ucon.uc_stack.ss_size = stack_size;
makecontext(&node->ucon, exec, 0);
}
static int run(){
struct env* current_node = current_list;
while(current_list){
printf("task_id=%d -- run_status=%d \r\n",current_list->task_id, current_list->run_status);
swapcontext(&run_env, ¤t_list->ucon);
printf("task_id=%d -- run_status=%d \r\n",current_list->task_id, current_list->run_status);
sleep(1);
//如果函数调用栈执行完毕则释放
if(current_list->run_status == RUN_END){
current_node = current_list;
if(current_node == current_list->next){
current_list = NULL;
}else{
current_list->prev->next = current_list->next;
current_list->next->prev = current_list->prev;
current_list = current_list->next;
}
free(current_node->ucon.uc_stack.ss_sp);
free(current_node);
}else{
current_list = current_list->next;
}
}
}
static void yield(){
swapcontext(¤t_list->ucon,&run_env);
}
int fun1(){
printf("fun1-a\r\n");
yield();
printf("fun1-b\r\n");
return 0;
}
void fun2(){
printf("fun2\r\n");
}
void fun3(){
printf("fun3\r\n");
}
int main(){
task((void(*)(void))fun1);
task(fun2);
//task(fun3);
run();
return 0;
}
运行结果
运行结果首先我们注册两个函数
fun1()
fun2()
然后进行执行,会依次打印fun1-a
,fun2
,fun1-b
执行环境
Linux 027a36236b38 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
结束
以上仅供学习,并不在生产环境有任何价值.
网友评论