alloca() 和 setjmp 的作用是啥?

Posted

技术标签:

【中文标题】alloca() 和 setjmp 的作用是啥?【英文标题】:What's the function of alloca() with setjmp?alloca() 和 setjmp 的作用是什么? 【发布时间】:2018-11-02 03:13:21 【问题描述】:

这个问题来自我问的Practical usage of setjmp and longjmp in C和How to implement coroutine within for loop in c。

jmp_buf bufferA, bufferB;

void routineB(); // forward declaration

void routineA()

    int r = 0;

    printf("(A1)\n");

    if (setjmp(bufferA) == 0) 
        r++;
        alloca(2048);
        routineB();
    

    printf("(A2) r=%d\n",r);

    if (setjmp(bufferA) == 0) 
        r++;
        longjmp(bufferB, 1);
    

    printf("(A3) r=%d\n",r);

    if (setjmp(bufferA) == 0) 
        r++;
        longjmp(bufferB, 1);
    

    printf("(A4) r=%d\n",r);


void routineB()

    int r = 0;

    printf("(B1)\n");

    if (setjmp(bufferB) == 0) 
        r++;
        longjmp(bufferA, 1);
    

    printf("(B2) r=%d\n", r);

    if (setjmp(bufferB) == 0) 
        r++;
        longjmp(bufferA, 1);
    

    printf("(B3) r=%d\n", r);

    if (setjmp(bufferB) == 0) 
        r++;
        longjmp(bufferA, 1);
    

    printf("(B4) r=%d never reach\n", r);


int main()

    printf("main\n");
    routineA();
    return 0;

我正在研究 C. 的协程实现,并试图查看longjmp 之后堆栈中发生了什么。

问题一:

在使用alloca(2048) 之后,使routineB 的堆栈存活的魔法是什么? 我听说alloca 是邪恶的,但为什么它使堆栈看起来像扩展了。 我应该这样使用它吗?

输出:

main
(A1)
(B1)
(A2) r=1
(B2) r=1
(A3) r=2
(B3) r=2
(A4) r=3

问题 2:

删除alloca(2048) 后。告诉编译器禁用优化(-O2)后给出不同的结果。

-O0

main
(A1)
(B1)
(A2) r=1
(B2) r=6356584
(A3) r=2
(B3) r=6356584
(A4) r=3

-O2

main
(A1)
(B1)
(A2) r=1
(B2) r=0
(A3) r=1
(B3) r=0
(A4) r=1

如果它不是未定义的,如何使代码获得相同的行为?如果是,请忘记 Q2。

【问题讨论】:

提防undefined behavior 就像我解释的here,代码调用了未定义的行为。您不应该在函数之间进行这种跳转。 @AjayBrahmakshatriya 所以我将简单地将longjmp 视为一个特殊的return @Jiu, longjmp 并不总是回报。仅当相应的setjmp 位于嵌套调用集中较早的函数中时,它才会终止该函数。如果setjmp 在同一个函数中,它就像一个普通的gotosetjmp 也可能是嵌套的许多步骤。所以它根本不像return @AjayBrahmakshatriya 谢谢!使用longjmp & setjmp的最佳总结。 【参考方案1】:

这是一篇关于使用 setjmp/longjmp/alloca 实现 coros 的文章:https://fanf.livejournal.com/105413.html

这个想法是,为了让 B 在长跳回到 A 时保留它的完整上下文(不仅仅是寄存器(由 setjmp 保留),还有本地的堆栈变量),B 需要它自己的堆栈或至少它需要确保 A 所做的任何事情都不会覆盖 B 的变量。

alloca 是一种无需深入组装即可实现这一目标的方法。 alloca 基本上会使 B 在堆栈上比 A 移动得更远,因此除非 A 使用深度递归或任何使其使用超过 2KiB(在本例中)的堆栈,否则 A 和 B 将保持它们的 on-堆栈局部变量分开。

(这种技术很自然地不严格符合 C,如果您在多个 malloc'd 堆栈之间使用来回跳转,它会更少。)

【讨论】:

那么2048 拆分堆栈使A & B 分开? @Jiu alloca(2048) 将堆栈指针向前移动2048字节,这意味着routineB将在2048之后开始,当B longjmp-returns A时,A将可以使用之前的2048字节堆栈B 的堆栈状态受到损害。【参考方案2】:

关于第二个问题的解答:

int r 放入数据段中,无论是发布还是在 GCC 中调试都会得到相同的结果。

static int r = 0;

【讨论】:

以上是关于alloca() 和 setjmp 的作用是啥?的主要内容,如果未能解决你的问题,请参考以下文章

setjmp和longjmp简介

alloca() 可以替代 C++ 中的自动指针吗?

MOS管都有哪些作用,工作原理是啥?

关于管道如何在 Bash 中工作的简单解释是啥?

setjmp、longjump 和堆栈重建

setjmp 和省略帧指针