alloca() 和 setjmp 的作用是什么?
What's the function of alloca() with setjmp?
这个问题来自Practical usage of setjmp and longjmp in C and 我问过的
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
之后堆栈中发生了什么。
问题 1:
是什么魔法让 routineB
的堆栈在使用 alloca(2048)
后还活着?
我听说 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。
这是一篇关于使用 setjmp/longjmp/alloca 实现 coros 的文章: https://fanf.livejournal.com/105413.html 。
这个想法是,为了让 B 在长跳回到 A 时保留它的完整上下文(不仅是寄存器(由 setjmp 保留),还有局部的堆栈变量),B 需要它自己的堆栈或至少它需要确保无论 A 做什么都不会覆盖 B 的变量。
alloca
是一种无需深入研究汇编即可实现的方法。
alloca
基本上会将 B 在堆栈上移动得比 A 远得多,因此除非 A 使用深度递归或任何使其使用超过 2KiB(在本例中)的堆栈,否则 A 和 B 将保留它们的堆栈上的局部变量分开。
(这种技术很自然地不严格符合 C,如果你在多个 malloc
' 堆栈之间使用来回跳转,它就更不符合了。)
关于第二题的解答:
将 int r
放入数据段将在 GCC 中发布或调试时给出相同的结果。
static int r = 0;
这个问题来自Practical usage of setjmp and longjmp in C and
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
之后堆栈中发生了什么。
问题 1:
是什么魔法让 routineB
的堆栈在使用 alloca(2048)
后还活着?
我听说 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。
这是一篇关于使用 setjmp/longjmp/alloca 实现 coros 的文章: https://fanf.livejournal.com/105413.html 。
这个想法是,为了让 B 在长跳回到 A 时保留它的完整上下文(不仅是寄存器(由 setjmp 保留),还有局部的堆栈变量),B 需要它自己的堆栈或至少它需要确保无论 A 做什么都不会覆盖 B 的变量。
alloca
是一种无需深入研究汇编即可实现的方法。
alloca
基本上会将 B 在堆栈上移动得比 A 远得多,因此除非 A 使用深度递归或任何使其使用超过 2KiB(在本例中)的堆栈,否则 A 和 B 将保留它们的堆栈上的局部变量分开。
(这种技术很自然地不严格符合 C,如果你在多个 malloc
' 堆栈之间使用来回跳转,它就更不符合了。)
关于第二题的解答:
将 int r
放入数据段将在 GCC 中发布或调试时给出相同的结果。
static int r = 0;