Context
context是bthread的上下文,提供上下文初始化和上下文切换功能
源码位置:bthread/context.h bthread/context.cpp
接口api
1 2 3 4 5 6 7 8 9 10 11 12 13 | // 上下文切换,从ofc切换到nfc intptr_t BTHREAD_CONTEXT_CALL_CONVENTION bthread_jump_fcontext( bthread_fcontext_t * ofc, bthread_fcontext_t nfc, intptr_t vp, bool preserve_fpu = false ); // 上下文初始化 bthread_fcontext_t BTHREAD_CONTEXT_CALL_CONVENTION bthread_make_fcontext( void * sp, // 栈顶指针 size_t size, // 栈长度 void (* fn)( intptr_t)); // 栈初始化函数 |
x86_64下接口实现
bthread_make_fcontext:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | __asm ( ".text\n" ".globl bthread_make_fcontext\n" ".type bthread_make_fcontext,@function\n" ".align 16\n" "bthread_make_fcontext:\n" " movq %rdi, %rax\n" " andq $-16, %rax\n" " leaq -0x48(%rax), %rax\n" " movq %rdx, 0x38(%rax)\n" " stmxcsr (%rax)\n" " fnstcw 0x4(%rax)\n" " leaq finish(%rip), %rcx\n" " movq %rcx, 0x40(%rax)\n" " ret \n" "finish:\n" " xorq %rdi, %rdi\n" " call _exit@PLT\n" " hlt\n" ".size bthread_make_fcontext,.-bthread_make_fcontext\n" ".section .note.GNU-stack,\"\",%progbits\n" ); |
该函数执行完成后,栈如图
其中,栈顶保存finish函数的地址,finish函数中调用_exit(),sp - 0x08位置保存用户函数指针fn。由于寄存器惯例约定保存6个寄存器,因此预留6个64-bit,这样可以和上下文切换逻辑保持一致。将sp-0x48作为新的%rsp返回调用者,调用者保存此指针用于上下文切换作为bthread_jump_fcontext的参数
bthread_jump_fcontext:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | __asm ( ".text\n" ".globl bthread_jump_fcontext\n" ".type bthread_jump_fcontext,@function\n" ".align 16\n" "bthread_jump_fcontext:\n" " pushq %rbp \n" " pushq %rbx \n" " pushq %r15 \n" " pushq %r14 \n" " pushq %r13 \n" " pushq %r12 \n" " leaq -0x8(%rsp), %rsp\n" " cmp $0, %rcx\n" " je 1f\n" " stmxcsr (%rsp)\n" " fnstcw 0x4(%rsp)\n" "1:\n" " movq %rsp, (%rdi)\n" // 用ofc指针保存当前bthread的%rsp,用于下次从其他bthread切换回来使用 " movq %rsi, %rsp\n" // 恢复nfc地址到rsp寄存器中 " cmp $0, %rcx\n" " je 2f\n" " ldmxcsr (%rsp)\n" // 恢复之前保存的MXCSR寄存器 " fldcw 0x4(%rsp)\n" // 恢复之前保存的FPU控制字 "2:\n" " leaq 0x8(%rsp), %rsp\n" // rsp加0x8,跳过MXCSR和FPU " popq %r12 \n" // 恢复%r12寄存器,下同 " popq %r13 \n" " popq %r14 \n" " popq %r15 \n" " popq %rbx \n" " popq %rbp \n" " popq %r8\n" // 将之前保存的%rip恢复到r8,即上次发生上下文切换时此bthread的下一条指令地址 " movq %rdx, %rax\n" // 将第3个参数vp保存到%rax " movq %rdx, %rdi\n" // 将第3个参数vp保存到%rdi用于下一个函数调用的第一个参数 " jmp *%r8\n" // 跳转到%r8的函数地址,即之前的%rip,到这里完成一次上下文切换 ".size bthread_jump_fcontext,.-bthread_jump_fcontext\n" ".section .note.GNU-stack,\"\",%progbits\n" ); |
Stack
bthread当前实现提供三种大小类型的栈,分别为32K、1M、8M,默认使用1M大小的栈。bthread通过mmap分配page大小对齐的栈空间,并在底部通过mprotect将一段4096字节大小的空间设置为不可访问,目的是防止栈溢出。stack从bottom开始创建上下文,如图:
stack设计为工厂模式,根据提供的参数获取对应stack