st(state-threads) coroutine和stack分析

分享《面试高频问题》线程-协程-进程-st-thread那些事.mp4 内容包括:C/C++,Linux,Nginx,golang,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,ffmpeg,流媒体, 音视频,CDN,P2P,K8S,Docker,Golang,TCP/IP,协程,嵌入式,ARM,DPDK等等。。。 立即下载

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

我将st进行了简化,去掉了其他系统,只考虑linux系统,以及i386/x86_64/arm/mips四种cpu系列,参考:https://github.com/winlinvip/simple-rtmp-server/tree/master/trunk/research/st
本文介绍了coroutine的创建和stack的管理。

STACK分配

Stack数据结构定义为:

 

typedef struct _st_stack {
    _st_clist_t links;
    char *vaddr;                /* Base of stack's allocated memory */
    int  vaddr_size;            /* Size of stack's allocated memory */
    int  stk_size;              /* Size of usable portion of the stack */
    char *stk_bottom;           /* Lowest address of stack's usable portion */
    char *stk_top;              /* Highest address of stack's usable portion */
    void *sp;                   /* Stack pointer from C's point of view */
} _st_stack_t;


实际上vaddr是栈的内存开始地址,其他几个地址下面分析。

 

栈的分配是在_st_stack_new函数,在st_thread_create函数调用,先计算stack的尺寸,然后分配栈。

 

    | REDZONE |          stack         |  extra  | REDZONE |
    +---------+------------------------+---------+---------+
    |    4k   |                        |   4k/0  |    4k   |
    +---------+------------------------+---------+---------+
    vaddr     bottom                   top


上图是栈分配后的结果,两边是REDZONE使用mprotect保护不被访问(在DEBUG开启后),extra是一个额外的内存块,st_randomize_stacks开启后会调整bottom和top,就是随机的向右边移动一点。

 

总之,最后使用的,对外提供的接口就是bottom和top,st_thread_create函数会初始化sp。stack对外提供的服务就是[bottom, top]这个内存区域。

THREAD初始化栈

开辟Stack后,st会对stack初始化和分配,这个stack并非直接就是thread的栈,而是做了以下分配:

 

        +--------------------------------------------------------------+
        |                         stack                                |
        +--------------------------------------------------------------+
        bottom                                                         top


分配如下:

 

 

        +-----------------+-----------------+-------------+------------+
        | stack of thread |pad+align(128B+) |thread(336B) | keys(128B) |
        +-----------------+-----------------+-------------+------------+
        bottom            sp                trd           ptds         top
               (context[0].__jmpbuf.sp)             (private_data)


也就是说:

 

ptds:这个是thread的private_data,是12个指针(ST_KEYS_MAX指定),参考st_key_create()。

trd:thread结构本身也是在这个stack中分配的。

pad+align:在trd之后是对齐和pad(_ST_STACK_PAD_SIZE指定)。

sp:这个就是thread真正的stack了。

coroutine必须要自己分配stack,因为setjmp保存的只是sp的值,而没有全部copy栈,所以若使用系统的stack,各个thread之间longjmp时会导致栈混淆。参考:http://blog.csdn.net/win_lin/article/details/40948277

Thread启动和切换

st的thread如何进入到指定的入口呢?

其实在第一次setjmp时,是初始化thread,这时候返回值是0,初始化完后就返回到调用函数继续执行了。

调用函数会在其他地方调用longjmp到这个thread,这时候是从setjmp地方开始执行,返回值是非0,这时进入thread的主函数:_st_thread_main。

参考我改过的代码:

 

_st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stk_size)
{
// by winlin, expend macro MD_INIT_CONTEXT
#if defined(__mips__)
    MD_SETJMP((trd)->context);
    trd->context[0].__jmpbuf[0].__pc = (__ptr_t) _st_thread_main;
    trd->context[0].__jmpbuf[0].__sp = stack->sp;
#else
    int ret_setjmp = 0;
    if ((ret_setjmp = MD_SETJMP((trd)->context)) != 0) {
        _st_thread_main();
    }
    MD_GET_SP(trd) = (long) (stack->sp);
#endif
}


gdb调试,第一次setjmp时,返回值是0,调用堆栈是创建线程的堆栈,62行的代码是st_thread_t trd = st_thread_create(thread_func, NULL, 1, 0);:

 

 

(gdb) f
#0  st_thread_create (start=0x4073fb <thread_func>, arg=0x0, joinable=1, stk_size=65536) at sched.c:600
600	    if ((ret_setjmp = MD_SETJMP((trd)->context)) != 0) {
(gdb) bt
#0  st_thread_create (start=0x4073fb <thread_func>, arg=0x0, joinable=1, stk_size=65536) at sched.c:600
#1  0x00000000004074b5 in thread_test () at srs.c:62
#2  0x00000000004081c3 in main (argc=1, argv=0x7fffffffe4b8) at srs.c:344
(gdb) p ret_setjmp 
$36 = 0


从其他线程切换过来时,即longjmp过来时,返回值非0,调用堆栈是longjmp的堆栈,68行的代码是st_thread_join(trd, NULL);:

 

 

(gdb) f
#0  st_thread_create (start=0x4073fb <thread_func>, arg=0x6390b0, joinable=0, stk_size=6599392) at sched.c:601
601	        _st_thread_main();
(gdb) bt
#0  st_thread_create (start=0x4073fb <thread_func>, arg=0x6390b0, joinable=0, stk_size=6599392) at sched.c:601
#1  0x00000000004074f4 in thread_test () at srs.c:68
#2  0x00000000004081c3 in main (argc=1, argv=0x7fffffffe4b8) at srs.c:344
(gdb) p ret_setjmp 
$37 = 1


注意,虽然显示都是thread_test这个函数过来,实际上函数行数已经不一样了,gdb显示的stk_size也是破坏了的,因为这个时候的栈是用的st自己开辟的栈了。

 

进入到_st_thread_main中后,会调用用户指定的线程函数(这个函数里面会调用st函数setjmp,下次longjmp是到这个位置了);从线程函数返回后,会调用st_thread_exit清理线程,然后切换到其他函数,直到完成最后一个函数就返回了。

 

void _st_thread_main(void)
{
    _st_thread_t *trd = _ST_CURRENT_THREAD();
    
    /* Run thread main */
    trd->retval = (*trd->start)(trd->arg);
    
    /* All done, time to go away */
    st_thread_exit(trd->retval);
}

 

这个就是st的thread启动和调度的过程。

 

第一次创建线程和setjmp后,会设置sp,即设置stack。也就是说,这个函数的所有stack信息在longjmp之后都是未知的了,这就是所有st的thread结束后,必须longjmp到其他的线程,或者退出,不能直接return的原因(因为没法return了,顶级stack就是_st_thread_main)。

Thread退出

在st的thread中退出后,会切换到其他thread(st创建的线程stack是重新建立的,无法返回后继续执行)。

st创建的thread,结束后会调用st_thread_exit,参考_st_thread_main的定义,这个就是thread执行的主要流程。

st在初始化st_init时,会把当前的线程当作_ST_FL_PRIMORDIAL,也就是初始化线程,这个线程若调用exit,等待其他thread完成后,会直接exit。实际上是没有线程时会切换到idle线程:

 

void _st_vp_schedule(void)
{
    _st_thread_t *trd;
    
    if (_ST_RUNQ.next != &_ST_RUNQ) {
        /* Pull thread off of the run queue */
        trd = _ST_THREAD_PTR(_ST_RUNQ.next);
        _ST_DEL_RUNQ(trd);
    } else {
        /* If there are no threads to run, switch to the idle thread */
        trd = _st_this_vp.idle_thread;
    }


idle线程是在st_init时创建,也就是说st_init会创建一个idle线程(使用st_thread_create),以及直接创建一个_ST_FL_PRIMORDIAL线程(直接calloc)。idle线程的代码:

 

 

void *_st_idle_thread_start(void *arg)
{
    _st_thread_t *me = _ST_CURRENT_THREAD();
    
    while (_st_active_count > 0) {
        /* Idle vp till I/O is ready or the smallest timeout expired */
        _ST_VP_IDLE();
        
        /* Check sleep queue for expired threads */
        _st_vp_check_clock();
        
        me->state = _ST_ST_RUNNABLE;
        _ST_SWITCH_CONTEXT(me);
    }
    
    /* No more threads */
    exit(0);
    
    /* NOTREACHED */
    return NULL;
}

 

所有线程完成时就exit。

 

Thread初始线程

st的初始线程,或者叫做物理线程,primordial线程,是调用st_init的那个线程。一般而言,调用st的程序都是单线程,所以这个初始线程也就是那个系统的唯一的一个线程。

所有st的线程都是调用st_create_thread创建的,使用st自己开辟的stack;除了一种初始线程,没有重新设置stack,这个就是初始线程(物理线程)。

参考st_init的代码:

 

    /*
    * Initialize primordial thread
    */
    trd = (_st_thread_t *) calloc(1, sizeof(_st_thread_t) +
    (ST_KEYS_MAX * sizeof(void *)));
    if (!trd) {
        return -1;
    }
    trd->private_data = (void **) (trd + 1);
    trd->state = _ST_ST_RUNNING;
    trd->flags = _ST_FL_PRIMORDIAL;
    _ST_SET_CURRENT_THREAD(trd);
    _st_active_count++;


在分配trd对象时,分配了_st_thread_t和keys两个对象,可以参考前面对于stack的使用。keys用来做private_data,所以后面初始化private_data时是指向下一个thread。

 

创建后设置这个线程为_ST_FL_PRIMORDIAL,这个就是用来指明stack是否是st自己分配的:

 

void st_thread_exit(void *retval)
{
    if (!(trd->flags & _ST_FL_PRIMORDIAL)) {
        _st_stack_free(trd->stack);
    }
}


如果是初始线程(物理线程),那么stack是不释放的,这个stack是NULL。

 

在调度时,不管stack是否是自己创建的,对于调度都没有影响。stack如果是st自己创建的,只是在setjmp之后的context中修改sp的地址,这个时候longjmp会使用新的stack而已,对于longjmp的jmp_buf到底sp是自己创建的还是系统的,其实没有区别。

所以初始线程(物理线程)也是作为一个st的thread被调度,没有任何区别。

Thread生命周期

再整理下st整个线程的执行流程。

第一个阶段,st_init创建idle线程和创建priordial线程(初始线程,物理线程,_ST_FL_PRIMORDIAL),这时候_st_active_count是1,也就是初始线程(调用st_init,也是物理线程)在运行,idle线程不算一个active的线程,它主要是做切换和退出。

第二个阶段,可选的阶段,用户创建线程。调用st_thread_create时,会把_st_active_count递增,并且加入线程队列。譬如创建了一个线程;这时候st调度有两个线程,一个是初始线程,一个是刚刚创建的线程。

第三个阶段,初始线程切换,将控制权交给st。也就是初始线程,做完st_init和创建其他线程后,这个时候还没有任何的线程切换。初始线程(物理线程)需要将控制权切换给st,可以调用st_sleep循环和休眠,或者调用st_thread_exit(NULL)等待其他线程结束。假设这个阶段物理线程不进行切换,st将无法获取控制权,程序会直接返回。

这么设计其实很完善,如果物理线程不exit,那么st的idle线程也不退出(认为有个初始线程还在跑)。如果初始线程直接退出,那么idle线程不会拿到控制权。如果初始线程调用st_thread_exit(NULL),认为是物理线程也退出,那么idle会等所有线程完了再exit,相当于控制权交给st了。

或者说,可以在初始线程(物理线程)里面做各种的业务逻辑,譬如srs用初始线程更新各种数据,给api使用。或者可以直接创建线程后st_thread_exit,就等所有线程退出。

Setjmp分析

实现协程跳转的关键,就是setjmp和longjmp,如下图所示:

更多分析参考Analysis。

Stack

ST会重新分配堆栈,堆栈的布局和结构,如下图所示:

更多分析参考Analysis

ASM

ST的汇编代码,详细的注释参考ASM Code

Scheduler

调度器的详细实现,参考Scheduler

IO

IO事件框架和处理的实现,参考IO

 

2023电赛B题实战解析:基于立创天空星开发板的同轴线缆长度与负载测量系统 本文详细解析了2023年全国大学生电子设计竞赛B题的实战方案,重点介绍了基于立创EDA天空星开发板(STM32F103)的同轴线缆长度与负载测量系统。文章深入剖析了两种核心测量方案,并推荐了更稳定易实现的NE555测电容法,提供了从硬件选型、电路设计到软件编程、参数标定的完整“施工图”,实测精度可达厘米级皮法级,为电赛复现与高精度测量学习提供了清晰指导。 阅读详情

相关推荐

Palantir的Ontology:从静态知识图谱到动态业务引擎的跃迁

本文探讨了Palantir的Ontology技术如何将静态知识图谱升级为动态业务引擎。通过引入函数行动作为核心元素,Ontology实现了业务逻辑的自动化响应与执行,显著提升物流、能源等领域的运营效率。文章详细解析了其混合存储引擎、状态监听网络等关键技术设计,并对比展示了传统架构与Ontology架构的业务价值差异。

weixin_42570296的博客 177

ST源码分析-st_thread_create

本文 主要分析st_thread_create()函数的内部实现。 st_thread_create流程图如下: 现在开始一行一行代码分析st_thread_create _st_thread_t *thread; _st_stack_t *stack; void **ptds; char *sp; 上面代码声明了 4 个变量 ,变量ptds的全称应该是 pthread_data_register(线程的寄存器缓存),sp 的全称 是 stack point。 ...

u012117034的博客 1508

Windows防火墙服务异常修复指南:0x80070422与0x6D9报错实战处理

本文针对Windows防火墙服务异常导致的0x80070422与0x6D9报错,提供了从基础到进阶的实战修复指南。核心是通过services.msc服务管理器检查并修复Windows Firewall服务,包括设置启动类型、检查依赖服务等关键步骤。同时介绍了当基础方法失效时,如何使用命令行工具、系统文件检查器(SFC/DISM)以及排查组策略与第三方软件冲突等深度解决方案,帮助用户彻底解决防火墙服务无法启动或访问的问题。

m9n0o的博客 506

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

怎么理解st_thread库(4)

st_thread的的调用过程是在过于复杂。为了方便理解,我想写了个比较简单的例子 代码如下 // jumptest.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include #include jmp_buf buf; jmp_buf buf_banana; jmp_buf buf_apple; void banana() { p

ddr77的专栏 2568

路径规划 | 详解混合A*算法Hybrid A*(附ROS C++/Python/Matlab仿真)

Hybrid A*算法通过结合连续状态空间搜索、运动约束考虑轨迹优化等技术,弥补了普通A*算法在高维连续空间的不足,提高了路径规划的效率鲁棒性,广泛应用于车规级路径规划。本文详细图解Hybrid A*算法的原理——启发式、维诺耗散项、节点扩展法则等,并给出ROS C++、python、matlab三种语言的仿真实现加深理解

FRIGIDWINTER的博客 1万+

【网络通信 -- 直播】SRS 源码分析 -- 协程基础知识点总结()

【网络通信 -- 直播】SRS -- 协程基础知识点总结() 【1】网络应用 (Internet Application, IA) 架构的演变 阶段一,多进程(MP),创建进程服务新用户,开销过高,调度单位是进程; 阶段二,多线程(MT),创建新线程服务新用户,线程间上下文切换,锁竞争等,增加了额外开销,调度单位是线程; 阶段三,事件驱动状态机(EDSM),基于 IO 复用机制,实现大量并发请求的处理; 阶段四,协程,调度单位减小到函数,上下文切换不需要内核参与,不存在系统调用; ST 协程 V

CopperSun 技术博客 1224

安装Openclaw[本地自动化AI助手]至windows【WSL2模式】系统 Openclaw for Windows

摘要:OpenClaw最佳兼容OpenAI系列模型,推荐使用ChatGPT API(需Auth鉴权),避免K*mi等模型导致的信用卡风险。Windows系统需满足WSL2支持条件(Win11或Win10 1903+版本)。安装需导入自签名根证书或跳过浏览器安全警告,通过离线包部署WSL发行版。使用时通过clawtoken命令获取网关认证令牌,并在浏览器完成设备配对。提供代购ChatGPT Plus账号服务(¥140/月),详细操作指南需加微信群获取文件。(150字)

qq_27246521的博客 436

怎么理解state thread 库(2)

分析程序,一般首先是分析它的线程模型。看看一共有几个线程,各个线程都干了什么。 但分析st-thread库,就比较迷茫了。因为它本身有个线程模块,其实是一个堆里的数据块,并没有真正的线程。我曾经怀疑,在st_ini里,有两次创建线程的机会,一个是一个idle_therad,一个是自带第一个线程。但仔细分析代码。还是没有真正的线程创建。莫非是外部创建线程,通过某种巧妙的方法,传给st库?看pub

ddr77的专栏 2603

()st(state-threads) coroutinestack分析

目录(?)[-] STACK分配 THREAD初始化栈 Thread启动切换 Thread退出 Thread初始线程 Thread生命周期 st(state-threads)https://github.com/winlinvip/state-threads 以及基于st的RTMP/HLS服务器:https://github.com/winlin...

dianchixian5277的博客 158

基于Roop-Unleashed-Gradio的AI视频换脸规避NSFW

人脸交换技术的不断发展,给我们带来了无限的可能性创意。这款Gradio应用以其强大的功能用户友好的界面,使得人脸交换变得简单而高效。从图片到视频,从简单的编辑到复杂的处理,每一个模块都精心设计,满足用户的不同需求。此外,实时摄像头人脸管理功能的加入,使得该应用更加全面实用。无论是用于娱乐、教育,还是其他专业领域,这款应用都将是您不可或缺的工具。期待未来的技术进步,带来更多惊喜可能。

Mr数据杨 2304

state-threads单元测试与覆盖率分析:确保协程库稳定性的完整方案

在现代高性能网络服务器开发中,**state-threads**作为轻量级C/C++协程库,其稳定性可靠性至关重要。本文将深入探讨如何通过全面的单元测试覆盖率分析来确保这个**高性能协程库**的质量,为开发者提供一套完整的测试方案。😊 ## 为什么单元测试对state-threads如此重要? **state-threads**是一个轻量级的线程库,专门为C/C++协程设计,类似于Go语

gitblog_00479的博客 439

问答式AI vs 智能体(Agent):它们有什么不同?

你肯定用过 ChatGPT 或文心一言——问它一个问题,它给你一个答案。AI Agent(AI智能体)。很多人搞不清楚:它们不都是"AI"吗?有什么区别?这篇文章用一个最简单的类比,彻底讲清楚这两者的本质差异。框架特点适合人群AutoGPT完全自主执行任务,链式思考开发者MetaGPT模拟团队协作,多角色分工开发者LangChain最流行的Agent开发框架开发者Dify可视化无代码搭建Agent非技术用户Coze(扣子)字节跳动出品,国内最易用普通用户WorkBuddy。

Johnny2004的专栏 405

st(state-threads) coroutinesetjmp/longjmp的关系

st(state-threads) https://github.com/winlinvip/state-threads 以及基于st的RTMP/HLS服务器:

OSSRS 5524

Emscripten 中 LLVM compiler-rt 的集成与维护:基于 llvm-project 22.1.8 的运行时库移植指南

compiler-rt 是 LLVM 项目自带的运行时库集合,涵盖编译器内置辅助函数(builtins)、AddressSanitizer/LeakSanitizer/UBSan 等内存与未定义行为检测工具,以及代码覆盖率(profile)支撑模块。在 Emscripten 工具链中,这一整套运行时库被直接移植进仓库,为 WebAssembly 目标提供底层支撑。本文以 [system/lib/c

gitblog_00915的博客 1111

具身大小脑全栈全网独家复现|LLM链式推理+边缘VLA感知+毫秒级运动小脑、优化人形步态僵硬运动滞后、打通思考-行走-操作全链路适配居家工业多场景落地

高层决策大脑(异步10Hz,GPU算力):轻量化机器人微调LLM+RhinoVLA边缘视觉大模型,负责自然语言链式工序拆解、全局环境语义感知、目标物体筛选、跨机器人动作适配,输出粗粒度运动目标,不介入底层伺服计算;依托View Registry实现异构机器人动作对齐,适配多形态机器人切换;中层调度小脑(硬实时1kHz,CPU算力)

qq_36130719的博客 229

Salt Pillar 输出掩码修复剖析:`salt.utils.secret.serial` 如何堵住 int/float/bool/bytes 泄露

Pillar 是 Salt 中存放密码、密钥等敏感数据的核心通道,而 Salt 3008 起引入的 pillar 输出掩码(output masking)机制负责在这些数据被打印、记日志或通过 `pillar.get` 等函数返回时自动脱敏。本文以 changelog [98852.fixed.md](https://link.gitcode.com/i/7791e47d819ece0959ea7

gitblog_00123的博客 445

网页内容抓取的艺术:使用R语言解析动态网页

本文探讨了使用R语言处理动态网页数据的方法。针对AJAX加载的学术网站,传统的静态抓取RSelenium方法存在会话终止、浏览器错误等问题。通过分析发现内容通过admin-ajax.php加载,提出使用httr2模拟POST请求获取数据,配合rvest解析HTML内容。该方法避免了浏览器自动化的弊端,实现了高效稳定的动态内容提取,为处理类似AJAX网站提供了通用解决方案。

m0_74337424的博客 38

state-threads性能优化指南:内存管理、栈分配与事件循环调优技巧

state-threads是一个轻量级的C/C++协程库,专为高性能网络服务器设计,提供了类似Go语言goroutine的并发编程体验。对于需要处理大量并发连接的网络应用开发者来说,掌握state-threads的性能优化技巧至关重要。本指南将深入探讨内存管理、栈分配策略事件循环调优等关键优化技术,帮助您构建更高效、更稳定的网络应用。🚀 ## state-threads架构概述与性能挑战

gitblog_00857的博客 393

state-threads源码探秘:从汇编到C实现,深入理解协程切换的底层原理

state-threads是一个轻量级的C/C++协程库,为高性能网络服务器提供了类似Goroutine的用户态线程实现。本文将带你深入探索state-threads的源码实现,从汇编级的上下文切换到底层的协程调度机制,为你揭开协程切换的神秘面纱。🚀 ## 什么是state-threads协程库? state-threads是一个为高性能网络服务器设计的轻量级线程库,它实现了用户态的协程(c

gitblog_00552的博客 404

xml spy 2010绿色 精简 破解版

xml spy 2010 绿色 精简 破解版 解压缩后,第一次启动需要注册(使用内部注册机)xml spy主文件已经破解 ,不再进行正版验证

2025 C++并发编程实战:从线程安全到无锁数据结构与协程应用

并发编程是现代软件开发中提升性能的核心技术,其核心原理在于通过多线程或多进程同时执行任务,以充分利用多核CPU的计算能力。从技术价值看,它能显著提高程序的吞吐量响应速度,尤其适用于高并发服务器、数据处理实时系统等场景。在C++中,实现并发需要深入理解线程生命周期管理、同步机制内存模型等基础概念。本文聚焦于C++20/23的最新特性,通过实战案例解析如何构建健壮的线程安全队列,并探讨无锁数据结构与协程的应用。针对常见的性能瓶颈,如锁竞争缓存伪共享,提供了具体的优化策略调试方法,帮助开发者从入门到精通

weixin_34411563的博客 341

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

python3 核心笔记

本文主要是针对python3的一些核心要点做一些记录,感兴趣的可以关注订阅。会持续更新,这个玩意学习一下,可以为数据分析AI打下一定的基础。

yanghuashuiyue的专栏 585

SRS流媒体服务器 ---- st-thread框架

我们用一个简单的demo研究一下st框架。012345670x010x020x040x080x10这里线程的调度是基于 setjmplongjmp,goto类似,不过goto是函数内部跳转,这个跳转范围更大。所以讲解一下用法,返回值:若直接调用则返回0,若从longjmp调用返回则返回非0值的longjmp中的val值调用此函数则返回到语句setjmp所在的地方,其中env 就是setjmp中的 env,而val 则是使setjmp的返回值变为val。

C/C++Linux、音视频、DPDK 685

DOSCommand for Delphi11.zip

DOSCommand 是一个 Delphi 组件,它允许 Delphi 应用程序执行 DOS 命令或 shell 命令。这个组件通常用于执行那些在 Windows 命令行或 DOS 提示符下可以运行的命令程序。以下是 DOSCommand 组件的一些基本功能:执行命令:运行简单的 DOS 或 shell 命令,如 dir、copy、del 等。异步执行:可以设置命令以异步方式运行,这样 Delphi 应用程序就可以继续执行其他任务,而 DOS 命令在后台运行。命令输出:捕获 DOS 命令的标准输出,这可以用于读取由命令生成的信息。错误处理:可以捕获处理命令执行过程中的错误输出。命令参数:支持向 DOS 命令传递参数,以定制命令的行为。环境变量:可以设置修改环境变量,这些变量可以被 DOS 命令使用。工作目录:可以指定 DOS 命令的工作目录,这可能对某些命令的执行很重要。等待命令完成:可以配置 Delphi 应用程序等待 DOS 命令执行完成,或继续执行其他任务。DOSCommand 组件对于需要在 Delphi 应用程序中集成命令行功能的开发

ST源码分析-st_thread_join

本文主要讲解,st_thread_join()函数的使用 ,参考文档《st_thread_join》。下面用一个小的示例代码,演示一下st_thread_join()函数 的使用。 #include <stdio.h> #include <memory.h> #include "st.h" #include "../common.h" #include <stdlib.h> ​ int do_something(void *arg) { st_utim...

u012117034的博客 719

BR_POWERLINK-SLAVE_ALTERA_V0.2.0.rar_C Builder_powerlink_power

实时以太网powerlink 最新BR_POWERLINK_V0.2.0源码开发包

【网络通信 -- 直播】SRS 源码分析 -- 协程基础知识点总结()

【网络通信 -- 直播】SRS -- 协程基础知识点总结() 【1】SRS 源码中协程相关文件 SRS 源码中与协程相关的文件总结如下 trunk\src\app\srs_app_st.hpp trunk\src\app\srs_app_st.cpp 以上两个文件中,基于面向对象的思想,封装了与协程处理的相关接口、抽象类、具体实现类 trunk\src\protocol\srs_service_st.hpp trunk\src\protocol\srs_service_st.cpp 以上两个文件

CopperSun 技术博客 1349
上一篇: st(state-threads) coroutine和setjmp/longjmp的关系
下一篇: st(state-threads) coroutine调度
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、付费专栏及课程。

余额充值