今天的定时任务抛了个异常,在调用栈中发现有个名为UNIXProcess的类,这里记录一下。
UNIXProcess类是*nix系统在java程序中的体现,程序员可以使用该类创建新进程,实现与”fork”类似的功能(对于Windows系统,使用的是java.lang.ProcessImpl类)。相关用法参见Runtime.exec方法。下面继续说说UNIXProcess本身。
java.lang.UNIXProcess这个类只存在于*nix系的操作系统中,并且分为linux、bsd、solaris个版本(mac os使用的是solaris版本的),它们都位于$openjdk_src_home/jdk/src/solaris/classes/java/lang目录下。
在编译的时候,会根据当前操作系统的不同,会编译指定的UNIXProcess.java文件,并将其拷贝到rt.jar中,,参见makefile
# UNIXProcess.java is different for solaris and linux. We need to copy
# the correct UNIXProcess.java over to $(GENSRCDIR)/java/lang/.
ifeq ($(PLATFORM), macosx)
PLATFORM_UNIX_PROCESS = \
$(PLATFORM_SRC)/classes/java/lang/UNIXProcess.java.bsd
else
PLATFORM_UNIX_PROCESS = \
$(PLATFORM_SRC)/classes/java/lang/UNIXProcess.java.$(PLATFORM)
endif
$(GENSRCDIR)/java/lang/UNIXProcess.java: $(PLATFORM_UNIX_PROCESS)
$(install-file)
其中,install-file
的定义在Defs.gmk中:
# Simple install of $< file to $@
define install-file
$(prep-target)
$(CP) $< $@
endef
就其区别来说,bsd和linux两个版本的代码实际上完全相同,而与solaris版本区别较大,具体表现在以下几个方面。
“fork”的使用策略
在文件UNIXProcess_md.c的注释中,说明了对”fork”的使用,以及最终在linux上使用vfork,Unix系统使用fork的决定(clone方法的相关代码仍然保留)。
fork(2)
可移植性较好,但会由于overcommit发生错误。
vfork()
有重大问题(参见man手册),但好在glibc已将该函数记录在案,并且XPG4已将其标准化。另一个好处是,vfork实际上是一个独立的系统调用,这说明linux会一直支持下去。
clone()
调用该函数时使用`CLONE_VM`选项,而非`CLONE_THREAD`选项,由于glibc中包含了对`CLONE_VM`和`CLONE_THREAD`相关组合的处理,所以在其他系统上也应该可以工作,但实际上并不是这样。
代码中相关宏的设置:
#define START_CHILD_USE_CLONE 0 /* clone() currently disabled; see above. */
#ifndef START_CHILD_USE_CLONE
#ifdef __linux__
#define START_CHILD_USE_CLONE 1
#else
#define START_CHILD_USE_CLONE 0
#endif
#endif
/* By default, use vfork() on Linux. */
#ifndef START_CHILD_USE_VFORK
#ifdef __linux__
#define START_CHILD_USE_VFORK 1
#else
#define START_CHILD_USE_VFORK 0
#endif
#endif
具体使用实在startChild方法中,根据宏的设置,调用vfork
,fork
或者clone
函数。
默认”PATH”的设置
代码说明一切
/**
* If PATH is not defined, the OS provides some default value.
* Unfortunately, there's no portable way to get this value.
* Fortunately, it's only needed if the child has PATH while we do not.
*/
static const char*
defaultPath(void)
{
#ifdef __solaris__
/* These really are the Solaris defaults! */
return (geteuid() == 0 || getuid() == 0) ?
"/usr/xpg4/bin:/usr/ccs/bin:/usr/bin:/opt/SUNWspro/bin:/usr/sbin" :
"/usr/xpg4/bin:/usr/ccs/bin:/usr/bin:/opt/SUNWspro/bin:";
#else
return ":/bin:/usr/bin"; /* glibc */
#endif
}
这是急眼了么?
/* These really are the Solaris defaults! */
waitForProcessExit方法对子进程返回值的处理
/* The child exited because of a signal.
* The best value to return is 0x80 + signal number,
* because that is what all Unix shells do, and because
* it allows callers to distinguish between process exit and
* process death by signal.
* Unfortunately, the historical behavior on Solaris is to return
* the signal number, and we preserve this for compatibility. */
#ifdef __solaris__
return WTERMSIG(status);
#else
return 0x80 + WTERMSIG(status);
#endif