【问题标题】:Save & Copy jmp_buf C++保存和复制 jmp_buf C++
【发布时间】:2015-09-24 02:04:11
【问题描述】:

是否可以制作 jmp_buf 的副本并在以后恢复它?像

jmp_buf oldEnv = env;
int val = setjmp(env);
.......
env = oldEnv;

我使用 memcopy() 和 sizeof(env) 来复制 jmp_buf 数据。这似乎工作正常。这只是巧合吗?

【问题讨论】:

  • longjmp 在 C++ 中一开始就无效,您需要使用异常来进行跨函数流控制,因为 C++ 依赖于适当的堆栈展开。 setjmp/longjmp 可能会在异常情况下实现,但不能保证堆栈展开。标准实际上说的是:“如果将setjmplongjmp 替换为catchthrow,则setjmp/longjmp 调用对具有未定义的行为,将为任何自动对象调用任何非平凡的析构函数。 "

标签: c++ setjmp


【解决方案1】:

只有在此期间您不使用 jmp_buf 做任何事情时,您才能这样做。最值得注意的是,您不能再次调用setjmp,恢复旧的jmp_buf 内容,并使用longjmp 回到之前的setjmp 调用。

C 标准中的规则:

longjmp 函数恢复由最近一次调用setjmp保存的环境,该调用在程序的同一调用中使用相应的jmp_buf 参数。

因为jmp_buf是“数组类型”,所以对longjmp的调用实际上是传递了一个衰减的指针;上面关于“对应的jmp_buf参数”的说法引用的是jmp_buf的实际地址,而不仅仅是它的内容。

如果您一直对 jmp_buf 的内容感到困惑,我不知道标准如何保证您将返回到最新的 setbuf 的上下文,所以我会处理任何修改jmp_buf 使其完全无法用于 longjmp 目的。

如果您对特定平台上的内部布局有所了解,并且您正在使用jmp_buf 保存处理器上下文以进入调试日志,那么这种事情就很好。但是副本不能与longjmp一起使用。

【讨论】:

    【解决方案2】:

    根据http://en.cppreference.com/w/c/program/jmp_buf,未指定 jmp_buf 的类型。因此,当您实际使用 jmp_buf 时,您不知道会得到什么,并且 sizeof 可能不会返回您想要复制的内存的实际大小。 memcpy 和 sizeof 可能会起作用,但由于您不确定,您可能会遇到各种错误。

    这也引出了你为什么要复制它的问题?您根本不应该使用 jmp_buf 的内容。当 setjmp 填充env 时,您所做的只是为操作系统提供存储空间。就像 Ron Popeil 说的,“设置好然后忘记它。”

    【讨论】:

      【解决方案3】:

      唯一安全的方法是实现您自己的setjmp/longjmp 相似。幸运的是,对于大多数编译器/架构对来说,这很容易做到。

      【讨论】:

        【解决方案4】:

        回复中有很多不准确的信息。

        Ben 是对的,jmp_buf 不能可靠地保存和恢复,并且不能可靠地与具有非平凡析构函数的 自动对象 一起使用。但是,如果最初的问题实际上是如何保存多个非本地 goto 点,则一种解决方案是只使用 jmp_buf 堆栈:

        jmp_buf env[2];
        int env_index = 0;
        
        // first/outer computation
        if (setjmp(jmp_buf[env_index++], 0) == 0) {
           ...
           .. call second/inner computation here ..
           env_index--;
        } else {
           ...
           env_index--;
        }
        
        // in second/inner computation
        if (setjmp(jmp_buf[env_index++], 0) == 0) {
          .. do computation here ..
          env_index--;
        } else {
          .. trap code here ..
          env_index--;
        }
        

        关于setjmp/longjmp 与 C++ 中的try/catch:使用setjmp/longjmp 的唯一限制是在创建具有非平凡析构函数的自动对象但不销毁它们之间setjmplongjmp - 也就是说,如果我执行类似 setjmp 后跟 std::map<std::string, int> x; 的自动变量的操作,则将一个或两个值放入 x,然后是 longjmp - @987654336 的析构函数不调用 @ 来清理内部映射分配。但如果我有这样的事情:

        // some external library written purely in C
        extern "C" do_some_floating_point_computation(params...);
        
        void SIGFPE_handler(...) {
          ...
          .. do longjmp back to wrap_C_Computation_Library here ..
          .. after SIGFPE handling ..
        }
        
        SomeClass::wrap_C_Computation_Library(...) {
          .. set up SIGFPE handler ..
          .. setjmp ..
          do_some_floating_point_computation(params...);
          .. setjmp error handler
          .. clean up SIGFPE handler ..
        }
        

        在类方法中使用setjmp 并在SIGFPE 处理程序中使用longjmp 没有任何问题。 (同样,在不依赖具有非平凡析构函数的自动对象的 C++ 方法调用中捕获一些信号也是可以的。)

        【讨论】:

          猜你喜欢
          • 2013-05-28
          • 2023-04-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多