前言
协程是一个比较新的概念,它的使用场景越来越广泛,一些服务器的后台,高并发的场景,有时候就会就会用到协程,协程简单理解就是对进程,线程的一些改进和优化,其实对于线程也存在一定的开销,其包括创建,销毁,上下文切换,同步,条件变量,读写锁都涉及到用户态到内核态之间的切换,协程的改进点在于它调度开销小,不存在同步,死锁,资源竞争等问题,这是因为协程的调度是由我们用户态自定义完成的,是主动调度,而进程和线程的调度由内核完成,是被动调度,对于C语言来说有不少的协程库,比如libtask,libco,libgo,实现协程的关键在于上下文的维护,栈的维护,协程在函数运行过程中,它会主动让出CPU,保存当前的函数上下文,适当的时候,再将保存的上下文恢复到CPU继续运行,其中上下文包括寄存器,PC,堆栈.对于ARM寄存器主要包括R0-R15,R0-R12是通用寄存器,R13是堆栈指针SP,R14是链接寄存器,使用BL跳转时,自动将下一条指令的地址保存到LR中,R15是PC指针,还有一个就是ATPCS,ATPCS规定子程序通过寄存器R0-R3进行传参,多余的参数就通过堆栈进行保存,返回值保存在R0,对于协程来说,上下文切换的时候不仅仅是保存寄存器,还有信号量集等协程拥有的资源,还要将协程当前的栈内容保存下来(一个函数在执行完退出后它的栈不保存的话就会被系统自动销毁),可以参考POSIX API:getcontext(用于保存当前上下文)和setcontext(用于切换上下文),swapcontext函数(会保存当前上下文并切换到另一个上下文),makecontext(创建一个上下文)的实现,这些函数是跨平台的,不同平台下有不同的实现
初步分析
test.c
#include <stdio.h>
#include <unistd.h>
typedef struct mcontext
{
int regs[16];
}mcontext_t;
int getmcontext(mcontext_t *);
int setmcontext(mcontext_t *);
int main(void)
{
int count = 0;
mcontext_t ctx;
getmcontext(&ctx);
printf("count : %d\n",count++);
sleep(1);
setmcontext(&ctx);
puts("main exit\n");
return 0;
}
lib.S
.globl getmcontext
getmcontext:
/* 保存所有当前寄存器,包括sp和lr */
str r1, [r0, #4] // mcontext.mc_r1 = r1
str r2, [r0, #8] // mcontext.mc_r2 = r2
str r3, [r0, #12] // mcontext.mc_r3 = r3
str r4, [r0, #16] // mcontext.mc_r4 = r4
str r5, [r0, #20] // mcontext.mc_r5 = r5
str r6, [r0, #24] // mcontext.mc_r6 = r6
str r7, [r0, #28] // mcontext.mc_r7 = r7
str r8, [r0, #32] // mcontext.mc_r8 = r8
str r9, [r0, #36] // mcontext.mc_r9 = r9
str r10, [r0, #40] // mcontext.mc_r10 = r10
str r11, [r0, #44] // mcontext.mc_fp = r11
str r12, [r0, #48] // mcontext.mc_ip = r12
str r13, [r0, #52] // mcontext.mc_sp = r13
str r14, [r0, #56] // mcontext.mc_lr = r14
mov r0, #0 // return 0
mov pc, lr
.globl setmcontext
setmcontext:
// 恢复指定context的所有寄存器,包括sp和lr
ldr r1, [r0, #4] // r1 = mcontext.mc_r1
ldr r2, [r0, #8] // r2 = mcontext.mc_r2
ldr r3, [r0, #12] // r3 = mcontext.mc_r3
ldr r4, [r0, #16] // r4 = mcontext.mc_r4
ldr r5, [r0, #20] // r5 = mcontext.mc_r5
ldr r6, [r0, #24] // r6 = mcontext.mc_r6
ldr r7, [r0, #28] // r7 = mcontext.mc_r7
ldr r8, [r0, #