本章主要讲解一下创建Java虚拟前,对所需要的操作系统本身信息的初始化操作。
void os::init(void) {
char dummy; /* used to get a guess on initial stack address */
/*
* 执行JavaMain的线程的pid不同于java launcher线程的pid。在linux中,java launcher线程pid可以通过VM的 sun.java.launcher.pid属性设置,否则就通过系统调用getpid()获取进程id
*/
// 获取参数 -Dsun.java.launcher.pid= 设置的值
pid_t java_launcher_pid = (pid_t) Arguments::sun_java_launcher_pid();
// java_launcher_pid 没有设置值,那就取当前进程id
_initial_pid = (java_launcher_pid > 0) ? java_launcher_pid : getpid();
// 系统调用sysconf获取属性_SC_CLK_TCK的值,这个值表示时钟每秒的滴答数
clock_tics_per_sec = sysconf(_SC_CLK_TCK);
// 设置随机数的种子 1234567
init_random(1234567);
// 钩子函数,目前没有具体实现
ThreadCritical::initialize();
// 系统调用sysconf获取内存页大小,并设置,供后面使用
Linux::set_page_size(sysconf(_SC_PAGESIZE));
if (Linux::page_size() == -1) {
fatal(err_msg("os_linux.cpp: os::init: sysconf failed (%s)",
strerror(errno)));
}
// 设置到 _page_sizes 数组中
init_page_sizes((size_t) Linux::page_size());
Linux::initialize_system_info();
// _main_thread 指向当前线程,也就是创建/加载 JVM的线程,pthread_self 也是一个系统调用api
Linux::_main_thread = pthread_self();
// 时钟处理初始化
Linux::clock_init();
// 记录时间
initial_time_count = javaTimeNanos();
// pthread_condattr 初始化时钟属性,时钟属性就是控制条件变量超时时采用的哪个时钟
int status;
pthread_condattr_t* _condattr = os::Linux::condAttr();
if ((status = pthread_condattr_init(_condattr)) != 0) {
fatal(err_msg("pthread_condattr_init: %s", strerror(status)));
}
// 如果 CLOCK_MONOTONIC 是可用的,就把条件变量的时钟属性设置成 CLOCK_MONOTONIC
if (Linux::supports_monotonic_clock()) {
if ((status = pthread_condattr_setclock(_condattr, CLOCK_MONOTONIC)) != 0) {
if (status == EINVAL) {
warning("Unable to use monotonic clock with relative timed-waits" \
" - changes to the time-of-day clock may have adverse affects");
} else {
fatal(err_msg("pthread_condattr_setclock: %s", strerror(status)));
}
}
}
// 初始化互斥变量,作为后面互斥锁使用
pthread_mutex_init(&dl_mutex, NULL);
/*
* const int os::Linux::_vm_default_page_size = (8 * K); os_linux.cpp源码中有对
* _vm_default_page_size 初始化为 8k
* 如果page size 大于8k,就要启用保护页,正常page szie就是4k,不会大于8k,所以这一段暂且不考虑
*/
if (vm_page_size() > (int)Linux::vm_default_page_size()) {
StackYellowPages = 1;
StackRedPages = 1;
StackShadowPages = round_to((StackShadowPages*Linux::vm_default_page_size()), vm_page_size()) / vm_page_size();
}
// 动态链接解析系统调用函数 pthread_setname_np 并用Linux::_pthread_setname_np指针指向该函数,该函数用来设置线程名,默认创建的线程名都是程序名
Linux::_pthread_setname_np =
(int(*)(pthread_t, const char*))dlsym(RTLD_DEFAULT, "pthread_setname_np");
}
// Most versions of linux have a bug where the number of processors are
// determined by looking at the /proc file system. In a chroot environment,
// the system call returns 1. This causes the VM to act as if it is
// a single processor and elide locking (see is_MP() call).
// 原文注释翻译:大部分的linux都有一个错误,即处理器数量通过查看/proc 文件系统来确定。在chroot环境中,系统调用返回是1,这就使的虚拟机看起来像是在单处理器中工作。
// 这一段描述,我暂时还没搞明白具体涵义,但是不影响源码分析,后续弄清楚后,我会补充说明
// 这个函数就做2件事:1.获取并设置cpu核数;2.获取并设置系统总物理内存数
void os::Linux::initialize_system_info() {
// 获取到系统的处理器总核数
set_processor_count(sysconf(_SC_NPROCESSORS_CONF));
if (processor_count() == 1) { // 下面这段操作就跟上面的注释有关,待后续弄清楚,但不影响整体读取进度
pid_t pid = os::Linux::gettid();
char fname[32];
jio_snprintf(fname, sizeof(fname), "/proc/%d", pid);
FILE *fp = fopen(fname, "r");
if (fp == NULL) {
unsafe_chroot_detected = true;
} else {
fclose(fp);
}
}
// 物理内存总数
_physical_memory = (julong)sysconf(_SC_PHYS_PAGES) * (julong)sysconf(_SC_PAGESIZE);
assert(processor_count() > 0, "linux error");
}
这个函数就是初始化系统要用的各种属性,并封装成一个个SystemProperty对象,形成一个链表存储起来。SystemProperty类继承自CHeapObj,当new一个该对象时,实际是在C堆里分配一个存放该对象的空间,这里的C堆指的是jvm层面,不是我们java的堆,关于内存空间的,后面会专门抽一个章节来讲。所以这个函数实际没什么看头
void Arguments::init_system_properties() {
// 每个属性都是一个key-value格式,想像成 java.vm.specification.name=Java Virtual Machine Specification
PropertyList_add(&_system_properties, new SystemProperty("java.vm.specification.name",
"Java Virtual Machine Specification", false));
PropertyList_add(&_system_properties, new SystemProperty("java.vm.version", VM_Version::vm_release(), false));
PropertyList_add(&_system_properties, new SystemProperty("java.vm.name", VM_Version::vm_name(), false));
PropertyList_add(&_system_properties, new SystemProperty("java.vm.info", VM_Version::vm_info_string(), true));
// following are JVMTI agent writeable properties.
// Properties values are set to NULL and they are
// os specific they are initialized in os::init_system_properties_values().
_java_ext_dirs = new SystemProperty("java.ext.dirs", NULL, true);
_java_endorsed_dirs = new SystemProperty("java.endorsed.dirs", NULL, true);
_sun_boot_library_path = new SystemProperty("sun.boot.library.path", NULL, true);
_java_library_path = new SystemProperty("java.library.path", NULL, true);
_java_home = new SystemProperty("java.home", NULL, true);
_sun_boot_class_path = new SystemProperty("sun.boot.class.path", NULL, true);
_java_class_path = new SystemProperty("java.class.path", "", true);
// Add to System Property list.
PropertyList_add(&_system_properties, _java_ext_dirs);
PropertyList_add(&_system_properties, _java_endorsed_dirs);
PropertyList_add(&_system_properties, _sun_boot_library_path);
PropertyList_add(&_system_properties, _java_library_path);
PropertyList_add(&_system_properties, _java_home);
PropertyList_add(&_system_properties, _java_class_path);
PropertyList_add(&_system_properties, _sun_boot_class_path);
// Set OS specific system properties values
os::init_system_properties_values();
}