博客传送门:libco源码分析、学习笔记(腾讯协程开源库) x86_64部分
协程基本原理就是上下文和栈的切换,本篇将libco中核心代码抽离出来实现一个迷你的协程实现演示。
一、Linux下效果展示
在deepin (64位 Linux)和mac os上测试成功了。贴一下效果图:
deepin(64位)效果图:
macOs(64位)效果图:
红色部分为并发部分。
二、代码
新建一个coctx_swap.S汇编文件,代码如下:
代码来源于libco (注意:libco此上下文切换汇编代码有bug,详情参考GitHub的libco项目)
.globl coctx_swap
#if !defined( __APPLE__ ) && !defined( __FreeBSD__ )
.type coctx_swap, @function
#endif
coctx_swap:
leaq 8(%rsp),%rax
leaq 112(%rdi),%rsp
pushq %rax
pushq %rbx
pushq %rcx
pushq %rdx
pushq -8(%rax) //ret func addr
pushq %rsi
pushq %rdi
pushq %rbp
pushq %r8
pushq %r9
pushq %r12
pushq %r13
pushq %r14
pushq %r15
movq %rsi, %rsp
popq %r15
popq %r14
popq %r13
popq %r12
popq %r9
popq %r8
popq %rbp
popq %rdi
popq %rsi
popq %rax //ret func addr
popq %rdx
popq %rcx
popq %rbx
popq %rsp
pushq %rax
xorl %eax, %eax
ret
此汇编的作用是上下文切换。
新建一个coctx.c
#include "coctx.h"
#include <string.h>
#define RSP 0
#define RIP 1
#define RBX 2
#define RDI 3
#define RSI 4
#define RBP 5
#define R12 6
#define R13 7
#define R14 8
#define R15 9
#define RDX 10
#define RCX 11
#define R8 12
#define R9 13
//-------------
// 64 bit
//low | regs[0]: r15 |
// | regs[1]: r14 |
// | regs[2]: r13 |
// | regs[3]: r12 |
// | regs[4]: r9 |
// | regs[5]: r8 |
// | regs[6]: rbp |
// | regs[7]: rdi |
// | regs[8]: rsi |
// | regs[9]: ret | //ret func addr
// | regs[10]: rdx |
// | regs[11]: rcx |
// | regs[12]: rbx |
//hig | regs[13]: rsp |
enum
{
kRDI = 7,
kRSI = 8,
kRETAddr = 9,
kRSP = 13,
};
//64 bit
extern void coctx_swap( coctx_t *,coctx_t* ) asm("coctx_swap");
int coctx_make( coctx_t *ctx,coctx_pfn_t pfn)
{
char *sp = ctx->ss_sp + ctx->ss_size;
sp = (char*) ((unsigned long)sp & -16LL );
memset(ctx->regs, 0, sizeof(ctx->regs));
ctx->regs[ kRSP ] = sp - 8;
ctx->regs[ kRETAddr] = (char*)pfn;
return 0;
}
int coctx_init( coctx_t *ctx )
{
memset( ctx,0,sizeof(*ctx));
return 0;
}
新建一个coctx.h
#include <stdlib.h>
typedef void* (*coctx_pfn_t)( void* s, void* s2 );
struct coctx_t
{
void *regs[ 14 ];
size_t ss_size;
char *ss_sp;
};
typedef struct coctx_t coctx_t;
int coctx_init( coctx_t *ctx );
int coctx_make( coctx_t *ctx,coctx_pfn_t pfn);
到此为止短小精悍的协程库已经建立完毕了
三、使用
建立main.c
#include<stdio.h>
#include"coctx.h"
extern void coctx_swap( coctx_t *,coctx_t* ) asm("coctx_swap");
coctx_t ctx_func,ctx_main;
int i=0;
void *func(void*arga,void*argb){
while(1){
printf("hello \n");
i++;
coctx_swap(&ctx_func,&ctx_main);//swap context
}
}
int main(){
printf("start\n");
coctx_init(&ctx_main);
coctx_init(&ctx_func);
//alloc stack memory
ctx_func.ss_sp=malloc(128);
ctx_func.ss_size=128;
//init func coroutine
coctx_make( &ctx_func,func);//struct ctx, main function
printf("make\n");
while(1){
printf("nihao \n");
i++;
if(i>=5)
return 0;
coctx_swap(&ctx_main,&ctx_func); //swap context
}
}
其中func函数和main函数中两个循环交替运行,预期结果是打印一个nihao,再打印一个hello,再打印nihao。。。在没有操作系统强参与下实现了并发。
使用下列命令编译运行:
gcc -o run main.c coctx.c coctx_swap.S
./run