繁体   English   中英

安全使用`setjmp`和`longjmp`

[英]Safe usage of `setjmp` and `longjmp`

我知道人们总是说不要使用longjmp ,它是邪恶的,它是危险的。

但我认为它对退出深度递归/嵌套函数调用很有用。

单个longjmp比很多重复检查和返回更快,如if(returnVal != SUCCESS) return returnVal;

至于安全性,只要动态内存和其他资源正确发布,就不会出现问题,对吧?

到目前为止似乎使用longjmp并不困难,它甚至使我的代码更加严格。 我很想使用它。

(恕我直言,在很多情况下,首先在深度递归中没有分配动态内存/资源。深度函数调用似乎更常见于数据解析/操作/验证。动态分配通常发生在更高级别,在调用函数之前setjmp出现。)

setjmplongjmp可以看作是一个穷人的异常机制。 顺便说一句, Ocaml异常与setjmp一样快,但语义更清晰。

当然, longjmp比在中间函数中重复返回错误代码要快得多,因为它会弹出一个可能很重要的调用堆栈部分。

(我隐含地专注于Linux)

只要没有在它们之间分配资源,它们就是有效且有用的,包括:

  • 堆内存( malloc
  • fopen -ing FILE*处理
  • 打开操作系统文件描述符(例如,用于套接字)
  • 其他操作系统资源,例如定时器或信号处理程序
  • 获取一些服务器管理的外部资源,例如X11窗口(因此使用任何小工具包如GTK),或数据库句柄或连接......
  • 等等...

主要问题是泄漏资源的属性是全局整体程序属性 (或者至少是可能在setjmplongjmp之间调用的所有函数的全局属性 ),因此它禁止模块化软件开发 :任何其他同事必须改进一些代码setjmplongjmp之间的任何函数都必须知道该限制并遵循该规则。

因此,如果您非常清楚地使用 setjmp文档。

顺便说一句,如果你关心malloc系统地使用Boehm的保守垃圾收集器会有很大帮助; 你将在任何地方使用GC_malloc而不是malloc ,你不会关心free ,实际上GC_malloc足够了; 那么你可以毫无恐惧地使用setjmp (因为你可以在setjmplongjmp之间调用GC_malloc )。

(请注意,垃圾收集器周围的概念和术语与异常处理和setjmp密切相关,但很多人对它们了解不多。阅读垃圾收集手册应该是值得的)

阅读有关RAII的内容并了解C ++ 11异常(以及它们与析构函数的关系)。 了解延续CPS

读取setjmp(3)longjmp(3) (以及关于sigsetjmpsiglongjmpsetcontext(3) )并注意编译器必须知道setjmp

您应该注意,在某些上下文中调用setjmp并不保证是安全的(例如,您无法移植存储setjmp的返回值)。

此外,如果要在调用setjmp之后访问局部变量,在同一函数中可能已更改,则应将变量标记为volatile。

使用setjmp和longjmp也很有用,因为如果递归导致堆栈溢出,您可以使用信号处理程序中的longjmp进行恢复(不要忘记设置备用堆栈)并返回错误。 如果你想这样做,你应该考虑使用sigsetjmp和siglongjmp来保留信号处理。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM