作用:允许当前的函数称为协程,挂在到当前线程;
调用GetCurrThreadCo
获取当前的stCoRoutine_t* 类型协程结构体co;
调用co_get_curr_thread_env()
获取 当前stCoRoutineEnv_t*
类型变量。
gCoEnvPerThread
;调用GetCurrCo(stCoRoutineEnv_t*)
获取类型stCoRoutine_t*变量;
gCoEnvPerThread->pCallStack[gCoEnvPerThread->iCallStackSize-1]
;struct stCoRoutineEnv_t
{
stCoRoutine_t *pCallStack[ 128 ];
int iCallStackSize;
stCoEpoll_t *pEpoll;
//for copy stack log lastco and nextco
stCoRoutine_t* pending_co;
stCoRoutine_t* occupy_co;
};
- stCoRoutineEnv_t用来存放协程实时运行时状态;
- pCallStack应该是目前全部的待运行的协程,iCallStackSize是协程的数量;
设置返回co->cEnableSysHook = 1;
stCoEpoll_t*
ctxpfn_co_eventloop_t
pfnvoid*
arg获取当前协程栈的最后一个协程结构体,并置cEnableSysHook
coctx_t* p1
coctx_t* p2
将p1协程切出,将p2协程切入。
索引 | 字节 | 寄存器 |
---|---|---|
0 | [0,7] | r15 |
1 | [8,15] | r14 |
2 | [16,23] | r13 |
3 | [24,31] | r12 |
4 | [32,39] | r9 |
5 | [40,47] | r8 |
6 | [48,55] | rbp |
7 | [56,63] | rdi |
8 | [64,71] | rsi |
9 | [72,79] | 返回地址 |
10 | [80,87] | rdx |
11 | [88,95] | rcx |
12 | [96,103] | rbx |
13 | [104,112] | rsp |
.globl coctx_swap
#if !defined( __APPLE__ )
.type coctx_swap, @function
#endif
coctx_swap:
leaq (%rsp),%rax ; rax=rsp
movq %rax, 104(%rdi) ; p1->regs[13] = rsp
movq %rbx, 96(%rdi) ; p1->regs[12] = rbx
movq %rcx, 88(%rdi) ; p1->regs[11] = rcx
movq %rdx, 80(%rdi) ; p1->regs[10] = rdx
movq 0(%rax), %rax ; rsp寄存器存储位置的内存为返回指令地址,ret后会写进rip指令寄存器
movq %rax, 72(%rdi) ; p1->regs[9] = ret
movq %rsi, 64(%rdi) ; p1->regs[8] = rsi
movq %rdi, 56(%rdi) ; p1->regs[7] = rdi
movq %rbp, 48(%rdi) ; p1->regs[6] = rbp
movq %r8, 40(%rdi) ; p1->regs[5] = r8
movq %r9, 32(%rdi) ; p1->regs[4] = r9
movq %r12, 24(%rdi) ; p1->regs[3] = r12
movq %r13, 16(%rdi) ; p1->regs[2] = r13
movq %r14, 8(%rdi) ; p1->regs[1] = r14
movq %r15, (%rdi) ; p1->regs[0] = r15
xorq %rax, %rax ; rax = null
movq 48(%rsi), %rbp ; rbp = p2->regs[6]
movq 104(%rsi), %rsp ; rsp = p2->regs[13]
movq (%rsi), %r15 ; r15 = p2->regs[0]
movq 8(%rsi), %r14 ; r14 = p2->regs[1]
movq 16(%rsi), %r13 ; r13 = p2->regs[2]
movq 24(%rsi), %r12 ; r12 = p2->regs[3]
movq 32(%rsi), %r9 ; r9 = p2->regs[4]
movq 40(%rsi), %r8 ; r8 = p2->regs[5]
movq 56(%rsi), %rdi ; rdi = p2->regs[7], 8是rsi地址,最后赋值,9是ret地址,最后入栈
movq 80(%rsi), %rdx ; rdx = p2->regs[10]
movq 88(%rsi), %rcx ; rcx = p2->regs[11]
movq 96(%rsi), %rbx ; rbx = p2->regs[12]
leaq 8(%rsp), %rsp ; rsp退栈8字节
pushq 72(%rsi) ; 将返回代码地址ret入栈,这样函数ret后会将该值加载进rip指令寄存器,从而执行原来的代码
movq 64(%rsi), %rsi ; rsi = p2->regs[8]
ret
coctx_t* ctx 新创建的上下文对象
coctx_pfn_t pfn 上下文要执行的函数
const void* s 执行函数参数1
const void* s1 执行函数参数2
初始化协程
int coctx_make(coctx_t* ctx, coctx_pfn_t pfn, const void* s, const void* s1) {
char* sp = ctx->ss_sp + ctx->ss_size - sizeof(void*);
sp = (char*)((unsigned long)sp & -16LL);
memset(ctx->regs, 0, sizeof(ctx->regs));
void** ret_addr = (void**)(sp);
*ret_addr = (void*)pfn;
ctx->regs[kRSP] = sp; // 每个co给他一个独立的栈空间
ctx->regs[kRETAddr] = (char*)pfn; // 切换协程后的rip地址,这样即可做到切换协程后执行函数
ctx->regs[kRDI] = (char*)s; // 入参1地址放入rdi
ctx->regs[kRSI] = (char*)s1; // 入参2地址放入rsi
return 0;
}
stCoRoutine_t* co 需要唤醒的协程
void co_resume( stCoRoutine_t *co )
{
stCoRoutineEnv_t *env = co->env;
stCoRoutine_t *lpCurrRoutine = env->pCallStack[ env->iCallStackSize - 1 ]; // 取出当前运行的协程
if( !co->cStart ) // 协程未经初始化则进行协程初始化
{
coctx_make( &co->ctx,(coctx_pfn_t)CoRoutineFunc,co,0 );
co->cStart = 1;
}
env->pCallStack[ env->iCallStackSize++ ] = co; // 新协程压入
co_swap( lpCurrRoutine, co ); // 切入新协程
}
CoRoutineFunc函数
static int CoRoutineFunc( stCoRoutine_t *co,void * )
{
if( co->pfn )
{
co->pfn( co->arg ); // 此处可能调用co_yield切出
}
co->cEnd = 1; // 执行完毕
stCoRoutineEnv_t *env = co->env;
co_yield_env( env ); // 切出
return 0;
}
stCoEpoll_t ctx 协程上下文,包含所有当前线程的协程信息
struct pollfd fds[] 检测的文件描述符
nfds_t nfds 文件描述符数量
int timeout_ms 超时时长,毫秒
int co_poll( stCoEpoll_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout_ms )
{
return co_poll_inner(ctx, fds, nfds, timeout_ms, NULL);
}
graph TD
start([开始]) --> judge_timeout_0{"timeout==0"} --Y--> call_pollfunc["pollfunc(fds, nfds, timeout)"] --> finish([结束])
judge_timeout_0--N-->create_arg["创建stPoll_t类型变量arg"]-->set_epollfd["设置arg.iEpollfd为ctx->iEpollFd"]-->add_epoll["在ctx->iEpollFd上增加监听事件,事件数据为栈中的pPollItem[i]对象,处理函数为OnPollPreparePfn"]-->调用AddTimeout将arg加到计时循环队列中-->add_timeout_ok{增加是否成功}--Y-->yield_env["co_yield_env( co_get_curr_thread_env() )切出当前协程,切入下一个协程..."]-->timeout_occur[\超时检查触发resume,恢复当前协程/]-->rasecnt["记录raisecnt数"]
subgraph 清除操作
event_come["从超时队列中取出arg"]-->rewrite_event["回写事件信息到返回数组中"]-->clean_memory["清除分配的内存"]
end
rasecnt-->event_come;
clean_memory-->finish
总体的网络服务器操作流程是这样的: