create_vm 之4 os::init() (runtime_os模块)

前言

os 属于runtime模块下的os子模块;

os 定义了操作系统的接口,这包括传统的操作系统服务(如时间、I/O),以及其他与系统相关的功能,其中可能包含系统特定的代码,里面定义的方法大部分都是静态方法,根据所在系统的不同,会有不同的实现。

os

openjdk-jdk8u/hotspot/src/share/vm/runtime/os.hpp

openjdk-jdk8u/hotspot/src/share/vm/runtime/os.cpp

linux下特殊实现 os_linux

openjdk-jdk8u/hotspot/src/os/linux/vm/os_linux.hpp

openjdk-jdk8u/hotspot/src/os/linux/vm/os_linux.cpp

os::init()  os/linux下实现

包括如下一些步骤:

  • 设置内存页大小
  • 初始化系统信息(处理器、物理内存等)
  • 初始化操作系统信息
  • 获取main线程的句柄
  • 初始化系统时钟
  • ...
void os::init(void) {
    char dummy;   /* used to get a guess on initial stack address */
    
    // 在 LinuxThreads 环境下,JavaMain 线程的 pid(原始线程)
    // 与 Java 启动器线程的 pid 不同。
    // 因此,在 Linux 系统上,启动器线程的 pid 通过 sun.java.launcher.pid 属性传递给虚拟机。
    // 如果该属性正确传递,则使用此属性而不是 getpid()。
    // 参见 Bug 6351349。
    
    // LinuxThreads 是 Linux 上早期实现 POSIX 线程(pthread)的一个线程库,用于在多核处理器上运行并发程序。
    // 它是 Linux 系统中最早的一种多线程编程模型,但由于其设计局限性,后来被 NPTL(Native POSIX Thread Library)取代

    pid_t java_launcher_pid = (pid_t) Arguments::sun_java_launcher_pid();

    _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());
    
    // 获取并设置cpu核数; 获取并设置系统总物理内存数 
    Linux::initialize_system_info();
    
    // _main_thread 指向当前线程,也就是main线程
    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_REALTIME: 使用系统的实时时钟来计算等待时间。
    // CLOCK_MONOTONIC: 使用单调时钟来计算等待时间,不受系统时间调整的影响    

    if (Linux::supports_monotonic_clock()) {
        // 设置线程条件变量_condatt的时钟类型
        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)));
            }
        }
    }
    // else it defaults to CLOCK_REALTIME
    
    // 初始化互斥变量,作为后面互斥锁使用
    pthread_mutex_init(&dl_mutex, NULL);
    
    // 如果page size 大于8k,就要调整虚拟机栈保护页大小,
    // 这里我们暂时只考虑page_size 4K的情况,所以这里忽略掉
    if (vm_page_size() > (int)Linux::vm_default_page_size()) {// const int os::Linux::_vm_default_page_size = (8 * K);
        StackYellowPages = 1;
        StackRedPages = 1;
        StackShadowPages = round_to((StackShadowPages*Linux::vm_default_page_size()), vm_page_size()) / vm_page_size();
    }
    
    // dlsym 在之前从 dlopen() 装入的模块导出的符号中,查找指定符号

    // pthread_setname_np: 该函数用来设置线程名,默认创建的线程名都是程序名 
    Linux::_pthread_setname_np = (int(*)(pthread_t, const char*))dlsym(RTLD_DEFAULT, "pthread_setname_np");

}

 RTLD_DEFAULTdlsym 函数的一个特殊标志,它告诉 dlsym 在整个全局符号表中查找符号,而不仅仅是从指定的动态库中。它通常用于查找那些已经加载的全局符号。

当你想查找一个全局的符号,而不关心它来自哪个动态库时,使用 RTLD_DEFAULT 作为 dlsym 的句柄。

Linux::initialize_system_info()

hotspot/src/os/linux/vm/os_linux.cpp
// 在大多数 Linux 版本中,通过查看 /proc 文件系统来确定处理器数量存在一个 bug。
// 在 chroot 环境中,该系统调用返回 1。这会导致虚拟机表现得好像只有一个处理器,并省略锁定操作(参见is_MP() 调用)
void os::Linux::initialize_system_info() {
    // 获取并设置cpu核数
    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");
}

附录 

库函数 dlsym 说明

  • 动态链接dlsym 用于获取已加载动态库中的符号地址,通常配合 dlopendlclose 一起使用,来动态加载和卸载共享库。

  • 返回指针dlsym 返回指定符号的地址,调用者需要将其转换为相应类型的指针,以便调用该函数或访问变量。

  • 错误处理:如果找不到指定的符号,dlsym 返回 NULL,可以使用 dlerror() 获取详细的错误信息。

    #include <dlfcn.h>
    #include <stdio.h>
    
    int main() {
        void *handle = dlopen("libm.so", RTLD_LAZY);
        if (!handle) {
            fprintf(stderr, "%s\n", dlerror());
            return 1;
        }
    
        double (*cosine)(double) = dlsym(handle, "cos");
        if (!cosine) {
            fprintf(stderr, "%s\n", dlerror());
            return 1;
        }
    
        printf("%f\n", cosine(2.0));
        dlclose(handle);
        return 0;
    }
    

查看物理CPU核数

# 查看物理CPU个数

cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l

查看每个物理CPU中core的个数

# 查看每个物理CPU中core的个数(即核数)

cat /proc/cpuinfo| grep "cpu cores"| uniq

proc

proc 是 Processes(进程) 的缩写,/proc 是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,这个目录是一个虚拟的目录,它是系统内存的映射,我们可以通过直接访问这个目录来获取系统信息。

这个目录的内容不在硬盘上而是在内存里,我们也可以直接修改里面的某些文件,比如可以通过下面的命令来屏蔽主机的ping命令,使别人无法ping你的机器:

可以通过修改 /proc/sys/net/ipv4/icmp_echo_ignore_all 文件来屏蔽主机的 ping 响应。具体的操作命令如下:

# 这条命令会使你的主机忽略所有 ICMP 回显请求(ping)。
# 屏蔽 ping 响应
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all



# 如果需要恢复,让主机重新响应 ping 请求,可以执行
# 恢复 ping 响应
echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_all

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FTC9527

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值