如何在程序中的特定点保存(和恢复)程序的堆栈和堆?
考虑这样的程序:
int main()
{
int a;
int *b;
b = (int*)malloc(sizeof(int))
a = 1;
*b = 2;
save_stack_and_heap(); // looking for this...
a = 3;
*b = 4;
restore_stack_and_heap() // ... and this
printf("%d %dn",a,*b);
return 0;
}
输出应为:
1 2
它归结为(我说得对吗?):我如何获得指向堆栈和堆及其大小的指针?
编辑
我想把它用于很多事情。其中之一是编写代码,这些代码可以通过检查点处理硬件故障,并能够在检查点状态下重新启动。
让我们专注于堆栈,因为可以通过其他方式跟踪堆分配(例如,很好的旧malloc预加载)。
代码应该是可重用的。堆栈上可以有任何可能的数量和类型的变量。
最好的是标准C99。下一个最好的波西斯符合。下一个最好的Linux符合。
我通常使用 GCC,但我不想使用内置...
int main()
{
int a = 1;
int *b = malloc(sizeof(int));
*b = 2;
if (fork() == 0) {
a = 3;
*b = 4;
return 0;
}
int status;
wait(&status);
printf("%d %dn",a,*b);
return 0;
}
所以你没有给出你想要实现的目标的很多范围,但我会尝试解决一些观点,至少可以让你开始一些东西。
它归结为(我说得对吗?):我如何获得指向堆栈的指针 以及堆和它们的大小?
堆栈很大,并且通常可以扩展大小。我将跳过堆位,因为您将努力保存所有堆(这没有任何意义)。获取指向堆栈的指针就像声明一个变量并像这样引用它一样简单。
int a = 5;
void *stack_ptr = &a;
void *another_stack_ptr = &stack_ptr;
// We could could go on forever with this....
但是,这不是堆栈的基址。如果你想找到可能有很多方法,甚至是API(我认为Windows上有)。您甚至可以从堆栈上的地址向两个方向走,直到出现页面错误。这可能标志着堆栈的开始和结束。以下方法可能有效,无法保证。你将需要设置异常处理程序来处理页面错误,以便你的应用不会崩溃。
int variable = 5;
int *stack_start = &variable;
int *stack_end = stack_start;
int *last_good_address = NULL;
// Setup an exception handler
...
// Try accessing addresses lower than the variable address
for(;;)
{
int try_read = stack_start[0];
// The read didn't trigger an exception, so store the address
last_good_address = stack_start
stack_start--;
}
// Catch exception
... stack_start = last_good_address
// Setup an exception handler
...
// Try accessing addresses higher than the variable address
for(;;)
{
int try_read = stack_end[0];
// The read didn't trigger an exception, so store the address
last_good_address = stack_end
stack_end--;
}
// Catch exception
... stack_end = last_good_address
因此,如果您有堆栈的基址和结束地址,您现在可以将其内存到一些内存中(不过我建议不要使用堆栈!
如果您只想复制一些变量,因为复制整个堆栈会很疯狂,那么传统方法是在调用之前保存它们
int a = 5;
int b = 6;
int c = 7;
// save old values
int a_old = a;
int b_old = b;
int c_old = c;
some_call(&a, &b, &c);
// do whatever with old values
我假设您编写了一个在堆栈上有 10,000 个变量的函数,并且您不想手动保存它们。在这种情况下,以下内容应该有效。它使用 _AddressOfReturnAddress
获取当前函数堆栈的最高可能地址,并分配一些堆栈内存以获取最低电流值。然后,它会复制介于两者之间的所有内容。
免责声明:这还没有被编译,也不太可能开箱即用,但我相信这个理论是合理的。
// Get the address of the return address, this is the highest address in the current stack frame.
// If you over-write this you are in trouble
char *end_of_function_stack = _AddressOfReturnAddress();
// Allocate some fresh memory on the stack
char *start_of_function_stack = alloca(16);
// Calculate the difference between our freshly allocated memory and the address of the return address
// Remember to subtract the size of our allocation from this to not include it in the stack size.
ptrdiff_t stack_size = (end_of_function_stack - start_of_function_stack) - 16);
// Calculation should not be negative
assert(stack_size > 0)
// Allocate some memory to save stack variables
void *save_the_stack = malloc(stack_size);
// Copy the variables
memcpy(save_the_stack, &start_of_function_stack[16], stack_size);
这就是我能为您提供的问题中有限信息的全部内容。
我认为您希望在这种情况下重用变量名称 a 和 b?您应该在不同的作用域上声明同名的新变量!
int main()
{
int a=1;
int *b = (int*)malloc(sizeof(int));
*b=2;
{
int a=3;
int *b = (int*)malloc(sizeof(int));
*b=4
}//beware, other lang such as C# may persist stack variables after this point
//old a,b should be reachable here
}