【发布时间】:2013-02-08 08:02:23
【问题描述】:
动机
我正在尝试通过setjmp 保存各种环境,以便稍后跳转回它们并调用函数并确保堆栈帧相互运行。例如:
env[0] 堆栈:
----------
| text |
----------
| data |
----------
| |
| |
| |
| |
| |
| |
| |
| ~~~~ |
----------
波浪线代表 main 等的堆栈帧。
env[1] 堆栈:
----------
| text |
----------
| data |
----------
| |
| |
| |
| **** |
| **** |
| **** |
| **** |
| ~~~~ |
----------
星星代表我分配的一个大数组。
想法是如果我longjmp到env[0]开始运行一些方法,然后longjmp到env[1]开始运行一些方法,env[0]的堆栈帧将开始填满空的该数组中的空间,并且env[1] 的堆栈帧将位于该数组的顶部,并且不会覆盖env[0] 的堆栈帧。基本线程。
我还想要不止两个线程,事实上,我想要 MAXTHREADS。直觉上它是这样工作的:
for(int i = 0; i < MAXTHREADS; i++) {
char c[STACKSIZE];
if( setjmp(env[i]) != 0 ) {
/* Stuff that thread i will do goes here */
}
}
测试 1
但是,正如您在下面的测试程序中清楚地看到的那样,每个 c 数组都从堆栈上的同一位置开始,违背了目的。
for(int i = 0; i < 10; i++) {
char c[10];
printf("On Iteration %d array starts at %x and goes to %x\n",i,c,c+10);
}
输出:
On Iteration 0 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 1 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 2 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 3 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 4 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 5 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 6 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 7 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 8 array starts at ffbfef02 and goes to ffbfef0c
On Iteration 9 array starts at ffbfef02 and goes to ffbfef0c
出于多种原因,这是有道理的,范围表明 c 数组在循环结束时不再存在,此外编译器可能会将其移出循环,因为大小是静态的。
测试 1 给了我想要的大小,但不是我想要的分配
测试 2
所以我的下一个想法是分配一个 STACKSIZE * i 数组,这样即使每个数组都从同一个位置开始,它们每次都会变大,并且在切换时将我的堆栈指针推得足够远到那个环境。这是测试:
for(int i = 0; i < 10; i++) {
char c[10 * i];
printf("On Iteration %d array starts at %u and goes to %u\n",i,c,c+(10*i));
//note: switched from %x to %u to make comparison mentally simpler
}
但是,这会输出
On Iteration 0 array starts at 4290768760 and goes to 4290768760
On Iteration 1 array starts at 4290768744 and goes to 4290768754
On Iteration 2 array starts at 4290768720 and goes to 4290768740
On Iteration 3 array starts at 4290768688 and goes to 4290768718
On Iteration 4 array starts at 4290768648 and goes to 4290768688
On Iteration 5 array starts at 4290768592 and goes to 4290768642
On Iteration 6 array starts at 4290768528 and goes to 4290768588
On Iteration 7 array starts at 4290768456 and goes to 4290768526
On Iteration 8 array starts at 4290768376 and goes to 4290768456
On Iteration 9 array starts at 4290768280 and goes to 4290768370
一开始有点难看,但由于数字很大,你可以只关注最后三位。
第一个数组从 760 到 760 -- 很好,它是 i = 0
第二个数组从 744 到 754 -- 很好,我们移动了一点,但它是一个大小为 10 的数组,在我们想要的一般位置
第三个数组从 720 到 740 -- 呃-哦,这个数组的起始点与其他数组不同,它的大小是原来的两倍.
为了说明堆栈上的模式,每个 * 代表一个数组中的 10 个字符,星块之间的间隙是数组之间的间隙:
*
*
*
*
*
*
~
波浪号是主要的,等等。
模式继续。这可能与机器有关,但我的机器是按顺序分配数组,而不是像固定大小的数组那样每次都回到底部。
测试 2 给了我想要的分配,但大小比我想要的要大。
测试 3
现在得知我的机器将分配数组,如果它们的大小相同,则它们会相互覆盖,但如果它们的大小不同,则会堆叠在一起,我想也许我可以分配 STACKSIZE + i 而不是 @987654338 @ 每个。我进行了这个实验:
for(int i = 0; i < 10; i++) {
char c[10+i];
printf("On Iteration %d array starts at %u and goes to %u\n",i,c,c+10+i);
}
哪些输出:
On Iteration 0 array starts at 4290768808 and goes to 4290768818
On Iteration 1 array starts at 4290768792 and goes to 4290768803
On Iteration 2 array starts at 4290768776 and goes to 4290768788
On Iteration 3 array starts at 4290768760 and goes to 4290768773
On Iteration 4 array starts at 4290768744 and goes to 4290768758
On Iteration 5 array starts at 4290768728 and goes to 4290768743
On Iteration 6 array starts at 4290768712 and goes to 4290768728
On Iteration 7 array starts at 4290768688 and goes to 4290768705
On Iteration 8 array starts at 4290768664 and goes to 4290768682
On Iteration 9 array starts at 4290768640 and goes to 4290768659
经过分析,它使用了与我预期相同的分配行为。
测试 3 给了我想要的分配,尺寸接近我想要的,但仍然不完美
问题
所以测试 3 的策略可以在我的原始代码中运行,分配 STACKSIZE + i 的 MAXTHREADS 数组,在每个数组之间保存 env,但它似乎不是最理想的。我在每个数组中分配额外的空间只是为了欺骗我的编译器。
有没有更好的方法来做到这一点?让编译器每次都分配新的 STACKSIZE 数组的其他技巧?
如何获得测试 2 和 3 的分配以及测试 1 的大小?
【问题讨论】:
-
你可以标记数组
volatile?那么编译器不应该为了优化而碰它。 -
无效。用第一个测试代码试了一下,改成
volatile char c[10],输出一样。 -
您似乎相信您有权获得“堆栈”。鉴于 C11 标准添加的功能,我相信“图”可能更适合存储具有自动存储持续时间的对象。
-
我刚醒来,所以还是有点头晕,但当然使用
volatile是行不通的……编译器可能仍会在循环内重用相同的堆栈空间。 -
我想内联
asm是不可能的 =(
标签: c arrays optimization compiler-construction