如何保存堆栈和堆

时间:2013-09-20 14:44:57

标签: c stack heap restore

如何在程序中的特定点保存(和恢复)程序的堆栈和堆?

考虑这样的程序:

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 %d\n",a,*b);
  return 0;
}

输出应为:

1 2

归结为(我是对的吗?):如何获得指向堆栈和堆及其大小的指针?

修改

我想将它用于许多方面。其中之一是编写能够通过检查点处理硬件故障并能够在检查点状态下重新启动的代码。

让我们关注堆栈,因为可以跟踪堆分配(例如旧的malloc预加载)。

代码应该是可重用的。堆栈中可以有任何可能的变量数量和类型。

最好的是标准C99。接下来最好的Posix符合。接下来最好的Linux符合。

我通常使用GCC,但我不想使用内置插件...

3 个答案:

答案 0 :(得分:1)

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 %d\n",a,*b);
    return 0;
}

答案 1 :(得分:1)

所以你没有给出你想要达到的目标范围,但我会尝试解决一些观点,至少可以让你开始。

  

归结为(我是对的吗?):我如何获得指向堆栈的指针   以及堆和它们的大小?

堆栈很大,通常可以扩展。我要跳过堆,因为你要努力保存所有的堆(这有点没有任何意义)。获取指向堆栈的指针就像声明变量并像这样引用它一样简单。

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);

这就是我能为您提供的所有信息中的有限信息。

答案 2 :(得分:0)

我认为你想在这种情况下重用变量名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
}