这里写自定义目录标题

  • 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给母函数。