C 如何保存堆栈和堆

C 如何保存堆栈和堆,c,stack,heap,restore,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,*

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

考虑这样一个程序:

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,但我不希望使用内置的…

我想您希望在这种情况下重用变量名a和b?您应该在不同的作用域上声明相同名称的新变量

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

所以你还没有给出很多你想要实现的目标,但我会尝试解决一些观点,至少是一些可以让你开始的东西

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

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

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
因此,如果您有堆栈的基地址和结束地址,现在可以将其memcpy到一些内存中(不过我建议不要使用堆栈!)

如果您只想复制几个变量,因为复制整个堆栈会很疯狂,传统的方法是在调用之前保存它们

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
我假设您已经编写了一个堆栈上有10000个变量的函数,并且您不想手动保存所有变量。在这种情况下,以下内容应该起作用。它用于获取当前函数堆栈的最高可能地址,并分配一些堆栈内存以获取最低的当前值。然后它复制中间的所有内容

免责声明:这还没有被编译,不太可能开箱即用,但我相信这个理论是正确的

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

这就是我所能提供给您的关于您问题的有限信息。

否。还有需要保存和恢复的静态数据段,以及文件句柄、套接字等资源。这似乎是一个相当糟糕的想法,至少在您的代码中而不是在操作系统中实现时是如此。你想做什么?为什么?听起来像是“保存进程状态”。我能想到的最简单的解决方案是
fork()
。虽然还不清楚问题出在哪里。让我们暂时假设资源和内存从保存到恢复都是有效的。我不知道有哪种操作系统允许您这样做。我看过一些关于事务性内存操作系统的研究,但除了实验之外没有什么有用的。我想对这个答案给出两个“ups”。。。这很有帮助。你能详细说明C中的异常处理吗?我会用C++来做,但是代码需要C。我还要更新我的答案,我要做的事情是什么。什么是OS /编译器。您可以在Windows上使用try,catch in c,因为MSVC支持SEH,并且有一个API,允许您在需要时手动设置异常处理程序。对于Linux,我不知道。但是异常是一个操作系统问题,所以会有办法!可能有很多变量,代码应该是可重用的。我想范围还不够。这是一个有趣的方法。即使我一直在使用线程,我也没有想到用这种方式使用线程;)我会看看我能走多远。。。