JVM 信号链 SIGPIPE

2022-09-03 15:44:16

我们有一个带有嵌入式JVM(Sun)的C++应用程序。因为我们注册了自己的信号处理程序,所以建议在初始化JVM之前这样做,因为它会安装自己的处理程序(请参阅此处)。

根据我的理解,JVM在内部知道信号是否来自它自己的代码,如果不是,它沿着链传递给我们的处理程序。

我们开始看到的是,我们正在获得SIPPE,其调用堆栈看起来大致如下所示(顶部条目是我们的信号处理程序):

/.../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 似乎正在决定将从中引发的 SIGPIPE 传递给我们的信号管理器。这样做是对的吗?send

另外,为什么调用堆栈不完整?我的意思是,显然它之前不能向我显示java代码,但是为什么我无法在java代码之前看到堆栈?socketWrite0


答案 1

JVM无法判断SIGPIPE是来自它自己的代码,还是来自你的代码。这些信息不是由信号给出的。因为它不希望您错过任何您可能感兴趣的事件,所以它必须将您传递给所有SIPPE,即使事实证明这些SIPPE来自它自己的代码。

Unix信号有两种类型-“同步”和“异步”。仅执行代码时的一些特殊情况可能会导致陷阱并导致“同步”信号。这些是诸如未对齐的内存访问(SIGBUS),非法内存访问,通常是NUL(SIGSEGV),除以零和其他数学错误(SIGFPE),不可编码指令(SIGILL)等。它们具有精确的执行上下文,并直接传递到导致它们的线程。信号处理程序可以查找堆栈并看到“嘿,我得到了一个非法的内存访问执行java代码,指针是NULL。让我去解决这个问题吧。

相比之下,与外部世界交互的信号是“异步”品种,包括SIGTERM,SIGQUIT,SIGUSR1等。它们没有固定的执行上下文。对于线程程序,它们几乎是随机传递到任何线程的。重要的是,SIGPIPE就是其中之一。是的,从某种意义上说,它通常与一个系统调用相关联。但是很有可能(例如)有两个线程监听两个单独的连接,这两个连接都在调度任何一个线程之前关闭。内核只是确保有一个 SIGPIPE 挂起(通常的实现是作为挂起信号的位掩码),并在重新调度进程中的任何线程时处理它。这只是 JVM 可能没有足够的信息来排除客户端代码对此信号感兴趣的更简单情况之一。

(至于读取调用会发生什么情况,它们返回“有一个错误:EINTR”并继续。此时,JVM 可以将其转换为异常,但返回发生在信号传递和信号处理程序触发之后

结果是你只需要处理误报。(并处理只获得一个信号,而预期可能有两个信号。


答案 2