这里写自定义目录标题
- jvm重定义的一些功能函数
- jvm启动后参数处理
- classpth参数处理过程详解
- JLI_WildcardExpandClasspath解析
- FileList_split解析
- FileList_expandWildcards解析
- FileList_join解析
jvm重定义的一些功能函数
#define JLI_StrLen(p1) strlen((p1))
#define JLI_StrChr(p1, p2) strchr((p1), (p2))
#define JLI_StrRChr(p1, p2) strrchr((p1), (p2))
#define JLI_StrCmp(p1, p2) strcmp((p1), (p2))
#define JLI_StrNCmp(p1, p2, p3) strncmp((p1), (p2), (p3))
#define JLI_StrCat(p1, p2) strcat((p1), (p2))
#define JLI_StrCpy(p1, p2) strcpy((p1), (p2))
#define JLI_StrNCpy(p1, p2, p3) strncpy((p1), (p2), (p3))
#define JLI_StrStr(p1, p2) strstr((p1), (p2))
#define JLI_StrSpn(p1, p2) strspn((p1), (p2))
#define JLI_StrCSpn(p1, p2) strcspn((p1), (p2))
#define JLI_StrPBrk(p1, p2) strpbrk((p1), (p2))
#define JLI_StrTok(p1, p2) strtok((p1), (p2))
jvm启动后参数处理
在launcher/java.c中我们可以在
int
JLI_Launch(int argc, char ** argv, /* main argc, argc */
int jargc, const char** jargv, /* java args */
int appclassc, const char** appclassv, /* app classpath */
const char* fullversion, /* full version defined */
const char* dotversion, /* dot version defined */
const char* pname, /* program name */
const char* lname, /* launcher name */
jboolean javaargs, /* JAVA_ARGS */
jboolean cpwildcard, /* classpath wildcard*/
jboolean javaw, /* windows-only javaw */
jint ergo /* ergonomics class policy */
)
这个函数中看到java源码中对参数的操作
/* Parse command line options; if the return value of
* ParseArguments is false, the program should exit.
*/
if (!ParseArguments(&argc, &argv, &mode, &what, &ret, jrepath))
{
return(ret);
}
其中argc是我们在输入参数的数量,argv是我们的参数
/*
* Parses command line arguments. Returns JNI_FALSE if launcher
* should exit without starting vm, returns JNI_TRUE if vm needs
* to be started to process given options. *pret (the launcher
* process return value) is set to 0 for a normal exit.
*/
static jboolean
ParseArguments(int *pargc, char ***pargv,
int *pmode, char **pwhat,
int *pret, const char *jrepath)
{
int argc = *pargc;
char **argv = *pargv;
int mode = LM_UNKNOWN;
char *arg;
*pret = 0;
while ((arg = *argv) != 0 && *arg == '-') {
argv++; --argc;
if (JLI_StrCmp(arg, "-classpath") == 0 || JLI_StrCmp(arg, "-cp") == 0) {
ARG_CHECK (argc, ARG_ERROR1, arg);
SetClassPath(*argv);
mode = LM_CLASS;
argv++; --argc;
} else if (JLI_StrCmp(arg, "-jar") == 0) {
ARG_CHECK (argc, ARG_ERROR2, arg);
mode = LM_JAR;
} else if (JLI_StrCmp(arg, "-help") == 0 ||
JLI_StrCmp(arg, "-h") == 0 ||
JLI_StrCmp(arg, "-?") == 0) {
printUsage = JNI_TRUE;
return JNI_TRUE;
} else if (JLI_StrCmp(arg, "-version") == 0) {
printVersion = JNI_TRUE;
return JNI_TRUE;
} else if (JLI_StrCmp(arg, "-showversion") == 0) {
showVersion = JNI_TRUE;
} else if (JLI_StrCmp(arg, "-X") == 0) {
printXUsage = JNI_TRUE;
return JNI_TRUE;
/*
* The following case checks for -XshowSettings OR -XshowSetting:SUBOPT.
* In the latter case, any SUBOPT value not recognized will default to "all"
*/
} else if (JLI_StrCmp(arg, "-XshowSettings") == 0 ||
JLI_StrCCmp(arg, "-XshowSettings:") == 0) {
showSettings = arg;
} else if (JLI_StrCmp(arg, "-Xdiag") == 0) {
AddOption("-Dsun.java.launcher.diag=true", NULL);
/*
* The following case provide backward compatibility with old-style
* command line options.
*/
} else if (JLI_StrCmp(arg, "-fullversion") == 0) {
JLI_ReportMessage("%s full version \"%s\"", _launcher_name, GetFullVersion());
return JNI_FALSE;
} else if (JLI_StrCmp(arg, "-verbosegc") == 0) {
AddOption("-verbose:gc", NULL);
} else if (JLI_StrCmp(arg, "-t") == 0) {
AddOption("-Xt", NULL);
} else if (JLI_StrCmp(arg, "-tm") == 0) {
AddOption("-Xtm", NULL);
} else if (JLI_StrCmp(arg, "-debug") == 0) {
AddOption("-Xdebug", NULL);
} else if (JLI_StrCmp(arg, "-noclassgc") == 0) {
AddOption("-Xnoclassgc", NULL);
} else if (JLI_StrCmp(arg, "-Xfuture") == 0) {
AddOption("-Xverify:all", NULL);
} else if (JLI_StrCmp(arg, "-verify") == 0) {
AddOption("-Xverify:all", NULL);
} else if (JLI_StrCmp(arg, "-verifyremote") == 0) {
AddOption("-Xverify:remote", NULL);
} else if (JLI_StrCmp(arg, "-noverify") == 0) {
AddOption("-Xverify:none", NULL);
} else if (JLI_StrCCmp(arg, "-prof") == 0) {
char *p = arg + 5;
char *tmp = JLI_MemAlloc(JLI_StrLen(arg) + 50);
if (*p) {
sprintf(tmp, "-Xrunhprof:cpu=old,file=%s", p + 1);
} else {
sprintf(tmp, "-Xrunhprof:cpu=old,file=java.prof");
}
AddOption(tmp, NULL);
} else if (JLI_StrCCmp(arg, "-ss") == 0 ||
JLI_StrCCmp(arg, "-oss") == 0 ||
JLI_StrCCmp(arg, "-ms") == 0 ||
JLI_StrCCmp(arg, "-mx") == 0) {
char *tmp = JLI_MemAlloc(JLI_StrLen(arg) + 6);
sprintf(tmp, "-X%s", arg + 1); /* skip '-' */
AddOption(tmp, NULL);
} else if (JLI_StrCmp(arg, "-checksource") == 0 ||
JLI_StrCmp(arg, "-cs") == 0 ||
JLI_StrCmp(arg, "-noasyncgc") == 0) {
/* No longer supported */
JLI_ReportErrorMessage(ARG_WARN, arg);
} else if (JLI_StrCCmp(arg, "-version:") == 0 ||
JLI_StrCmp(arg, "-no-jre-restrict-search") == 0 ||
JLI_StrCmp(arg, "-jre-restrict-search") == 0 ||
JLI_StrCCmp(arg, "-splash:") == 0) {
; /* Ignore machine independent options already handled */
} else if (ProcessPlatformOption(arg)) {
; /* Processing of platform dependent options */
} else if (RemovableOption(arg)) {
; /* Do not pass option to vm. */
} else {
AddOption(arg, NULL);
}
}
if (--argc >= 0) {
*pwhat = *argv++;
}
if (*pwhat == NULL) {
*pret = 1;
} else if (mode == LM_UNKNOWN) {
/* default to LM_CLASS if -jar and -cp option are
* not specified */
mode = LM_CLASS;
}
if (argc >= 0) {
*pargc = argc;
*pargv = argv;
}
*pmode = mode;
return JNI_TRUE;
}
在这个函数中我们可以看到函数对每一个arg的映射和处理,其中第一个函数if (JLI_StrCmp(arg, "-classpath") == 0 || JLI_StrCmp(arg, "-cp") == 0) { ARG_CHECK (argc, ARG_ERROR1, arg); SetClassPath(*argv); mode = LM_CLASS; argv++; --argc;
就是对-classpath或者-cp的处理,首先先使用JLI_StrCmp(p1, p2)
函数匹配-cp和-classpath,在jli_util.h文件中我们可以查看到对该函数的定义#define JLI_StrCmp(p1, p2) strcmp((p1), (p2))
,该函数就是strcmp的重定义,熟悉C语言的应该对该函数有所了解,匹配到-cp或-classpath之后就是获取他们的参数,然后调用ARG_CHECK()对参数进行审查,最后使用SetClassPath(*argv)对classpath下的class进行处理。
classpth参数处理过程详解
static void
SetClassPath(const char *s)
{
char *def;
const char *orig = s;
static const char format[] = "-Djava.class.path=%s";
/*
* usually we should not get a null pointer, but there are cases where
* we might just get one, in which case we simply ignore it, and let the
* caller deal with it
*/
if (s == NULL)
return;
s = JLI_WildcardExpandClasspath(s);
if (sizeof(format) - 2 + JLI_StrLen(s) < JLI_StrLen(s))
// s is corrupted after wildcard expansion
return;
def = JLI_MemAlloc(sizeof(format)
- 2 /* strlen("%s") */
+ JLI_StrLen(s));
sprintf(def, format, s);
AddOption(def, NULL);
if (s != orig)
JLI_MemFree((char *) s);
}
如果输入的classpath为空那么就直接返回,如果参数不为空就调用const char * JLI_WildcardExpandClasspath(const char *classpath)
函数对classpath进行处理,首先判断是否包含classpath,如果不包含就直接返回,如果包含就进行解析。JLI_WildcardExpandClasspath
通过对地址进行解析将里面的通配符进行拆解,最后将生成的地址传入到jvm中为后续的运行提供路径。
JLI_WildcardExpandClasspath解析
const char *
JLI_WildcardExpandClasspath(const char *classpath)
{
char *expanded;
FileList fl;
if (JLI_StrChr(classpath, '*') == NULL)
return classpath;
fl = FileList_split(classpath, PATH_SEPARATOR);
FileList_expandWildcards(fl);
expanded = FileList_join(fl, PATH_SEPARATOR);
FileList_free(fl);
if (getenv(JLDEBUG_ENV_ENTRY) != 0)
printf("Expanded wildcards:\n"
" before: \"%s\"\n"
" after : \"%s\"\n",
classpath, expanded);
return expanded;
}
FileList_split解析
在代码中我们可以看到一个FileList_的结构,这个结构定义了一个动态文件名列表
/*
* FileList ADT - a dynamic list of C filenames
*/
struct FileList_
{
char **files;
int size;
int capacity;
};
typedef struct FileList_ *FileList;
classpath一般都是几个地址的集合,在不同的系统中切分的方式不同,PATH_SEPARATOR代表不同系统中不同的切割符号,FileList_split()函数将地址按照指定的符号进行切割,生成一个动态文件名称列表。然后对列表中包含通配符的地址进行扩展。
static FileList
FileList_split(const char *path, char sep)
{
const char *p, *q;
int len = (int)JLI_StrLen(path);
int count;
FileList fl;
for (count = 1, p = path; p < path + len; p++)
count += (*p == sep);
fl = FileList_new(count);
for (p = path;;) {
for (q = p; q <= path + len; q++) {
if (*q == sep || *q == '\0') {
FileList_addSubstring(fl, p, q - p);
if (*q == '\0')
return fl;
p = q + 1;
}
}
}
}
在该函数中,首先先计算path的长度存储在len变量中,通过遍历path寻找sep的数量保存在count中,count表示classpath表示的地址的数量,然后根据count的数值对FileList进行初始化。
static FileList
FileList_new(int capacity)
{
FileList fl = NEW_(FileList);
fl->capacity = capacity;
fl->files = (char **) JLI_MemAlloc(capacity * sizeof(fl->files[0]));
fl->size = 0;
return fl;
}
初始化调用了一个NEW_函数,NEW_使用了预编译期粘连的方式,使得NEW_函数可以被复用。
#define NEW_(TYPE) ((TYPE) JLI_MemAlloc(sizeof(struct TYPE##_)))
在分配空间空将fl的每个值分别进行初始化然后返回初始化后的FileList。
上面定义了p和q指针,p指针在读取path的时候递增知道path末尾,q从末尾开始读起,每次读取到切分符后将path进行切割,并保存在FileList中。其中切割和保存的方法在FileList_addSubstring()方法中。
static void
FileList_addSubstring(FileList fl, const char *beg, int len)
{
char *filename = (char *) JLI_MemAlloc(len+1);
memcpy(filename, beg, len);
filename[len] = '\0';
FileList_ensureCapacity(fl, fl->size+1);
fl->files[fl->size++] = filename;
}
static void
FileList_ensureCapacity(FileList fl, int capacity)
{
if (fl->capacity < capacity) {
while (fl->capacity < capacity)
fl->capacity *= 2;
fl->files = JLI_MemRealloc(fl->files,
fl->capacity * sizeof(fl->files[0]));
}
}
FileList_expandWildcards解析
将classpath切分之后生成的地址可能还会包含通配符“*”,要把通配符拆开才是真正的classpath地址。
static void
FileList_expandWildcards(FileList fl)
{
int i, j;
for (i = 0; i < fl->size; i++) {
if (isWildcard(fl->files[i])) {
FileList expanded = wildcardFileList(fl->files[i]);
if (expanded != NULL && expanded->size > 0) {
JLI_MemFree(fl->files[i]);
FileList_ensureCapacity(fl, fl->size + expanded->size);
for (j = fl->size - 1; j >= i+1; j--)
fl->files[j+expanded->size-1] = fl->files[j];
for (j = 0; j < expanded->size; j++)
fl->files[i+j] = expanded->files[j];
i += expanded->size - 1;
fl->size += expanded->size - 1;
/* fl expropriates expanded's elements. */
expanded->size = 0;
}
FileList_free(expanded);
}
}
}
static int
isWildcard(const char *filename)
{
int len = (int)JLI_StrLen(filename);
return (len > 0) &&
(filename[len - 1] == '*') &&
(len == 1 || IS_FILE_SEPARATOR(filename[len - 2])) &&
(! exists(filename));
}
static int
exists(const char* filename)
{
#ifdef _WIN32
return _access(filename, 0) == 0;
#else
return access(filename, F_OK) == 0;
#endif
}
static FileList
wildcardFileList(const char *wildcard)
{
const char *basename;
FileList fl = FileList_new(16);
WildcardIterator it = WildcardIterator_for(wildcard);
if (it == NULL)
{
FileList_free(fl);
return NULL;
}
while ((basename = WildcardIterator_next(it)) != NULL)
if (isJarFileName(basename))
FileList_add(fl, wildcardConcat(wildcard, basename));
WildcardIterator_close(it);
return fl;
}
struct WildcardIterator_
{
HANDLE handle;
char *firstFile; /* Stupid FindFirstFile...FindNextFile */
};
typedef struct WildcardIterator_* WildcardIterator;
static WildcardIterator
WildcardIterator_for(const char *wildcard)
{
WildcardIterator it = NEW_(WildcardIterator);
HANDLE handle = FindFirstFile(wildcard, &find_data);
if (handle == INVALID_HANDLE_VALUE) {
JLI_MemFree(it);
return NULL;
}
it->handle = handle;
it->firstFile = find_data.cFileName;
return it;
}
static char *
wildcardConcat(const char *wildcard, const char *basename)
{
int wildlen = (int)JLI_StrLen(wildcard);
int baselen = (int)JLI_StrLen(basename);
char *filename = (char *) JLI_MemAlloc(wildlen + baselen);
/* Replace the trailing '*' with basename */
memcpy(filename, wildcard, wildlen-1);
memcpy(filename+wildlen-1, basename, baselen+1);
return filename;
}
isWildcard(const char *filename)
用于判断该path是否长度大于0,是否最后一个字母是“”,是否倒数第二个字母是切割符,同时调用exists函数判断目录是否存在和权限,全为真的话返回非0的数。如果返回结果为是则交由wildcardFileList
来进行处理。首先先初始化一个FileList并且设置初始大小为12,之后便是基于地址来查找base地址下的所有文件的名字添加到FileList中,如果其中包含JAR文件则用“”来取代basename。
FileList_join解析
static char *
FileList_join(FileList fl, char sep)
{
int i;
int size;
char *path;
char *p;
for (i = 0, size = 1; i < fl->size; i++)
size += (int)JLI_StrLen(fl->files[i]) + 1;
path = JLI_MemAlloc(size);
for (i = 0, p = path; i < fl->size; i++) {
int len = (int)JLI_StrLen(fl->files[i]);
if (i > 0) *p++ = sep;
memcpy(p, fl->files[i], len);
p += len;
}
*p = '\0';
return path;
}
该函数主要是吧FileList展开成path+path切割符的形式,返回一个path给母函数。