经过非常长时间的搜索和相关错误,我发现了这种奇怪的行为:
如果在 Linux 上我运行一个 JNI 方法来执行 select
:
JNIEXPORT void JNICALL Java_SelectJNI_select(JNIEnv *env, jobject thisObj) {
// Print the curerent PID
fprintf(stderr, "PID: %d\n", getpid());
// Wait for 30 seconds
struct timeval *timeout = (struct timeval *) calloc(1, sizeof(struct timeval));
timeout->tv_sec = 30;
timeout->tv_usec = 0;
select(0, NULL, NULL, NULL, timeout);
return;
}
然后我用 strace 运行可执行文件,select
不是用我打印的 PID 执行的,而是用 child 的 PID 执行的,原始对象实际上在等待互斥锁(这如果我在普通的小型 C 程序中执行相同的调用,则不会发生。
说 strace -f -o strace_output.txt java SelectJNI
打印:
PID: 46811
然后 grep select\( strace_output.txt
将返回:
46812 select(0, NULL, NULL, NULL, {tv_sec=30, tv_usec=0} <unfinished ...>
我的猜测是 JNI 正在 fork ,以某种方式用它自己的包装版本替换原始选择,可能是为了保持响应。
我有很多问题,但我更关心的是:
- 我的假设正确吗? JNI 取代了我脚下的功能?
- 是否在某处记录了此行为?
- 调用实际选择的过程似乎总是第一个 child 的过程。我可以依靠它吗?如果不是,我如何找出
select
实际运行的位置?
最佳答案
JVM 确实可以 fork ,但这样做是为了创建新的 JVM 线程,而不是整个进程。虽然 46811 是 PID,但实际运行您的代码的线程具有 TID 46812(这是 strace 打印的内容),同时仍在 PID 46811 下运行。替换 getpid
在示例中使用 gettid
应该会导致一致的输出。
关于Java Native Interface 偷偷摸摸的 fork 行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66122018/