brpc: bthread的bthread_fcontext_t

定义

cpp 复制代码
/*

    libcontext - a slightly more portable version of boost::context

    Copyright Martin Husemann 2013.
    Copyright Oliver Kowalke 2009.
    Copyright Sergue E. Leontiev 2013.
    Copyright Thomas Sailer 2013.
    Minor modifications by Tomasz Wlostowski 2016.

 Distributed under the Boost Software License, Version 1.0.
      (See accompanying file LICENSE_1_0.txt or copy at
            http://www.boost.org/LICENSE_1_0.txt)

*/

#ifndef BTHREAD_CONTEXT_H
#define BTHREAD_CONTEXT_H

#include <stdint.h>
#include <stdio.h>
#include <stddef.h>

#if defined(__GNUC__) || defined(__APPLE__)

  #define BTHREAD_CONTEXT_COMPILER_gcc

  #if defined(__linux__)
	#ifdef __x86_64__
	    #define BTHREAD_CONTEXT_PLATFORM_linux_x86_64
	    #define BTHREAD_CONTEXT_CALL_CONVENTION

	#elif __i386__
	    #define BTHREAD_CONTEXT_PLATFORM_linux_i386
	    #define BTHREAD_CONTEXT_CALL_CONVENTION
	#elif __arm__
	    #define BTHREAD_CONTEXT_PLATFORM_linux_arm32
	    #define BTHREAD_CONTEXT_CALL_CONVENTION
	#elif __aarch64__
	    #define BTHREAD_CONTEXT_PLATFORM_linux_arm64
	    #define BTHREAD_CONTEXT_CALL_CONVENTION
        #elif __loongarch64
            #define BTHREAD_CONTEXT_PLATFORM_linux_loongarch64
            #define BTHREAD_CONTEXT_CALL_CONVENTION
	#endif

  #elif defined(__MINGW32__) || defined (__MINGW64__)
	#if defined(__x86_64__)
	    #define BTHREAD_CONTEXT_COMPILER_gcc
    	    #define BTHREAD_CONTEXT_PLATFORM_windows_x86_64
	    #define BTHREAD_CONTEXT_CALL_CONVENTION
	#elif defined(__i386__)
	    #define BTHREAD_CONTEXT_COMPILER_gcc
	    #define BTHREAD_CONTEXT_PLATFORM_windows_i386
	    #define BTHREAD_CONTEXT_CALL_CONVENTION __cdecl
	#endif

  #elif defined(__APPLE__) && defined(__MACH__)
	#if defined (__i386__)
	    #define BTHREAD_CONTEXT_PLATFORM_apple_i386
	    #define BTHREAD_CONTEXT_CALL_CONVENTION
	#elif defined (__x86_64__)
	    #define BTHREAD_CONTEXT_PLATFORM_apple_x86_64
	    #define BTHREAD_CONTEXT_CALL_CONVENTION
	#elif defined (__aarch64__)
	    #define BTHREAD_CONTEXT_PLATFORM_apple_arm64
	    #define BTHREAD_CONTEXT_CALL_CONVENTION
    #endif
  #endif

#endif

#if defined(_WIN32_WCE)
typedef int intptr_t;
#endif

typedef void* bthread_fcontext_t;

#ifdef __cplusplus
extern "C"{
#endif

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));

#ifdef __cplusplus
};
#endif

#endif  // BTHREAD_CONTEXT_H

所谓bthread,或者说一众fiber的本质,就是能绕过系统调用(线程切换),实现执行状态的切换(每个程序,都是一个状态机,cpu就是无情的不断执行指令的机器),而context,就是保存当前执行状态的struct,该空间是在用户态被创建,所以同线程下的context间的切换,一般不涉及系统调用。

bthread的context提供两个方法:

bthread_make_fcontextbthread_jump_fcontext , 用来创建一个context,以及保存当前context并跳转到另一个context。

简单实例

cpp 复制代码
#include <bthread/context.h>
#include <iostream>
#include <bthread/bthread.h>

::bthread_fcontext_t fc1;
::bthread_fcontext_t fc2;
::bthread_fcontext_t fc_tmp;
intptr_t  g_num1 = 0;
intptr_t  g_num2 = 0;

static void jumper1(intptr_t param) {
    for(int i = 0; i < 9; i++) {
        LOG(INFO) << "context1: g_num1 = " << ++g_num1 << ", then jump to context2";
        ::bthread_jump_fcontext(&fc1, fc2, 0);
    }
    // 最后结尾,jump到原始上下文中,继续执行
    LOG(INFO) << "context1: g_num1 = " << ++g_num1 << ", then jump to main";
    ::bthread_jump_fcontext(&fc1, fc_tmp, 0);
}
static void jumper2(intptr_t param) {
    for(int i = 0; i < 10; i++) {
        LOG(INFO) << "context2: g_num2 = " << ++g_num2 << ", then jump to context1";
        ::bthread_jump_fcontext(&fc2, fc1, 0);
    }
}

int main() {
    static const std::size_t stack_size = 8192;

    // 创建两个上下文
    void *sp1 = ::malloc(stack_size);
    fc1 = ::bthread_make_fcontext((char*)sp1 + stack_size, stack_size, jumper1);

    void *sp2 = ::malloc(stack_size);
    fc2 = ::bthread_make_fcontext((char*)sp2 + stack_size, stack_size, jumper2); 

    // 将当前上下文保存到fc_tmp,并切换上下为fc2
    ::bthread_jump_fcontext(&fc_tmp, fc2, 0);

    LOG(INFO) << "done";

    ::free(sp1);
    ::free(sp2);
    return 0;
}

结果:

cpp 复制代码
root@ac2342fc89f6:/home/brpc/example/demo/build# ./context 
I0410 20:34:45.199594 74060     0 /home/brpc/example/demo/context.cpp:30] context2: g_num2 = 1, then jump to context1
I0410 20:34:45.199747 74060     0 /home/brpc/example/demo/context.cpp:21] context1: g_num1 = 1, then jump to context2
I0410 20:34:45.199760 74060     0 /home/brpc/example/demo/context.cpp:30] context2: g_num2 = 2, then jump to context1
I0410 20:34:45.199767 74060     0 /home/brpc/example/demo/context.cpp:21] context1: g_num1 = 2, then jump to context2
I0410 20:34:45.199771 74060     0 /home/brpc/example/demo/context.cpp:30] context2: g_num2 = 3, then jump to context1
I0410 20:34:45.199777 74060     0 /home/brpc/example/demo/context.cpp:21] context1: g_num1 = 3, then jump to context2
I0410 20:34:45.199780 74060     0 /home/brpc/example/demo/context.cpp:30] context2: g_num2 = 4, then jump to context1
I0410 20:34:45.199787 74060     0 /home/brpc/example/demo/context.cpp:21] context1: g_num1 = 4, then jump to context2
I0410 20:34:45.199789 74060     0 /home/brpc/example/demo/context.cpp:30] context2: g_num2 = 5, then jump to context1
I0410 20:34:45.199792 74060     0 /home/brpc/example/demo/context.cpp:21] context1: g_num1 = 5, then jump to context2
I0410 20:34:45.199799 74060     0 /home/brpc/example/demo/context.cpp:30] context2: g_num2 = 6, then jump to context1
I0410 20:34:45.199802 74060     0 /home/brpc/example/demo/context.cpp:21] context1: g_num1 = 6, then jump to context2
I0410 20:34:45.199804 74060     0 /home/brpc/example/demo/context.cpp:30] context2: g_num2 = 7, then jump to context1
I0410 20:34:45.199807 74060     0 /home/brpc/example/demo/context.cpp:21] context1: g_num1 = 7, then jump to context2
I0410 20:34:45.199809 74060     0 /home/brpc/example/demo/context.cpp:30] context2: g_num2 = 8, then jump to context1
I0410 20:34:45.199812 74060     0 /home/brpc/example/demo/context.cpp:21] context1: g_num1 = 8, then jump to context2
I0410 20:34:45.199820 74060     0 /home/brpc/example/demo/context.cpp:30] context2: g_num2 = 9, then jump to context1
I0410 20:34:45.199822 74060     0 /home/brpc/example/demo/context.cpp:21] context1: g_num1 = 9, then jump to context2
I0410 20:34:45.199825 74060     0 /home/brpc/example/demo/context.cpp:30] context2: g_num2 = 10, then jump to context1
I0410 20:34:45.199828 74060     0 /home/brpc/example/demo/context.cpp:25] context1: g_num1 = 10, then jump to main

可以发现,整个上下文的切换,均在同一个线程上完成,切换的速度也相当快,几纳秒到几十纳秒即可完成。

上述代码的逻辑也非常简单,在堆上申请两块内存来作为context(类比thread的栈),然后开始不断来回切换:保存jumper1的状态,并切换回jumper2; 保存jumper2的状态,并切换回jumper1。

这两个函数的逻辑是由汇编写的,bthread_make_fcontext大致的逻辑是构造一个栈结构(实际上在OS视角,就是用户申请的堆内存)。

bthread_jump_fcontext 则是保存当前函数调用的状态,或者说上下文(即各种寄存器、栈指针等等),加载另一个函数调用的上下文。

一旦切换context,cpu就会按照context对应的位置,开始无情地执行指令,直到被再次切换。

tips:一个小细节,如果jumper1最终不切回main的context,从 LOG(INFO) << "done"; 开始就不会被执行。

这两个函数的具体实现细节可移步brpc了解(参考链接)。

参考

https://brpc.apache.org/zh/docs/blogs/sourcecodes/bthread/

相关推荐
Ritsu栗子16 分钟前
代码随想录算法训练营day35
c++·算法
好一点,更好一点26 分钟前
systemC示例
开发语言·c++·算法
卷卷的小趴菜学编程1 小时前
c++之List容器的模拟实现
服务器·c语言·开发语言·数据结构·c++·算法·list
年轮不改1 小时前
Qt基础项目篇——Qt版Word字处理软件
c++·qt
玉蜉蝣1 小时前
PAT甲级-1014 Waiting in Line
c++·算法·队列·pat甲·银行排队问题
半盏茶香3 小时前
扬帆数据结构算法之雅舟航程,漫步C++幽谷——LeetCode刷题之移除链表元素、反转链表、找中间节点、合并有序链表、链表的回文结构
数据结构·c++·算法
哎呦,帅小伙哦3 小时前
Effective C++ 规则41:了解隐式接口和编译期多态
c++·effective c++
DARLING Zero two♡4 小时前
【初阶数据结构】逆流的回环链桥:双链表
c语言·数据结构·c++·链表·双链表
9毫米的幻想4 小时前
【Linux系统】—— 编译器 gcc/g++ 的使用
linux·运维·服务器·c语言·c++
Cando学算法4 小时前
Codeforces Round 1000 (Div. 2)(前三题)
数据结构·c++·算法