brpc: bthread的bthread_fcontext_t

news/2024/5/18 0:39:06

定义

/*libcontext - a slightly more portable version of boost::contextCopyright 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 athttp://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;
#endiftypedef void* bthread_fcontext_t;#ifdef __cplusplus
extern "C"{
#endifintptr_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。

简单实例

#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;
}

结果:

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/


http://wed.xjx100/news/1296240.html

相关文章

python时间内存计算

记录python时间内存计算操作 时间计算采用time模块内存计算分别采用memory_profiler和psutil 1.占用时间计算&#xff1a;time import timedef funT(t):for i in range(t*10):passif __name____main__:t10begintime.time()funT(t)endtime.time()print(时间&#xff1a;{}s.for…

怎么开发国外客户,有哪些软件

开发国外客户可以通过一系列策略和技术手段来实现&#xff0c;其中一些专门的软件工具可以帮助企业更高效地完成这一过程。以下是一些建议和相关软件工具&#xff1a; 客户发现与信息搜集&#xff1a; 易谷歌地图数据采集大师&#xff1a;用于基于地理位置搜索和采集潜在客户信…

Qt plugin 开发UI界面插件

目录 1.创建接口 2.创建插件 3.创建插件界面 4.插件实现 5.创建应用工程 6.应用插件 1.创建接口 打开QtCreater&#xff0c;点击左上角“文件”->新建文件或项目&#xff0c;在弹窗中选择C/CHeader File。 输入文件名&#xff0c;选好路径&#xff08;可自行设置名称…

ThinkPHP审计(1) 不安全的SQL注入PHP反序列化链子phar利用简单的CMS审计实例

ThinkPHP代码审计(1) 不安全的SQL注入&PHP反序列化链子phar利用&简单的CMS审计实例 文章目录 ThinkPHP代码审计(1) 不安全的SQL注入&PHP反序列化链子phar利用&简单的CMS审计实例一.Thinkphp5不安全的SQL写法二.Thinkphp3 SQL注入三.Thinkphp链5.1.x结合phar实现…

Java项目:基于SSM+vue框架实现的人力资源管理系统设计与实现(源码+数据库+毕业论文+任务书)

一、项目简介 本项目是一套基于SSM框架实现的人力资源管理系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简单、功能…

蓝桥杯备考day2

1.1 map及其函数 map 提供一对一的数据处理能力&#xff0c;由于这个特性&#xff0c;它完成有可 能在我们处理一对一数据的时候&#xff0c;在编程上提供快速通道。map 中的第一 个值称为关键字(key)&#xff0c;每个关键字只能在 map 中出现一次&#xff0c;第二个称为该 关…

2024年华为OD机试真题-符号运算-Python-OD统一考试(C卷)

题目描述: 给定一个表达式,求其分数计算结果 表达式的限制如下: 1. 所有的输入数字皆为正整数(包括0) 2. 仅支持四则运算(+-*/)和括号 3. 结果为整数或分数, 分数必须化为最简格式(比如6, 3/4, 7/8, 90/7) 4. 除数可能为0,如果遇到这种情况,直接输出"ERROR" 5.…

vue实现海康h5player问题汇总

1. 引入问题 最开始写的时候&#xff0c;把h5player封装成了一个组件&#xff0c;把资源文件随便放在了一个目录下&#xff0c; 直接在子组件中引入&#xff0c;报错window.JSPlugin is not a constructor 或者JSPlugin is not defined 初步分析应该是引入资源文件失败&#x…