禁用 C++ 异常,如何使任何 std:: throw() 立即终止?

2022-01-23 00:00:00 exception-handling exit g++ c++

这个 C++ 程序是一个 CGI 脚本,我不想处理异常.我宁愿获得边际性能提升,并让操作系统(Linux)在进程终止后处理清理.

This C++ program is a CGI script, I have no desire to deal with exceptions. I'd rather get a marginal performance boost and let the OS (Linux) handle cleanup after the process dies.

我正在使用标准 C++ 库,并且希望任何函数die 就像在 Perl 中一样:只要它抛出异常. 不展开,或运行任何进一步的代码在我的过程中.

I am using the Standard C++ Library, and want any function to die like in Perl: Whenever it throws an exception. Without unwinding, or running any further code in my process.

-fno-exceptions 是如何工作的?如果我的代码中根本没有捕获,并且基本上假装不存在异常.但我确实使用可以 throw()的std::c++库?

How does -fno-exceptions work? If I have no catch at all in my code, and basically pretend like exceptions do no exist. but I do use std:: c++ library which can throw()?

推荐答案

选项#1:永远不要捕获异常.

Option #1: Simply never catch exceptions.

异常在没有被抛出或捕获时不会产生太多开销;如果您正在投掷并且不准备接住,那么无论如何您都会死,因此此时对性能的影响是微不足道的.另请注意,如果未处理异常,则不会执行堆栈展开;程序将直接终止而不执行堆栈展开.

Exceptions don't have much overhead when they're not thrown or caught; if you're throwing and not prepared to catch, well, you're doing to die anyway, so the performance impact at that point is trivial. Note also that stack unwinding will not be performed if an exception is not handled; the program will simply terminate without performing stack unwinding.

需要注意的是,在 G++ 中,异常在没有被实际抛出时几乎没有开销.G++ 生成的额外信息足以通过堆栈追溯程序的执行,以及一些额外的代码来调用析构函数等 - 但是在实际抛出异常之前,不会使用这些额外的代码或数据.因此,您不应该看到启用但未使用的异常代码与禁用异常的代码(通过任何机制)之间的性能差异.

It's important to note that, in G++, exceptions have almost no overhead when not actually thrown. G++ generates extra information sufficient to trace back the execution of the program through the stack, and some extra code to invoke destructors, etc - however none of this extra code or data is ever used until an exception is actually thrown. So you should not see a performance difference between code with exceptions enabled but not used and code with exceptions disabled (through whatever mechanism).

选项 #2:传递 -fno-exceptions.

这个标志指示 G++ 做两件事:

This flag instructs G++ to do two things:

  1. 删除了 STL 库中的所有异常处理;throws 被替换为 abort() 调用
  2. 堆栈展开数据和代码已删除.这节省了一些代码空间,并且可能使编译器的寄存器分配稍微容易一些(但我怀疑它会对性能产生很大影响).然而,值得注意的是,如果抛出异常,并且库尝试通过 -fno-exceptions 代码展开,它将在此时中止,因为没有展开数据.
  1. All exception handling in STL libraries are removed; throws are replaced with abort() calls
  2. Stack unwind data and code is removed. This saves some code space, and may make register allocation marginally easier for the compiler (but I doubt it'll have much performance impact). Notably, however, if an exception is thrown, and the library tries to unwind through -fno-exceptions code, it will abort at that point, as there is no unwind data.

这将有效地将所有异常转换为 abort(),如您所愿.但是请注意,您将不允许 throw - 代码中的任何实际 throwcatch 都将导致编译 -时间错误.

This will, effectively, turn all exceptions into abort()s, as you would like. Note, however, that you will not be allowed to throw - any actual throws or catchs in your code will result in a compile-time error.

选项 #3:(不可移植且不推荐!)挂钩 __cxa_allocate_exception.

Option #3: (Nonportable and not recommended!) Hook __cxa_allocate_exception.

C++ 异常是使用(以及其他)__cxa_allocate_exception 和 __cxa_throw 内部库函数实现的.您可以实现一个 LD_PRELOAD 库,将这些函数与 abort() 挂钩:

C++ exceptions are implemented using (among others) the __cxa_allocate_exception and __cxa_throw internal library functions. You can implement a LD_PRELOAD library that hooks these functions to abort():

void __cxa_allocate_exception() { abort(); }
void __cxa_throw() { abort(); }

警告:这是一个可怕的黑客攻击.它应该适用于 x86 和 x86-64,但我强烈建议不要这样做.值得注意的是,它实际上不会像 -fno-exceptions 那样提高性能或节省代码空间.但是,它将允许 throw syntax,同时将 throws 变成 abort()s.

WARNING: This is a horrible hack. It should work on x86 and x86-64, but I strongly recommend against this. Notably, it won't actually improve performance or save code space, as -fno-exceptions might. However, it will allow the throw syntax, while turning throws into abort()s.

相关文章