Hotspot源码解析-第六章

发布时间:2023年12月26日

第六章

本章主要讲解一下创建Java虚拟前,对所需要的操作系统本身信息的初始化操作。

6.1 OS初始化

6.1.1 os_linux.cpp

6.1.1.1 os::init
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");

}

6.1.1.2 os::Linux::initialize_system_info
// 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");
}

6.2 系统属性初始化

6.2.1 arguments.cpp

6.2.1.1 init_system_properties

这个函数就是初始化系统要用的各种属性,并封装成一个个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();
}

文章来源:https://blog.csdn.net/zhang527294844/article/details/135226049
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。