JVM 信号链 SIGPIPE

15
我们有一个带有嵌入式JVM(Sun的)的C++应用程序。由于我们注册了自己的信号处理程序,建议在初始化JVM之前这样做,因为它安装了自己的处理程序(请参见此处)。
据我所知,JVM在内部知道信号是否来自其自己的代码,如果不是,则将其传递到我们的处理程序链中。
我们开始看到的是,我们收到了SIGPIPEs,并且调用堆栈大致如下(顶部条目是我们的信号处理程序):
/.../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代码之前的堆栈?

你是否为SIGPIPE安装了信号处理程序?(如果是的话,是的 - 当有人向损坏的套接字写入时,从send()调用中收到SIGPIPE是很常见的) - nos
我知道send可能会引发SIGPIPE错误。我只是质疑JVM将它们传递给我,为什么不引发异常? - Idan K
但是你没有为它安装处理程序? - nos
我这样做是因为否则我的信号处理程序将不会被调用。 - Idan K
1个回答

8
JVM无法确定SIGPIPE是来自自己的代码还是您的代码。该信号没有提供这些信息。因为它不想让您错过任何可能感兴趣的事件,所以必须将所有SIGPIPE传递给您,即使其中一些是来自其自身的代码。
Unix信号有两种类型——“同步”和“异步”。仅执行代码时的一些异常情况会导致陷阱并产生“同步”信号。这些包括未对齐的内存访问(SIGBUS)、非法的内存访问(通常是NULL)(SIGSEGV)、除零和其他数学错误(SIGFPE)、无法解码的指令(SIGILL)等等。它们具有精确的执行上下文,并直接传递给引起它们的线程。信号处理程序可以查找堆栈并看到“嘿,我在执行java代码时遇到了非法的内存访问,指针是NULL。让我去修复它。”
相比之下,与外界交互的信号是“异步”的类型,包括诸如SIGTERM、SIGQUIT、SIGUSR1等。这些信号没有固定的执行上下文。对于线程化的程序,它们随机地传递给任何一个线程。重要的是,SIGPIPE也属于这类信号。是的,在某种意义上,它通常与一个系统调用相关联。但是完全有可能(例如)有两个线程监听两个不同的连接,两者都在任何一个线程被调度之前关闭。内核只需确保有一个SIGPIPE挂起(通常的实现方式是挂起信号的位掩码),并在进程中重新调度任何线程时处理它。这仅仅是JVM可能没有足够信息来排除您的客户端代码对此信号感兴趣的更简单的情况之一。
(至于读取调用会发生什么,它们返回“发生了错误:EINTR”并继续执行。此时,JVM可以将其转换为异常,但返回发生在信号传递和信号处理程序触发之后。)
总之,您只需要处理误报。(并处理只收到一个信号而预期收到两个信号的情况。)

那么它如何知道所有其他信号是否来自它的代码? - Idan K
我几乎为所有信号设置了信号处理程序,这是第一个表现出看似不良行为的信号。此外,JVM已注册以处理SIGPIPE,证据是调用堆栈中的“JVM_handle_linux_signal” - 此文档还指出JVM捕获了SIGPIPE http://www.oracle.com/technetwork/java/javase/signals-139944.html#gbzbl 在更复杂的情况下,JVM仅会“知道”信号是否来自其自己的代码,例如null指针优化,因此我仍然不理解你的论点。 - Idan K
谢谢,你的解释很有道理,也大致符合我对JVM决定是链式信号还是非链式信号的想法。但是,如果您查看我的信号处理程序中得到的调用堆栈,JVM很可能会检查堆栈并意识到SIGPIPE来自其自己的代码,例如通过在调用堆栈中看到Java_java_net_SocketOutputStream_socketWrite0地址。 - Idan K
这确实意味着内核“将其传递到那里”。但是,这并不保证该代码是实际原因。在这种情况下,它碰巧是,但并非总是如此。 - wnoise

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接