我们有一个带有嵌入式JVM(Sun的)的C ++应用程序。因为我们注册了自己的信号处理程序,所以建议我们在初始化JVM之前这样做,因为它安装了自己的处理程序(see here)。
根据我的理解,JVM在内部知道信号是否源自其自己的代码,如果不是,则将其传递给我们的处理程序。
我们开始看到的是我们正在获得SIGPIPE,其调用堆栈看起来大致如此(顶部条目是我们的信号处理程序):
/.../libos_independent_utilities.so(_ZN2os32smart_synchronous_signal_handlerEiP7siginfoPv+0x9) [0x2b124f7a3989]
/.../jvm/jre/lib/amd64/server/libjvm.so [0x2aaaab05dc6c]
/.../jvm/jre/lib/amd64/server/libjvm.so [0x2aaaab05bffb]
/.../jvm/jre/lib/amd64/server/libjvm.so(JVM_handle_linux_signal+0x718) [0x2aaaab05e878]
/.../jvm/jre/lib/amd64/server/libjvm.so [0x2aaaab05bf0e]
/lib64/libpthread.so.0 [0x3c2140e4c0]
/lib64/libpthread.so.0(send+0x91) [0x3c2140d841]
/.../jvm/jre/lib/amd64/libnet.so [0x2aaabd360269]
/.../jvm/jre/lib/amd64/libnet.so(Java_java_net_SocketOutputStream_socketWrite0+0xee) [0x2aaabd35cf4e]
[0x2aaaaeb3bf7f]
似乎JVM决定从send
引发的SIGPIPE应该传递给我们的信号处理程序。这样做是对的吗?
另外,为什么调用堆栈不完整?我的意思是显然它不能在socketWrite0
之前向我展示java代码,但为什么我不能在java代码之前看到堆栈?
答案 0 :(得分:7)
JVM无法判断SIGPIPE是来自它自己的代码还是代码。该信息不是由信号给出的。因为它不希望你错过你可能感兴趣的任何可能的事件,所以它必须传递给你所有的SIGPIPE,甚至那些它们来自它自己的代码。
Unix信号有两种形式 - “同步”和“异步”。仅执行代码时的一些特殊情况可能导致陷阱并导致“同步”信号。这些是诸如未对齐的内存访问(SIGBUS),非法内存访问,通常为NULL,(SIGSEGV),除以零和其他数学错误(SIGFPE),不可解码指令(SIGILL)等等。它们具有精确的执行上下文,并直接传递给导致它们的线程。信号处理程序可以查找堆栈并看到“嘿,我有一个执行java代码的非法内存访问,指针是NULL。让我去修复它。”
相反,与外界交互的信号是“异步”变种,包括SIGTERM,SIGQUIT,SIGUSR1等。这些信号没有固定的执行上下文。对于线程程序,它们几乎随机地传递给任何线程。重要的是,SIGPIPE就是其中之一。是的,在某种意义上,它通常与一个系统调用相关联。但是(例如)很可能有两个线程监听两个独立的连接,这两个连接在调度任一线程之前关闭。内核只是确保有一个SIGPIPE挂起(通常的实现是待处理信号的位掩码),并在重新安排进程中的任何线程时处理它。这只是JVM可能没有足够信息来排除对此信号感兴趣的客户端代码的简单情况之一。
(至于读取调用会发生什么,它们返回“有错误:EINTR”并继续。此时,JVM可以将其转换为异常,但返回之后> em>信号传递和信号处理程序触发。)
结果是你只需处理误报。 (并且只处理两个可能预期的信号。)