一、概述
传统基于事件驱动的异步网络服务优势高、性能高、CPU利用率高,但编写困难,业务逻辑编码被拆分得支离破碎,难以维护与排查故障。
使用协程可以做到同步编码,异步执行,性能与异步通信查是一个数量级(底层实现其实都一样),但编写并发任务很方便。
acl 中lib_fiber封装了协程的实现方式, libco是腾讯实现的一款协程库。总的来说,acl的协程库做了大量的封装优化,libco看起来则更加简单易懂。
二、协程概述
-
什么是协程?
微线程,用户态线程、纤程。
进程是分配资源的最小单位,线程是CPU调度的最小单位,协程是逻辑处理的最小单位。举个简单的例子,一个线程可以只处理一个tcp连接,也可以同时处理多个tcp连接请求(使用epoll等),但同一时刻,只能处理一个逻辑单元。 -
协程切换?
– 本协程上下文保存(寄存器/内存, 栈)
– 目标协程上下文恢复
– 指令跳转 -
实现协程的关键技术?
– 协程调度
– 协程上下文切换
– hook关键函数
- 协程调度:
当前一般使用epool/pool/select 等 用于底层调度; - 协程上下文切换:
协程上下文切换,libco,以及acl都编写了底层汇编代码,但使用make_context等也可以实现; - hook 关键函数:
协程Hook函数主要hook与网络相关的函数,如sleep, recv等;
下文就是模拟了协程的几个关键步骤实现的伪代码,其中没有实现调度。
#include "stdio.h"
#include "stdlib.h"
#define FIBER_STATUS_READY 1
#define FIBER_STATUS_RUNNING 2
#define FIBER_STATUS_EXITING 3
ucontenx_t ctx[3];
void run2();
void run1();
void shecdule(int from_id, int to_id);
int current_id = 1;
typedef unsigned (*sleep_fn)(unsigned int seconds)
static sleep_fn __sys_sleep = NULL;
__sys_sleep = (sleep_fn)dlsym(RTLD_NEXT, "sleep");
unsinged int sleep(unsigned int seconds) {
if (!var_hool_sys_api) {
return sys_sleep(1); //调用系统sleep(1);
}
sys_sleep(1);
if (curent_id == 1) {
shecdule(1,2);
} else {
shecdule(2,1);
}
}
void run1()
{
while (1) {
int id = 1;
current_id = id;
printf("id is %d \r\n", id);
sleep(1);
printf("end sleep id is %d \r\n", id);
}
}
void run2()
{
while (1) {
int id = 2;
current_id = id;
printf("id is %s \r\m", id);
sleep(1);
//schedule(2,1);
printf("sleep end id is %d \r\n", id);
}
}
void schedule(int from_id, int to_id) {
swapcontext(&ctx[from_id], &ctx[to_id]);
}
int main()
{
char st1[8192];
char st2[8192];
getcontext(&ctx[1]);
ctx[1].uc_stack.ss_sp = st1;
ctx[1].uc_stack.ss_size = sizeof st1;
ctx[1].uc_link = &ctx[0];
makecontext(&ctx[1], run1, 0);
printf("fiber 1 is ready\r\n");
getcontext(&ctx[2]);
ctx[2].uc_stack.ss_sp = st2;
ctx[2].uc_stack.ss_size = sizeof st2;
ctx[2].uc_link = &ctx[1];
makecontext(&ctx[2], run2, 0);
printf("fiber 2 is ready \r\n");
swapcontext(&ctx[0], &ctx[2]);
return 0;
}