C++ 异常框架是否需要内核支持?
Does C++ exception framework require kernel support?
我正在阅读 C++ 中的异常支持,可能结果也适用于其他语言。
https://wiki.osdev.org/C%2B%2B_Exception_Support
http://www.ucw.cz/~hubicka/papers/abi/node25.html
事情很多,我有点不知所措。我的问题是在展开过程中,C++是否需要内核的帮助(例如setjmp/longjmp评论中提到,这两个函数不涉及内核,然而我的问题还是一样)?或者整个过程只发生在用户 space?
我可以理解本地线程的展开(想想 setjmp/longjmp)但是无法理解远程线程是如何展开的。
如果所有的展开魔法都发生在用户 space 中,一个线程如何修改另一个有故障的线程以将其发送到处理代码而无需内核帮助?由于我相信线程上下文信息(任务结构)存在于内核中并且不允许直接从用户 space 进行修改,而无需访问线程上下文,它是如何实现的?谢谢!
更新:看来我对任务结构有一些根本性的误解。刚碰到有关信号处理的幻灯片 (signal handlers) (http://users.cms.caltech.edu/~donnie/cs124/lectures/CS124Lec15.pdf),进入信号处理程序后,内核堆栈为空,所有上下文信息都位于用户堆栈中。我想可以使用信号与其他故障线程进行通信以放松?如果是这样,是否意味着 C++ 异常支持需要内核帮助,因为生成信号是内核调用?
更新:就问题而言,我对保罗的回答感到满意。我想根据我对comment/answer的阅读,从更广泛的意义上总结一下我对这个话题的理解,如果我错了,请指正。
C++ 异常框架没有指定具体的实现方法,以下是我认为“通用”的总结。
异常对象的来源有3种,其展开的实现如下:
+------------------------------------------------+--------------------------------------------------------------------------+
| exception source | unwinding method |
+------------------------------------------------+--------------------------------------------------------------------------+
| a) exception created by `throw` | via code to walk the stack and assembly to transfer control to call site |
| b) exception created by hardware e.g. SIGFPE | via synchronous signal, and code to walk the stack |
| | and assembly to transfer control to call site |
| c)* exception created by remote process/thread | via asynchronous signal, and code to walk the stack |
| | and assembly to transfer control to call site |
+------------------------------------------------+--------------------------------------------------------------------------+
*对于案例c),我不确定是否有任何现实世界的用法需要由外部进程创建异常并在本地处理,它不符合throw
/ catch
语义(至少它是可行的),我把它放在这里只是为了完整性。
没有
据我所知,这都是在 C++ 运行时库中实现的(对于 GCC,这将是 libgcc_s.so,至少在 Linux 上是这样)。这需要代码遍历堆栈以查找 catch 块,并需要一些机器代码在找到时跳入此类块。正如已经提到的,setjmp/longjmp 也不需要内核。
更新:不要混淆 C++ 异常和信号。如果你想看到更多
C++ 异常如何工作是一个好的开始(如果你喜欢阅读
汇编程序)将使用 godbolt 来查看生成的汇编程序以查找一些抛出和捕获异常的简单函数。当心那个
浮点异常 (FPE) 确实是信号。
C++ 异常和 setjmp/longjmp + 信号之间的一个区别是
控制流程。后两者使用一种 'call'(保存上下文)和 'return'(恢复上下文)实现 'resumption semantics'。另一方面,C++ 异常实现 'termination semantics ' 并且无法返回到 throw
.
的站点
信号分为两类:同步信号立即传送(如 SIGBUS)和异步信号在下一个调度程序时间片传送(如 SIGINT)。这两种情况都通过内核传递。没有真正的
从用户角度来看的差异。
线程不能与其他任何进行堆栈操作的东西(setjmp/longjmp 和信号)很好地混合。特别是多线程应用程序可以
在任何线程上接收异步信号。为了使这更易于管理,
通常的做法是使用 pthread_sigmask
来阻止所有信号
除了一个线程之外的所有线程,它将成为信号处理线程。
我正在阅读 C++ 中的异常支持,可能结果也适用于其他语言。
https://wiki.osdev.org/C%2B%2B_Exception_Support
http://www.ucw.cz/~hubicka/papers/abi/node25.html
事情很多,我有点不知所措。我的问题是在展开过程中,C++是否需要内核的帮助(例如setjmp/longjmp评论中提到,这两个函数不涉及内核,然而我的问题还是一样)?或者整个过程只发生在用户 space?
我可以理解本地线程的展开(想想 setjmp/longjmp)但是无法理解远程线程是如何展开的。
如果所有的展开魔法都发生在用户 space 中,一个线程如何修改另一个有故障的线程以将其发送到处理代码而无需内核帮助?由于我相信线程上下文信息(任务结构)存在于内核中并且不允许直接从用户 space 进行修改,而无需访问线程上下文,它是如何实现的?谢谢!
更新:看来我对任务结构有一些根本性的误解。刚碰到有关信号处理的幻灯片 (signal handlers) (http://users.cms.caltech.edu/~donnie/cs124/lectures/CS124Lec15.pdf),进入信号处理程序后,内核堆栈为空,所有上下文信息都位于用户堆栈中。我想可以使用信号与其他故障线程进行通信以放松?如果是这样,是否意味着 C++ 异常支持需要内核帮助,因为生成信号是内核调用?
更新:就问题而言,我对保罗的回答感到满意。我想根据我对comment/answer的阅读,从更广泛的意义上总结一下我对这个话题的理解,如果我错了,请指正。
C++ 异常框架没有指定具体的实现方法,以下是我认为“通用”的总结。
异常对象的来源有3种,其展开的实现如下:
+------------------------------------------------+--------------------------------------------------------------------------+
| exception source | unwinding method |
+------------------------------------------------+--------------------------------------------------------------------------+
| a) exception created by `throw` | via code to walk the stack and assembly to transfer control to call site |
| b) exception created by hardware e.g. SIGFPE | via synchronous signal, and code to walk the stack |
| | and assembly to transfer control to call site |
| c)* exception created by remote process/thread | via asynchronous signal, and code to walk the stack |
| | and assembly to transfer control to call site |
+------------------------------------------------+--------------------------------------------------------------------------+
*对于案例c),我不确定是否有任何现实世界的用法需要由外部进程创建异常并在本地处理,它不符合throw
/ catch
语义(至少它是可行的),我把它放在这里只是为了完整性。
没有
据我所知,这都是在 C++ 运行时库中实现的(对于 GCC,这将是 libgcc_s.so,至少在 Linux 上是这样)。这需要代码遍历堆栈以查找 catch 块,并需要一些机器代码在找到时跳入此类块。正如已经提到的,setjmp/longjmp 也不需要内核。
更新:不要混淆 C++ 异常和信号。如果你想看到更多 C++ 异常如何工作是一个好的开始(如果你喜欢阅读 汇编程序)将使用 godbolt 来查看生成的汇编程序以查找一些抛出和捕获异常的简单函数。当心那个 浮点异常 (FPE) 确实是信号。
C++ 异常和 setjmp/longjmp + 信号之间的一个区别是
控制流程。后两者使用一种 'call'(保存上下文)和 'return'(恢复上下文)实现 'resumption semantics'。另一方面,C++ 异常实现 'termination semantics ' 并且无法返回到 throw
.
信号分为两类:同步信号立即传送(如 SIGBUS)和异步信号在下一个调度程序时间片传送(如 SIGINT)。这两种情况都通过内核传递。没有真正的 从用户角度来看的差异。
线程不能与其他任何进行堆栈操作的东西(setjmp/longjmp 和信号)很好地混合。特别是多线程应用程序可以
在任何线程上接收异步信号。为了使这更易于管理,
通常的做法是使用 pthread_sigmask
来阻止所有信号
除了一个线程之外的所有线程,它将成为信号处理线程。