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简直共享了内存?
下面来验证一下,思路是:
modules
的虚拟地点modules
是否物理地点是一样的linux下查察历程的mmap信息
pmap -x $pid
呼吁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"); }