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/

相关推荐
Antonio91517 分钟前
【CMake】使用CMake在Visual Studio内构建多文件夹工程
开发语言·c++·visual studio
LyaJpunov31 分钟前
C++中move和forword的区别
开发语言·c++
程序猿练习生35 分钟前
C++速通LeetCode中等第9题-合并区间
开发语言·c++·leetcode
z千鑫43 分钟前
【人工智能】如何利用AI轻松将java,c++等代码转换为Python语言?程序员必读
java·c++·人工智能·gpt·agent·ai编程·ai工具
一名路过的小码农1 小时前
C/C++动态库函数导出 windows
c语言·开发语言·c++
Ddddddd_1581 小时前
C++ | Leetcode C++题解之第416题分割等和子集
c++·leetcode·题解
编程版小新2 小时前
C++初阶:STL详解(四)——vector迭代器失效问题
开发语言·c++·迭代器·vector·迭代器失效
AlexMercer10123 小时前
【C++】二、数据类型 (同C)
c语言·开发语言·数据结构·c++·笔记·算法
小灰灰爱代码4 小时前
C++——求3个数中最大的数(分别考虑整数、双精度数、长整数的情况),用函数模板来实现。
开发语言·c++·算法
BeyondESH5 小时前
Linux线程同步—竞态条件和互斥锁(C语言)
linux·服务器·c++