欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 宝鼎售后问题提交 | 后台管理


新闻资讯

MENU

软件开发知识
原文出处: hengyunabc

Jdk9后加载lib/modules的方法

从jdk的代码里可以看出来,默认的实现加载lib/modules是用mmap来加载的。

class NativeImageBuffer {
    static {
        java.security.AccessController.doPrivileged(
                new java.security.PrivilegedAction<Void>() {
                    public Void run() {
                        System.loadLibrary("jimage");
                        return null;
                    }
                });
    }

    native static ByteBuffer getNativeMap(String imagePath);
}

在jimage动态库里最终是一个cpp实现的ImageFileReader来读取的。它在64位os上利用的是mmap方法:

https://github.com/dmlloyd/openjdk/blob/jdk/jdk10/src/java.base/share/native/libjimage/imageFile.cpp#L44

启动多个jvm时会有长处:

  • 淘汰内存占用
  • 加速启动速度
  • 溘然有个想法,劳务派遣管理系统,怎么验证多个jvm简直共享了内存?

    下面来验证一下,思路是:

    1. 先获取历程的mmap信息
    2. 获取jvm历程映射modules的虚拟地点
    3. 从虚拟地点转换为物理地点
    4. 启动两个jvm历程,计较它们映射modules是否物理地点是一样的

    linux下查察历程的mmap信息

    1. 利用pmap -x $pid呼吁
    2. 直接查察 cat /proc/$pid/maps文件的内容

    启动一个jshell之后,用pmap查察mmap信息,个中RSS(resident set size)列暗示真实占用的内存。:

    $ pmap -x 24615
    24615:   jdk9/jdk-9.0.4/bin/jshell
    Address           Kbytes     RSS   Dirty Mode  Mapping
    0000000000400000       4       4       0 r-x-- jshell
    0000000000601000       4       4       4 rw--- jshell
    000000000111b000     132     120     120 rw---   [ anon ]
    ...
    00007f764192c000      88      64       0 r-x-- libnet.so
    00007f7641942000    2048       0       0 ----- libnet.so
    00007f7641b42000       4       4       4 rw--- libnet.so
    00007f7641b43000    2496     588     588 rwx--   [ anon ]
    ...
    00007f7650b43000  185076    9880       0 r--s- modules
    00007f765c000000    5172    5124    5124 rw---   [ anon ]
    
    ---------------- ------- ------- -------
    total kB         2554068  128756  106560

    我们可以找到modules文件的信息:

    00007f7650b43000  185076    9880       0 r--s- modules

    它的文件映射巨细是185076kb,实际利用内存巨细是9880kb。

    linux kernel关于pagemap的说明

    上面我们获取到了modules的虚拟地点,可是还需要转换为物理地点。

    正常来说一个历程是没有步伐知道它本身的虚拟地点对应的是什么物理地点。不外我们用linux kernel提供的信息可以读取,转换为物理地点。

    linux每个历程都有个/proc/$pid/pagemap文件,内里记录了内存页的信息:

    https://www.kernel.org/doc/Documentation/vm/pagemap.txt

    简而言之,在pagemap里每一个virtual page都有一个对应的64 bit的信息:

        * Bits 0-54  page frame number (PFN) if present
        * Bits 0-4   swap type if swapped
        * Bits 5-54  swap offset if swapped
        * Bit  55    pte is soft-dirty (see Documentation/vm/soft-dirty.txt)
        * Bit  56    page exclusively mapped (since 4.2)
        * Bits 57-60 zero
        * Bit  61    page is file-page or shared-anon (since 3.5)
        * Bit  62    page swapped
        * Bit  63    page present

    只要把虚拟地点转换为pagemap文件里的offset,就可以读取详细的virtual page信息。计较要领是:

    // getpagesize()是系统挪用
    // 64bit是8字节
    long virtualPageIndex = virtualAddress / getpagesize()
    offset = virtualPageIndex * 8

    从offset里读取出来的64bit里,可以获取到page frame number,假如想要获得真正的物理地点,还需要再转换:

    // pageFrameNumber * getpagesize() 获取page的开始地点
    // virtualAddress % getpagesize() 获取到page里的偏移地点
    long pageFrameNumber = // read from pagemap file
    physicalAddress = pageFrameNumber * getpagesize() + virtualAddress % getpagesize();

    虚拟地点转换物理地点的代码

    参考这里的代码:https://github.com/cirosantilli/linux-kernel-module-cheat/blob/master/kernel_module/user/common.h

    获得的一个从虚拟地点转换为物理地点的代码:

    #define _POSIX_C_SOURCE 200809L
    #include <fcntl.h> /* open */
    #include <stdint.h> /* uint64_t  */
    #include <stdlib.h> /* size_t */
    #include <unistd.h> /* pread, sysconf */
    
    int BUFSIZ = 1024;
    
    typedef struct {
        uint64_t pfn : 54;
        unsigned int soft_dirty : 1;
        unsigned int file_page : 1;
        unsigned int swapped : 1;
        unsigned int present : 1;
    } PagemapEntry;
    
    /* Parse the pagemap entry for the given virtual address.
     *
     * @param[out] entry      the parsed entry
     * @param[in]  pagemap_fd file descriptor to an open /proc/pid/pagemap file
     * @param[in]  vaddr      virtual address to get entry for
     * @return 0 for success, 1 for failure
     */
    int pagemap_get_entry(PagemapEntry *entry, int pagemap_fd, uintptr_t vaddr)
    {
        size_t nread;
        ssize_t ret;
        uint64_t data;
    
        nread = 0;
        while (nread < sizeof(data)) {
            ret = pread(pagemap_fd, &data, sizeof(data),
                    (vaddr / sysconf(_SC_PAGE_SIZE)) * sizeof(data) + nread);
            nread += ret;
            if (ret <= 0) {
                return 1;
            }
        }
        entry->pfn = data & (((uint64_t)1 << 54) - 1);
        entry->soft_dirty = (data >> 54) & 1;
        entry->file_page = (data >> 61) & 1;
        entry->swapped = (data >> 62) & 1;
        entry->present = (data >> 63) & 1;
        return 0;
    }
    
    /* Convert the given virtual address to physical using /proc/PID/pagemap.
     *
     * @param[out] paddr physical address
     * @param[in]  pid   process to convert for
     * @param[in] vaddr virtual address to get entry for
     * @return 0 for success, 1 for failure
     */
    int virt_to_phys_user(uintptr_t *paddr, pid_t pid, uintptr_t vaddr)
    {
        char pagemap_file[BUFSIZ];
        int pagemap_fd;
    
        snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%ju/pagemap", (uintmax_t)pid);
        pagemap_fd = open(pagemap_file, O_RDONLY);
        if (pagemap_fd < 0) {
            return 1;
        }
        PagemapEntry entry;
        if (pagemap_get_entry(&entry, pagemap_fd, vaddr)) {
            return 1;
        }
        close(pagemap_fd);
        *paddr = (entry.pfn * sysconf(_SC_PAGE_SIZE)) + (vaddr % sysconf(_SC_PAGE_SIZE));
        return 0;
    }
    
    int main(int argc, char ** argv){
        char *end;
    
        int pid;
        uintptr_t virt_addr;
        uintptr_t paddr;
        int return_code;
    
        pid = strtol(argv[1],&end, 10);
        virt_addr = strtol(argv[2], NULL, 16);
    
        return_code = virt_to_phys_user(&paddr, pid, virt_addr);
    
        if(return_code == 0)
            printf("Vaddr: 0x%lx, paddr: 0x%lx \n", virt_addr, paddr);
        else
            printf("error\n");
    }