st(state-threads) coroutine和setjmp/longjmp的关系

理解协程的运行流程 这是我整合自己对协程的一些理解,文章中的图我有些是在网上直接拷贝的,在这里感谢原作者。这是http服务器整合协程的流程图: 代码图: 从控制台传进来的参数allfont,font,每次循环会创建font个task,task的概念可以说就是协程。协程通过stCoRoutine_t结构来描述,就像我们进程的task_struct一样,保存着运行时,关于协程运行环境的所有信息,所以每个task有一 阅读详情

st(state-threads) https://github.com/winlinvip/state-threads

以及基于st的RTMP/HLS服务器:https://github.com/winlinvip/simple-rtmp-server

st是实现了coroutine的一套机制,即用户态线程,或者叫做协程。将epoll(async,nonblocking socket)的非阻塞变成协程的方式,将所有状态空间都放到stack中,避免异步的大循环和状态空间的判断。

关于st的详细介绍,参考翻译:http://blog.csdn.net/win_lin/article/details/8242653

本文主要介绍了coroutine基于setjmp和longjmp的实现机制。

我将st进行了简化,去掉了其他系统,只考虑linux系统,以及i386/x86_64/arm/mips四种cpu系列,参考:https://github.com/winlinvip/simple-rtmp-server/tree/master/trunk/research/st

st最关键的地方在于需要重新分配stack,譬如在heap分配stack,支持超大并发。

mips和arm可以直接设置stack;而i386和x86_64的CPU体系在glibc2.4以上为了安全性考虑,jmp_buf的结构不是那么清楚,直接设置jmp_buf的sp是不可行的:

    /*
     * Starting with glibc 2.4, JB_SP definitions are not public anymore.
     * They, however, can still be found in glibc source tree in
     * architecture-specific "jmpbuf-offsets.h" files.
     * Most importantly, the content of jmp_buf is mangled by setjmp to make
     * it completely opaque (the mangling can be disabled by setting the
     * LD_POINTER_GUARD environment variable before application execution).
     * Therefore we will use built-in _st_md_cxt_save/_st_md_cxt_restore
     * functions as a setjmp/longjmp replacement wherever they are available
     * unless USE_LIBC_SETJMP is defined.
     */

可以参考glic的代码:http://ftp.gnu.org/gnu/glibc/

MIPS

这种最简单,实际上setjmp的jmp_buf提供了sp和pc,只需要把jmp_buf的sp设置为分配的stack,把pc设置为main地址就可以。

#if defined(__mips__)
    #define MD_STACK_GROWS_DOWN
    
    #define MD_INIT_CONTEXT(_thread, _sp, _main)               \
        ST_BEGIN_MACRO                                           \
        MD_SETJMP((_thread)->context);                           \
        _thread->context[0].__jmpbuf[0].__pc = (__ptr_t) _main;  \
        _thread->context[0].__jmpbuf[0].__sp = _sp;              \
        ST_END_MACRO


MIPS的libc的头文件定义jmp_buf时就指定了pc和sp。

ARM

ARM实际上st也是用的glibc的setjmp和longjmp,明显arm的glibc的jmp_buf是结构可知的,参考arm的setjmp头文件:

    /**
        /usr/arm-linux-gnueabi/include/bits/setjmp.h
        #ifndef _ASM
        The exact set of registers saved may depend on the particular core
           in use, as some coprocessor registers may need to be saved.  The C
           Library ABI requires that the buffer be 8-byte aligned, and
           recommends that the buffer contain 64 words.  The first 28 words
           are occupied by v1-v6, sl, fp, sp, pc, d8-d15, and fpscr.  (Note
           that d8-15 require 17 words, due to the use of fstmx.)
        typedef int __jmp_buf[64] __attribute__((__aligned__ (8)));
        
        the layout of setjmp for arm:
            0-5: v1-v6 
            6: sl
            7: fp
            8: sp
            9: pc
            10-26: d8-d15 17words
            27: fpscr
    */
    /**
    For example, on raspberry-pi, armv6 cpu:
        (gdb) x /64 env_func1[0].__jmpbuf
            v1, 0:  0x00	0x00	0x00	0x00	
            v2, 1:  0x00	0x00	0x00	0x00
            v3, 2:  0x2c	0x84	0x00	0x00	
            v4, 3:  0x00	0x00	0x00	0x00
            v5, 4:  0x00	0x00	0x00	0x00	
            v6, 5:  0x00	0x00	0x00	0x00
            sl, 6:  0x00	0xf0	0xff	0xb6	
            fp, 7:  0x9c	0xfb	0xff	0xbe
            sp, 8:  0x88	0xfb	0xff	0xbe	
            pc, 9:  0x08	0x85	0x00	0x00
        (gdb) p /x $sp
        $5 = 0xbefffb88
        (gdb) p /x $pc
        $4 = 0x850c
    */

调试的结果显示也是没有问题。st需要修改下JB_RSP,从20改成8,参考:https://github.com/winlinvip/simple-rtmp-server/issues/190

i386/x86_64

st在i386和x86_64下面,都定义了宏MD_USE_BUILTIN_SETJMP,也就是用st自己的md.S里面的setjmp和longjmp:

    /*
     * Starting with glibc 2.4, JB_SP definitions are not public anymore.
     * They, however, can still be found in glibc source tree in
     * architecture-specific "jmpbuf-offsets.h" files.
     * Most importantly, the content of jmp_buf is mangled by setjmp to make
     * it completely opaque (the mangling can be disabled by setting the
     * LD_POINTER_GUARD environment variable before application execution).
     * Therefore we will use built-in _st_md_cxt_save/_st_md_cxt_restore
     * functions as a setjmp/longjmp replacement wherever they are available
     * unless USE_LIBC_SETJMP is defined.
     */
    #if defined(__i386__)
        #define MD_STACK_GROWS_DOWN
        #define MD_USE_BUILTIN_SETJMP
        
        #if defined(__GLIBC__) && __GLIBC__ >= 2
            #ifndef JB_SP
                #define JB_SP 4
            #endif
            #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP]
        #else
            /* not an error but certainly cause for caution */
            #error "Untested use of old glibc on i386"
            #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp
        #endif
    #elif defined(__amd64__) || defined(__x86_64__)
        #define MD_STACK_GROWS_DOWN
        #define MD_USE_BUILTIN_SETJMP
        
        #ifndef JB_RSP
            #define JB_RSP 6
        #endif
        #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_RSP]

原因讲得很清楚,glibc2.4以上的jmp_buf的sp不能操作了,导致只能用st内建的setjmp和longjmp。

若使用glibc的setjmp和longjmp,即定义宏(参考下一章:ST宏定义)USE_LIBC_SETJMP,则出现segmentfault,gdb调试setjmp的jmp_buf:

        (gdb) x /64xb env_func1[0].__jmpbuf
        0x600ca0 <env_func1>:     0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
        0x600ca8 <env_func1+8>:   0xf8	0xc1	0x71	0xe5	0xa8	0x88	0xb4	0x15
        0x600cb0 <env_func1+16>:  0xa0	0x05	0x40	0x00	0x00	0x00	0x00	0x00
        0x600cb8 <env_func1+24>:  0x90	0xe4	0xff	0xff	0xff	0x7f	0x00	0x00
        0x600cc0 <env_func1+32>:  0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
        0x600cc8 <env_func1+40>:  0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
        0x600cd0 <env_func1+48>:  0xf8	0xc1	0x51	0xe5	0xa8	0x88	0xb4	0x15
        0x600cd8 <env_func1+56>:  0xf8	0xc1	0xd9	0x2f	0xd7	0x77	0x4b	0xea
        (gdb) p /x $sp
        $4 = 0x7fffffffe380

明显可以看到,jmp_buf中没有一个数据是和sp能对应上的,所以st在setjmp后设置sp就会有问题。

ST宏定义

s在make时可以定义宏,指定EXTRA_CFLAGS参数即可,参考说明:

##########################
# Other possible defines:
# To use poll(2) instead of select(2) for events checking:
# DEFINES += -DUSE_POLL
# You may prefer to use select for applications that have many threads
# using one file descriptor, and poll for applications that have many
# different file descriptors.  With USE_POLL poll() is called with at
# least one pollfd per I/O-blocked thread, so 1000 threads sharing one
# descriptor will poll 1000 identical pollfds and select would be more
# efficient.  But if the threads all use different descriptors poll()
# may be better depending on your operating system's implementation of
# poll and select.  Really, it's up to you.  Oh, and on some platforms
# poll() fails with more than a few dozen descriptors.
#
# Some platforms allow to define FD_SETSIZE (if select() is used), e.g.:
# DEFINES += -DFD_SETSIZE=4096
#
# To use malloc(3) instead of mmap(2) for stack allocation:
# DEFINES += -DMALLOC_STACK
#
# To provision more than the default 16 thread-specific-data keys
# (but not too many!):
# DEFINES += -DST_KEYS_MAX=<n>
#
# To start with more than the default 64 initial pollfd slots
# (but the table grows dynamically anyway):
# DEFINES += -DST_MIN_POLLFDS_SIZE=<n>
#
# Note that you can also add these defines by specifying them as
# make/gmake arguments (without editing this Makefile). For example:
#
# make EXTRA_CFLAGS=-DUSE_POLL <target>
#
# (replace make with gmake if needed).
#
# You can also modify the default selection of an alternative event
# notification mechanism. E.g., to enable kqueue(2) support (if it's not
# enabled by default):
#
# gmake EXTRA_CFLAGS=-DMD_HAVE_KQUEUE <target>
#
# or to disable default epoll(4) support:
#
# make EXTRA_CFLAGS=-UMD_HAVE_EPOLL <target>
#
##########################

譬如,st默认使用mmap分配栈,估计是为了提高性能,可以让st在heap分配栈,这样可以支持几百万个线程。编译时定义宏MALLOC_STACK:
make linux-debug EXTRA_CFLAGS="-DMALLOC_STACK"

或者在Makefile中改变默认的DEFINES也可以。

SETJMP和LONGJMP

看完了st的线程调度和生命周期,我才完全明白setjmp和longjmp的栈切换方式,以及st使用自己分配的stack到底如何切换。参考:http://blog.csdn.net/win_lin/article/details/40978665

考虑一个单线程程序,实际上程序是流水线执行的,也就是从main开始执行,进入各种子函数然后退出。参考:https://github.com/winlinvip/simple-rtmp-server/blob/master/trunk/research/arm/jmp_flow.cpp

/*
# for all supports setjmp and longjmp:
    g++ -g -O0 -o jmp_flow jmp_flow.cpp
*/
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>

jmp_buf context_level_0;

void func_level_0()
{
    const char* level_0_0 = "stack variables for func_level_0";
    int ret = setjmp(context_level_0);
    printf("func_level_0 ret=%d\n", ret);
    if (ret != 0) {
        printf("call by longjmp.\n");
        exit(0);
    }
}

int main(int argc, char** argv) 
{
    func_level_0();
    longjmp(context_level_0, 1);
    return 0;
}

调试这个程序:

(gdb) f 0
#0  func_level_0 () at jmp_flow.cpp:16
16	    if (ret != 0) {
(gdb) bt
#0  func_level_0 () at jmp_flow.cpp:16
#1  0x0000000000400725 in main (argc=1, argv=0x7fffffffe4b8) at jmp_flow.cpp:24
(gdb) i locals
level_0_0 = 0x400838 "stack variables for func_level_0"
ret = 0

在setjmp之后,stack是有效的,上层的变量值也对。而setjmp只是保存了各种指针,而没有保存完整的stack的拷贝。因此,若setjmp之后返回了,在longjmp回来时整个stack都是破坏的了(很显然)。

(gdb) f 0
#0  func_level_0 () at jmp_flow.cpp:16
16	    if (ret != 0) {
(gdb) bt
#0  func_level_0 () at jmp_flow.cpp:16
#1  0x0000000000400734 in main (argc=1, argv=0x7fffffffe4b8) at jmp_flow.cpp:25
(gdb) i locals
level_0_0 = 0x1 <error: Cannot access memory at address 0x1>
ret = 1

原因是这个函数返回后,栈已经释放了,再重新跳到这个地方执行,执行位置(PC)是对的,栈指针也是对的,但是栈的内容肯定是不一样了。

因此,longjmp到某个地方时,这个函数的堆栈实际上无效,访问变量和返回地址也是不可用的,因此longjmp只能在继续longjmp,这也就是为何有_st_thread_main的原因,永远不会从这个函数返回。

或者说,longjmp到某个函数之后,可以调用子函数,但只能通过longjmp来回到这个函数之外的函数。或者说,第一次longjmp的函数(即函数的thread_main),永远不能返回,只能通过longjmp跳转。

或者说,longjmp的目标只能是同一个stack,在不改变sp的情况下。而st那样需要跳来跳去的方式,必须在堆上分配sp,让每个线程私有自己的sp。

不分配stack的段错误

既然longjmp之后不能返回,若再次longjmp到其他的线程,堆栈是公用的,这时候应该会导致堆栈混淆。

查看代码,参考:https://github.com/winlinvip/simple-rtmp-server/blob/master/trunk/research/arm/jmp_2flow.cpp

/*
# for all supports setjmp and longjmp:
    g++ -g -O0 -o jmp_2flow jmp_2flow.cpp
*/
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>

jmp_buf context_thread_0;
jmp_buf context_thread_1;

void thread0_functions()
{
    int ret = setjmp(context_thread_0);
    // when ret is 0, create thread,
    // when ret is not 0, longjmp to this thread.
    if (ret == 0) {
        return;
    }
    
    int age = 10000;
    const char* name = "winlin";
    printf("[thread0] age=%d, name=%s\n", age, name);
    if (!setjmp(context_thread_0)) {
        printf("[thread0] switch to thread1\n");
        longjmp(context_thread_1, 1);
    }
    
    // crash, for the stack is modified by thread1.
    // name = 0x2b67004009c8 <error: Cannot access memory at address 0x2b67004009c8>
    printf("[thread0] terminated, age=%d, name=%s\n", age, name);
    exit(0);
}

void thread1_functions()
{
    int ret = setjmp(context_thread_1);
    // when ret is 0, create thread,
    // when ret is not 0, longjmp to this thread.
    if (ret == 0) {
        return;
    }
    
    int age = 11111;
    printf("[thread1] age=%d\n", age);
    if (!setjmp(context_thread_1)) {
        printf("[thread1] switch to thread0\n");
        longjmp(context_thread_0, 1);
    }
    
    printf("[thread1] terminated, age=%d\n", age);
    exit(0);
}

int main(int argc, char** argv) 
{
    thread0_functions();
    thread1_functions();
    
    // kickstart
    longjmp(context_thread_0, 1);
    
    return 0;
}

这两个函数虽然longjmp成功,但是stack互相混淆,导致段错误:

Breakpoint 1, thread0_functions () at jmp_2flow.cpp:23
23	    printf("[thread0] age=%d, name=%s\n", age, name);
(gdb) i locals
ret = 1
age = 10000
name = 0x400908 "winlin"

第一次进入thread0时,堆栈是ok的。然后会跳到thread1,它会进行破坏;最后还是跳到thread0:

Breakpoint 2, thread0_functions () at jmp_2flow.cpp:31
31	    printf("[thread0] terminated, age=%d, name=%s\n", age, name);
(gdb) i locals
ret = 1
age = 10000
name = 0x2b6700000001 <error: Cannot access memory at address 0x2b6700000001>

这个时候从thread1跳回来时,thread0的name完全变了。

因此,longjmp在stack没有在堆开辟时,不能跳转到已经破坏的栈。譬如:

main(setjmp) => func1 => func2 (longjmp to main)

func2若longjmp到main,是没有问题的,这时候func2的栈不可用,但是main的没有破坏。

假设下面的跳转路径:

main => func1 => func2 (setjmp)

                           => func3 (longjmp to func2)

func2返回了,然后func3再longjmp到func2时,栈的指针虽然是和func2在setjmp时一样,但是内容已经变了。这个时候就几乎会段错误。

也就是说,stack若不在堆上分配,每个线程有自己的stack时,setjmp的那个函数不能再次longjmp回来,这个时候肯定stack被破坏了。

最后的结论就是,st必须得自己分配stack,每个thread一个stack。

嵌入式系统/ARM技术中的setjmp构建简单协作式多任务系统 摘要:讨论一个利用标准C语言setjmp库函烽实现查询式协作多任务系统,给出完整的内核样例程序并对源代码进行说明。该系统具有简单易用的特点,只需要编写存取堆栈指针的宏就可方便地移植到新的平台上。文章详述了系统的优缺点,讨论一些性能扩展的方法。该内核适用于中小规模的嵌入式软件。 关键词:协作式多任务 C语言 setjmp引言本文介绍的是利用标准C语言setjmp库函数实现的具备此特点的协作式多任务系统。从本质上讲,实时多任务操作系统应该具备按照优先级抢占调度的内核。然而,在实际应用中,抢中式的多任务某种程序上带来了用户程序设计时数据保护的困难,并且,具备抢占功能的多任务内核设计时困难也比 立即下载

相关推荐

告别倍福开发板:手把手教你用SSC工具为STM32生成EtherCAT从站代码

本文详细介绍了如何使用SSC工具为STM32生成EtherCAT从站代码,帮助开发者在不依赖倍福开发板的情况下实现高效的工业以太网通信。通过SSC工具的配置、XML文件设计、代码移植与硬件适配等步骤,开发者可以轻松构建自定义的EtherCAT通信系统,提升工业自动化项目的灵活性效率。

weixin_33925615的博客 382

ST源码分析-协程通信

在讲解之前,推荐阅读 State-thread 的官方文章,每个函数的使用在文档都有讲解。《State-thread函数使用文档》 在 Linux 系统使用 多线程的时候,线程间通信,可以使用条件变量以及互斥锁。例如 线程 A 是生产者,不断写入任务到队列,线程 B 是消费者,不断从队列读取任务,没有任务的时候,线程B会阻塞,等待 线程A通知。这个通知就需要用到条件变量以及互斥锁。 pthread_mutex_lock()以及pthread_cond_wait() ST 的协程 跟线程...

u012117034的博客 1085

[AHK] 自动化获取通达信股票代码:从消息钩子到数据提取

本文详细介绍了如何使用AHK(AutoHotkey)通过Windows消息钩子技术自动化获取通达信股票代码,实现高效数据提取交易自动化。文章涵盖消息机制原理、AHK脚本开发、性能优化及安全注意事项,为股票交易者提供了一套完整的解决方案。

weixin_42614269的博客 584

ST源码分析-前言

STstate-thread 的缩写。state-thread 是一个 C 语言实现的协程库,这个库是 8年前的,《state-thread 官网文档》。 ST 协程优势有以下几点: 1,从性能上来说,ST传统的EDSM实现几乎一样快。也就是用 ST 跟用 单线程 epoll 一样高效。 2,在内存方面,ST几乎传统的EDSM一样高效。也就是用 ST 跟用 epoll 一样高效。 3,因为单线程非阻塞epoll的架构 会把请求跟回调分离,这种请求跟回调分离 的架构 ,...

u012117034的博客 785

ST源码分析-协程局部变量

ST源码分析-st_thread_create》跟 《ST源码分析-内存保护》两篇文章,讲解了,协程函数的栈内存就是下图的区域,如下: 下面就来验证一下协程函数的局部变量是不是真的存储到这块内存区域。还是用lookupdns做演示。 首先,在st_thread_create()函数加上以上代码。 stack->sp = sp - _ST_STACK_PAD_SIZE; //下面是调试代码。 printf("stack->stk_bottom is %p \r\n",stack...

u012117034的博客 878

【数据攻略】字节面试真题(含答案)+100道面试题库

数据攻略

qq_40002713的博客 4068

state-threads的协程切换

state-threads是一个C语言实现的轻量级协程库,基于setjmplongjmp来对不同协程进行切换。本文将先分析其保存上下文的setjmplongjmp的汇编代码,简单分析其上下文切换的过程。

MeRcy_PM的博客 1789

【SECS】初识SECS协议

项目标题的第一位第二位告诉下面有多少字节表示项目的长度。而项目的长度是指项目标题后面的字节数,这里第一个字节就是项目标题。1个字节可以表示的最大长度是0-255,所以只要项目标题后面的字节数小于等于254,都是0x01。

weixin_48618536的博客 1607

010_C标准库函数之<setjmp.h>

setjmp longjmp

qq_47325567的博客 770

react-native-vector-icons 的 FontAwesome Pro Light 字体包:安装、自定义字体与 Expo 配置指南

`@react-native-vector-icons/fontawesome-pro-light` 是 react-native-vector-icons 仓库([仓库根 README](https://link.gitcode.com/i/29bfda422646bbf1690c962490121df1))中以独立 npm 包形式发布的 Font Awesome Pro Light 字体集成包

gitblog_00762的博客 320

21、空间数据处理中的克里金方法及应用

本文介绍了空间数据处理中的克里金方法,特别是带外部漂移的克里金法(KED)及其在遥感数据融合支持变化问题中的应用。讨论了克里金方法的基本原理、优缺点以及在不同空间分辨率遥感图像融合中的具体操作流程。同时,还涉及了地统计数据融合支持变化问题的解决方案,包括块克里金法、面到点克里金法多边形克里金法等。

gold的博客 79

C 语言中 setjmp longjmp

在 C 语言中,我们不能使用goto语句来跳转到另一个函数中的某个label处;但提供了两个函数——setjmplongjmp来完成这种类型的分支跳转。后面我们会看到这两个函数在处理异常上面的非常有用。 setjmp longjmp 使用方法 我们都知道要想在一个函数内进行跳转,可以使用goto语句(不知怎么该语句在中国学生眼中就是臭名昭著,几乎所有国内教材都一刀切地教大...

zhangmiaoping23的专栏 255

基于改进YOLOv5的小目标检测 | 添加CBAM注意机制 + 更换Neck网络之BiFPN + 增加高分辨率检测头

本文针对图像中小目标难以检测的问题,提出了一种基于YOLOv5的改进模型。在主干网络中,加入CBAM注意力模块增强网络特征提取能力;在颈部网络部分,使用BiFPN结构替换PANet结构,强化底层特征利用;在检测头部分,增加高分辨率检测头,改善对于微小目标的检测能力。本文算法在无人机数据集VisDrone2019数据集上进行了多次对比实验,结果表明本文算法可以有效地检测小目标。🌈

小哥谈 2890

高性能协程库State Thread Library源代码剖析之二寻找入口

目前人类认知的所有现象,规律,定理都是有源可寻,有根可究的,唯一不知道根源的可能是谁创造了万有引力。对于软件工程师来说,一个软件系统生命开始的地方就在软件系统的入口,应用程序就是main函数,kernel就是kernel_entry。但是这里我们研究的是一个库,库自然是给应用程序使用的,应用程序在哪里呢,在example下。 lookupdns.c 查找域名的ip,可输入多个域名 st

deusomax的专栏 895

ST源码分析-lookupdns

lookupdns只有 300 行代码,但是却演示了 如何使用 协程 并发请求dns。 lookupdns的流程图如下: 编译之后,运行以下命令: ./obj/lookupdns www.xianwaizhiyin.net www.baidu.com 上面两个命令 用协程 查询了两个域名,wireshark抓包如下: 从数据包上看,两个udp查询,是并发的,第二个请求并没有被第一个请求阻塞。st_recvfrom()函数只会阻塞当前的协程,并不会阻塞第二个协程。而且 这两个...

u012117034的博客 951

通过情感分类实例 详细介绍如果借助Huggingface平台 完成Transformers模型运行(包括配置环境-tokenizer-Model-输出头-预测)

attention_maskpadding是配套使用的,当人为去修改padding的时候,也要把attention_mask里面的参数进行修改。下载Huggingface包,Huggingface可以说是一个社区,集成了很多nlp的模型、数据集、预训练权重文件等等,并且免费,只需要pip install下载即可。那么用什么做分类就选择什么输出头,就import什么东西。代码中使用了自动分词器选择,它是由checkpoint指定的预训练模型关联的,不用我们自己选择tokenizer,它自动帮我们选择好了。

LZL2020LZL的博客 1114

setjmp()longjmp() Linux Exception Handling/Error Handling、no-local goto

目录 1. 应用场景 2. Use Case Code Analysis 3. setjmplongjmp有关的glibc and eglibc 2.5, 2.7, 2.13 - Buffer Overflow Vulnerability   1. 应用场景 非局部跳转通常被用于实现将程序控制流转移到错误处理模块中;或者是通过这种非正常的函数返回机制,返回到之前调用的函数中...

weixin_33733810的博客 202

jira的详细使用教程

​jira的详细使用教程,1,Jira- 是一款缺陷管理软件工具,用其进行缺陷的管理跟踪。(是B/S架构的软件) 2,主要涉及的角色: 3,具体工作流 添加工作流 1.点击项目设置

计算机毕业论文源码,学生个人网页制作html源码。贴近用户做网络推广和互联网优化。 3263

st(state-threads) coroutine调度

st(state-threads) https://github.com/winlinvip/state-threads 以及基于st的RTMP/HLS服务器:https://github.com/winlinvip/simple-rtmp-server st是实现了coroutine的一套机制,即用户态线程,或者叫做协程。将epoll(async,nonblocking socket

OSSRS 5439

协程库st(state threads library)原理解析

协程库state threads library(以下简称st)是一个基于setjmp/longjmp实现的C语言版用户线程库或协程库(user level thread)。 这里有一个基本的协程例子http://www.csl.mtu.edu/cs4411.ck/www/NOTES/non-local-goto/coroutine.html, 可以了解setjmplongjmp的基本用法。...

weixin_30635053的博客 583

cad完整版教程

cad教程。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

Cortex-M单片机中 setjmplongjmp原理探究及实现

setjmplongjmp原理探究及实现一、原理1、实验2、猜想二、实现三、调用四、总结 一、原理 C语言中包含头文件 <setjump.h> 即可实现跨函数跳转,通常用于异常处理,在运行代码出现异常时可以自动跳转到调用setjump函数的位置。 1、实验 #include "setjmp.h" jmp_buf g_jmp_buf; int div__(int a,int b) { if(b==0) { longjmp(g_jmp_buf,1); }

Ranchaun的博客 1964

0.96寸OLED屏原理图

0.96OLED原理图可以用于OLED的学习开发。 

构建C协程之setjmp/long_jmp篇

原理简介 在标准C中的头文件中定义了一组函数 setjmp / long_jmp 用来实现“非本地跳转”的功能,利用 setjmp 可以保存当前执行线索状态,稍后通过 long_jmp 函数可以实现状态的恢复,并且可以跨多层函数调用栈进行跳转。具体接口定义如下: int setjmp(jmp_buf env) 该函数主要用来保存当前执行状态,作为后续跳转的目标。调用时,当前

kobejayandy的专栏 1万+

深入理解qemu协程

协程使用场景比较少,不易理解,本文试图从普通的协程demo,分解qemu协程的用法。

Take Easy,Work Hard! 3990

《新媒体运营》课件项目一-新媒体运营入门.pptx

《新媒体运营》 项目一 新媒体运营入门 21世纪高职高专规划教材 电子商务系列 《新媒体运营》课件项目一-新媒体运营入门全文共23页,当前为第1页。 项目内容 〖知识目标〗 1.掌握新媒体,公域流量,私域流量的概念 2.熟悉新媒体行业的发展趋势 3.了解新媒体运营部门的人员配置及分工 4.掌握新媒体运营的职责、具备的能力 5.熟悉新媒体平台的特点 6.掌握新媒体运营工作流程 〖能力目标〗 1.能够运用所学知识分析新媒体运营人才需求 2.能够为从事新媒体运营工作进行自我分析 3.能够分析公域流量私域流量的营销特点 4.能够运用所学分析新媒体工作的环节 《新媒体运营》课件项目一-新媒体运营入门全文共23页,当前为第2页。 小王是个高职大三的学生,开始为毕业后的工作上心了,经常到招聘网站看看,听说新媒体行业的发展前景都很不错,于是小王找到在企业负责新媒体的学长,想请教一下: 1.什么是新媒体? 2.新媒体行业的发展趋势如何? 〖课前提问〗 如果你是小王,你如何回答以上问题? 〖故事导入〗 任务一 了解新媒体 《新媒体运营》课件项目一-新媒体运营入门全文共23页,当前为第3页。 一、新媒体的

浅说c/c++ coroutine

浅说c/c++ coroutine 从上面我们可以得到关于协程的几个关键信息, 1.打破传统(regular)函数调用的限制。 2.stackful协程实现方式,基于独立栈,上下文切换。 3.stackless协程实现方式,状态机,闭包。 4.控制权切换的对称性。层级关系的切换为非对称。如caller跟callee 先来看传统(regular)函数调用,应该是专指我们的c/c++编...

weixin_36162235的博客 148

美国国家以及州地区地图边界GEOJSON数据

美国国家以及州地区地图边界GEOJSON数据

ST源码分析-运行协程

本文 基于 命令./obj/lookupdns www.xianwaizhiyin.net www.baidu.com做讲解,查询两个个域名。 lookupdns里面的do_resolve()函数前面的执行步奏如下: 本文 主要 讲解do_resolve()函数的逻辑,以及它里面使用的st_recvfrom()为何不会阻塞另一个协程的do_resolve()的运行。 do_resolve()里面一些 域名相关的 API 函数的调用,本文不过多讲解。do_resolve()...

u012117034的博客 773

【C++学习(35)】在Linux中基于ucontext实现C++实现协程(Coroutine),基于C++20的co_await 协程的关键字实现协程

同时维护两个map,io_waiting_fibers_表示监听的文件描述符所对应的一对读写的协程,expire_fibers_的value为一个有序集合,表示在某个时间点会超时的协程集合。先从最基本的创建一个协程开始,首先注意协程线程的区别,协程代表一段可以分开执行的逻辑,但是其它协程还是保持串行执行,因此协程创建并不会马上执行,而是由协程调度器统一执行。协程调度器主要维护两个协程队列,分别是运行队列就绪队列,运行队列中的协程会被切换到,而就绪队列中的协程会在下一次的循环中被切换到。

AbiUni(YY熊) 1578
上一篇: GO性能测试:未进行任何优化
下一篇: st(state-threads) coroutine和stack分析
winlinvip
博客等级 码龄16年 1073粉丝 267原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

winlinvip

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值