Java Native Interface 偷偷摸摸的分叉行为
Posted
技术标签:
【中文标题】Java Native Interface 偷偷摸摸的分叉行为【英文标题】:Java Native Interface sneaky forking behavior 【发布时间】:2021-02-09 15:29:02 【问题描述】:在 非常 长时间的寻找和一个相关的错误之后,我遇到了这种奇怪的行为:
如果在 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 执行的,而是使用孩子的 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 正在分叉,并且在某种程度上用自己的包装版本替换了原始选择,可能是为了保持响应。
我有很多问题,但我更关心的是:
-
我的假设正确吗? JNI 替换我脚下的函数?
此行为是否记录在某处?
调用实际选择的进程似乎总是第一个子进程。我可以依靠吗?如果没有,我如何找出
select
实际运行的位置?
【问题讨论】:
您是否确认父级没有立即分叉以设置 JVM 中预期的许多线程? 我可能没听懂你的话,但我不认为是这样的:如果父母已经分叉了,那么printf报告的pid和in strace 将是相同的。 令我惊讶的是,该进程似乎在 fprintf 之后分叉或委托选择调用 ,即调用 select 时。不过,也许我没听懂你的话。 如果我没记错的话,strace 输出中的 46812 是 TID,而不是 PID。而是打印gettid
的结果。
@Rick77 跑题了,但你可以用grep select\(
代替strace -f -e select ...
【参考方案1】:
JVM 可能确实分叉,但它这样做是为了创建新的 JVM 线程,而不是整个进程。虽然 46811 是 PID,但实际运行相关代码的线程具有 TID 46812(这是 strace 打印的内容),同时仍在PID 46811 下运行。将getpid
替换为gettid
样本应该导致一致的输出。
【讨论】:
谢谢@nanofarad,我可以在打印gettid()
结果后确认是这种情况。不过,我有点惊讶(在一个更复杂的示例中)我能够通过使用 TID 而不是 PID 向进程发送 SIGINT:“kill”是否也适用于 TID?
@Rick77 可以使用进程中的任何 TID (ref) 将信号路由到 进程,但哪个线程处理信号取决于信号是每个进程、每个线程等(请参阅链接参考)【参考方案2】:
我想详细说明@nanofarad 接受的答案,并明确解决我自己问题的 3 点。
我的猜测是 JNI 正在分叉,并且在某种程度上取代了 带有自己包装版本的原始选择,可能会保留 反应灵敏。 [...]
我的假设正确吗? JNI 替换我脚下的函数?
不,不是。
JNI 执行的select
没有什么特别之处。
JNI 将其替换为“分叉进程”的假设是错误的:我只是将strace
打印的 TID 误解为 PID。
JNI 只是在 Java 线程中执行 strace。
此行为是否记录在某处?
不需要:因为 JNI 调用是在调用 Java 线程中执行的,所以没有什么可写的。
调用实际选择的进程似乎总是第一个子进程的进程(等等...)
这是第一个派生线程的 TID,似乎总是等于 PID + 1,但我是一个可能的行为(Java 线程是在运行时启动后立即创建的),它不一定是。
【讨论】:
以上是关于Java Native Interface 偷偷摸摸的分叉行为的主要内容,如果未能解决你的问题,请参考以下文章
职场救星: 用Python如何实现办公效率化,挤出时间去偷偷摸鱼
职场救星: 用Python如何实现办公效率化,挤出时间去偷偷摸鱼
+Java中的native关键字浅析(Java+Native+Interface)++
Java固有接口JNI(Java Native Interface)之HelloWorld