小鱼塘--自说自话的地方

  • 小玩意
  • 小想法
记录自己技术和想法地方
  1. 首页
  2. c++
  3. 正文

协程之跳转代码研究一

26 12 月, 2024 671点热度 0人点赞 0条评论
内容目录

背景

不管什么协程库,最根本就是切换代码,类似c语言的关键词goto, 本质就是在汇编层切换eip,所以建议记住这句话,其他都是在这个前提写代码。

环境

windows 开发环境,我基本在windows开发,偶尔写linux 服务器写一下c++ 简单的代码,所以我的开发环境就是windows,我用vs2019进行开发。

技术点

  1. 使用SetThreadContext 和 GetThreadContext

简单需求1

我们使用SetThreadContext 和 GetTrheadContext 实现goto 跳转。

代码

void test(){
    int i = 0;
    HANDLE h = GetCurrentThread();
    CONTEXT context;
    GetThreadCOntext(h, context);
    if(i > 0){
        printf("i > 0")
    }
    i = 1;
    SetThreadContext(h, context);
    printf("end");
}

这个代码其实错误的,没有任何作用

可以跑的代码

这个是 __stdcall ,不然下一个地址不对,所以一定要这么设置,不然add esp,0x4 导致堆栈不对

void __stdcall getEip(int& eip_value) {
    int r = 0;
    __asm {
        mov eax, [ebp + 0x4];
        mov r, eax;
    }

    eip_value = r;
}

void testContext() {
    int i = 0;
    HANDLE h = GetCurrentThread();
    CONTEXT context;
    int eip_value = 0x0;
    int esp_value = 0x0;
    int ebp_value = 0x0;
    context.ContextFlags = CONTEXT_CONTROL;
    GetThreadContext(h, &context);
    getEip(eip_value);
    __asm {
        mov esp_value, esp;
        mov ebp_value, ebp;
    }
    if (i > 0) {
        printf("i > 0");
        return;
    }
    i = 1;
    context.Eip = eip_value;
    context.Esp = esp_value;
    context.Ebp = ebp_value;
    context.ContextFlags = CONTEXT_CONTROL;
    SetThreadContext(h, &context);
    printf("end");
}

void testContext2() {
    int i = 0;
    int eip_value = 0x0;
    int esp_value = 0x0;
    int ebp_value = 0x0;
    getEip(eip_value);

    if (i > 0) {
        printf("i > 0");
        return;
    }
    i = 1;
    __asm {
        jmp eip_value;
    }
    printf("end");
}

int main()
{
    testContext2();
    getchar();
    return 0;
}

知识点

  1. GetTrheadContext和SetThreadContext 调用应该是要在别的线程里面,一般前提都是把别的线程暂停掉,然后获取当前运行的eip(rip)指针。如果当前线程获取,就是GetTrheadContext这个函数里面eip,其实它里面调用系统api,获取eip(rip)指向这个函数结尾ret.
  2. getEip 必须是stdcall 不然eip指向汇编代码是add esp,0x4,这个函数调用约定,你可以搜索stdcall
  3. testContext 使用调用SetThreadContext ,testContext2纯汇编实现。
  4. 这里函数里面,基本不用考虑栈的问题,但协程需要考虑栈的问题,因为c/c++ 这个是函数概念,函数就堆栈概念,汇编也有函数这个说法,栈的作用是用来传递参数(x86汇编,x64基本通过寄存器)和局部变量使用,协程相当于把函数继续切分,我感觉其实协程就当成函数看也是可以的。
  5. 对机器码来说没有函数概念,它只是执行0101而已,都是上层的规则,计算机语法就是跟堆积木一样,一层层堆积,上层越来越复杂,同时也越来越好用,这个本质跟我们分层开发差不多,我们开发模块的时候,不断分层,封装,不断组合功能和业务。业务组合再组合系统。

目的

  1. 这个只是为了自己学习协程本质东西,只是用一个很简单东西说明协程的原理,我最开始不明白它怎么切换的,后面找了一些资料和思考才明白,在我没有认识这个协程之前,我一直以为协程之是用线程封装一个东西而已。
  2. 为了跟好理解协程,我后面会写一个简单系列,用简单代码实现一个简单协程,最重要写清楚原理,并不是为了开发这个协程库,协程对我看来一种更加优雅的异步写法,c#是没有协程概念,但它有异步概念,用await实现确实非常方便,node js用await也非常方便。c# await+ Task(线程)实现效果感觉也不差,不过这个客户端实现,因为客户端对高并发没有要求,对CPU消耗不大,服务器高并发(要求高就不一样),对于用户量巨大的,考虑用协程实现减少线程切换,但大部分基本不用考虑这么多,同时IO复用+异步+线程池+任务队列性能也非常不错了,同时用协程,对调试代码也会代码不方便,如果crash,堆栈只能部分能看,因为堆栈已经别切换了,上一个真正堆栈在哪里,这个也是为什么开发规范为什么要用禁用goto原因,所以本文为什么要模拟实现goto,也是说明协程类似goto,只是可以跨函数调用而已
标签: 协程
最后更新:26 12 月, 2024

小鱼儿

爱研究技术,爱玩LOL

点赞
< 上一篇

COPYRIGHT © 2022 小鱼塘. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

湘ICP备18005349号