Magenta源代码笔记(3) —— 内存管理【转】
转自:http://blog.csdn.net/boymax2/article/details/52550197
- 版权声明:本文为博主原创文章,未经博主允许不得转载。
- Magenta内核支持虚拟地址的配置,依赖于cpu内的mmu模块。
- 下面会从以下几个方面对Magenta内核内存管理方面的代码进行分析:
- 、mmu初始化,也就是硬件mmu的初始化,以底层寄存器操作为主,汇编
- 、pmm初始化,也就是代码中物理内存结构的初始化
- 、vmm初始化,也就是代码中虚拟内存结构的初始化
- mmu初始化
- mmu初始化的代码由汇编完成,其中主要涉及了以下几个结构
- TLB:内存映射表,其定义位于c代码中
- kernel/arch/arm/arm/mmu.c
- [cpp] view plain copy
- uint32_t arm_kernel_translation_table[TT_ENTRY_COUNT] __ALIGNED() __SECTION(".bss.prebss.translation_table");
- 以及初始化的内存映射关系,以qemu-virt平台
- kernel/platform/qemu-virt/platform.c
- [cpp] view plain copy
- struct mmu_initial_mapping mmu_initial_mappings[] = {
- /* all of memory */
- {
- .phys = MEMORY_BASE_PHYS, // 内存物理基地址
- .virt = KERNEL_BASE, // 内存虚拟基地址
- .size = MEMORY_APERTURE_SIZE,// 虚拟内存大小
- .flags = ,
- .name = "memory"
- },
- /* 1GB of peripherals */
- {
- .phys = PERIPHERAL_BASE_PHYS, // 外设物理基地址
- .virt = PERIPHERAL_BASE_VIRT, // 外设虚拟基地址
- .size = PERIPHERAL_BASE_SIZE, // 虚拟内存大小
- .flags = MMU_INITIAL_MAPPING_FLAG_DEVICE,
- .name = "peripherals"
- },
- /* null entry to terminate the list */
- { }
- };
- 这两个结构都会在之后的汇编代码中使用。
- mmu初始化的汇编代码位于内核的启动文件中,以arm32为例
- 自己对arm汇编不是很熟悉,在读汇编代码时花费了比较多的时间,希望有错误能指正出来
- 启动文件中与mmu相关的代码已经提取出来
- 在其中主要涉及到的操作为以下几个:
- 、重置mmu相关寄存器
- 、计算物理地址相对虚拟地址的偏移
- 、将tlb地址指向空间清零
- 、遍历mmu_initial_mappings结构,计算后写入tlb
- 、设置mmu相关寄存器
- 、跳转至c代码
- kernel/arch/arm/arm/start.S
- [plain] view plain copy
- #include <asm.h>
- #include <arch/arm/cores.h>
- #include <arch/arm/mmu.h>
- #include <kernel/vm.h>
- .section ".text.boot"
- .globl _start
- _start:
- b platform_reset
- b arm_undefined
- b arm_syscall
- b arm_prefetch_abort
- b arm_data_abort
- b arm_reserved
- b arm_irq
- b arm_fiq
- #if WITH_SMP
- b arm_reset
- #endif
- .weak platform_reset
- platform_reset:
- /* Fall through for the weak symbol */
- // arm复位处理程序
- .globl arm_reset
- arm_reset:
- /* do some early cpu setup */
- // 读SCTLR寄存器,手册P1711
- mrc p15, , r12, c1, c0,
- /* i/d cache disable, mmu disabled */
- // cache位与mmu位置0
- bic r12, #(<<)
- bic r12, #(<< | <<)
- #if WITH_KERNEL_VM
- /* enable caches so atomics and spinlocks work */
- // cache位与mmu位置1
- orr r12, r12, #(<<)
- orr r12, r12, #(<<)
- #endif // WITH_KERNEL_VM
- // 写SCTLR寄存器
- mcr p15, , r12, c1, c0,
- /* calculate the physical offset from our eventual virtual location */
- // 计算物理地址相对虚拟地址的偏移,用于之后的转换
- .Lphys_offset:
- ldr r4, =.Lphys_offset
- adr r11, .Lphys_offset
- sub r11, r11, r4
- ...
- #if ARM_WITH_MMU
- .Lsetup_mmu:
- /* set up the mmu according to mmu_initial_mappings */
- /* load the base of the translation table and clear the table */
- // 获取转换表地址
- ldr r4, =arm_kernel_translation_table
- // 获取转换表物理地址
- add r4, r4, r11
- /* r4 = physical address of translation table */
- mov r5, #
- mov r6, #
- /* walk through all the entries in the translation table, setting them up */
- // 遍历转换表结构清零
- :
- str r5, [r4, r6, lsl #]
- add r6, #
- cmp r6, #
- bne 0b
- /* load the address of the mmu_initial_mappings table and start processing */
- // 获取初始映射地址
- ldr r5, =mmu_initial_mappings
- // 获取初始映射物理地址
- add r5, r5, r11
- /* r5 = physical address of mmu initial mapping table */
- // 初始映射遍历绑定至转换表
- // 转换表的绑定 转换表中元素的高12位为物理基地址下标,低20位为mmu相关flag
- .Linitial_mapping_loop:
- // 把结构体加载到各个通用寄存器中
- ldmia r5!, { r6-r10 }
- /* r6 = phys, r7 = virt, r8 = size, r9 = flags, r10 = name */
- /* round size up to 1MB alignment */
- // 上调size对齐1MB
- ubfx r10, r6, #, #
- add r8, r8, r10
- add r8, r8, #( << )
- sub r8, r8, #
- /* mask all the addresses and sizes to 1MB boundaries */
- // 物理地址 虚拟地址 大小 右移20位 取高12位
- lsr r6, # /* r6 = physical address / 1MB */
- lsr r7, # /* r7 = virtual address / 1MB */
- lsr r8, # /* r8 = size in 1MB chunks */
- /* if size == 0, end of list */
- // 循环边界判断
- cmp r8, #
- beq .Linitial_mapping_done
- /* set up the flags */
- // 设置mmu相关flag,放置在r10
- ldr r10, =MMU_KERNEL_L1_PTE_FLAGS
- teq r9, #MMU_INITIAL_MAPPING_FLAG_UNCACHED
- ldreq r10, =MMU_INITIAL_MAP_STRONGLY_ORDERED
- beq 0f
- teq r9, #MMU_INITIAL_MAPPING_FLAG_DEVICE
- ldreq r10, =MMU_INITIAL_MAP_DEVICE
- /* r10 = mmu entry flags */
- :
- // 计算translation_table元素的值
- // r10:mmu相关flag r6:物理地址高12位
- // r12 = r10 | (r6 << 20)
- // 高20位为物理地址,低12位为mmu相关flag
- orr r12, r10, r6, lsl #
- /* r12 = phys addr | flags */
- /* store into appropriate translation table entry */
- // r4:转换表物理基地址 r7:虚拟地址对应的section
- // r12 -> [r4 + r7 << 2]
- str r12, [r4, r7, lsl #]
- /* loop until we're done */
- // 准备下一个转换表元素的填充
- add r6, #
- add r7, #
- subs r8, #
- bne 0b
- b .Linitial_mapping_loop
- .Linitial_mapping_done:
- ...
- /* set up the mmu */
- bl .Lmmu_setup
- #endif // WITH_KERNEL_VM
- ...
- // 跳转至c程序
- bl lk_main
- b .
- #if WITH_KERNEL_VM
- /* per cpu mmu setup, shared between primary and secondary cpus
- args:
- r4 == translation table physical
- r8 == final translation table physical (if using trampoline)
- */
- // 设置mmu相关寄存器
- // r4:转换表物理基地址
- // mmu相关寄存器 手册P1724
- .Lmmu_setup:
- /* Invalidate TLB */
- mov r12, #
- mcr p15, , r12, c8, c7,
- isb
- /* Write 0 to TTBCR */
- // ttbcr写0
- mcr p15, , r12, c2, c0,
- isb
- /* Set cacheable attributes on translation walk */
- // 宏MMU_TTBRx_FLAGS为 (1 << 3) | (1 << 6)
- orr r12, r4, #MMU_TTBRx_FLAGS
- /* Write ttbr with phys addr of the translation table */
- // 写入ttbr0
- mcr p15, , r12, c2, c0,
- isb
- /* Write DACR */
- // 写DACR cache相关
- mov r12, #0x1
- mcr p15, , r12, c3, c0,
- isb
- /* Read SCTLR into r12 */
- // 读SCTLR寄存器,手册P1711
- mrc p15, , r12, c1, c0,
- /* Disable TRE/AFE */
- // 禁用TRE和AFE标志位
- bic r12, #(<< | <<)
- /* Turn on the MMU */
- // MMU使能标志位
- orr r12, #0x1
- /* Write back SCTLR */
- // 写入SCTLR
- // MMU打开
- mcr p15, , r12, c1, c0,
- isb
- /* Jump to virtual code address */
- // 跳转
- ldr pc, =1f
- :
- ...
- /* Invalidate TLB */
- mov r12, #
- mcr p15, , r12, c8, c7,
- isb
- /* assume lr was in physical memory, adjust it before returning */
- // 计算跳转点的虚拟地址,跳转,之后会调用lk_main
- sub lr, r11
- bx lr
- #endif
- ...
- 硬件层的内存管理相关的初始化基本完成后,会跳转到c代码
- 位于kernel/top/main.c
- 其中有关内存管理的函数调用顺序为:
- 、pmm_add_arena 将物理内存加入pmm结构
- 、vm_init_preheap 堆初始化前的准备工作(钩子)
- 、heap_init 堆的初始化
- 、vm_init_postheap 堆初始化后的工作(钩子)
- 、arm_mmu_init mmu相关的调整
- 首先要完成pmm初始化工作
- pmm初始化主要分为以下几步:
- 、通过fdt库从bootloader中获取物理内存的长度
- 、在pmm中加入物理内存
- 、标记fdt结构的空间
- 、标记bootloader相关的空间
- pmm中比较重要的一个结构体,pmm_arena_t代表着一块物理内存的抽象
- kernel/include/kernel/vm.h
- [cpp] view plain copy
- typedef struct pmm_arena {
- struct list_node node; // 节点,物理内存链表
- const char* name; // 名称
- uint flags;
- uint priority;
- paddr_t base; // 物理内存基地址
- size_t size; // 物理内存长度
- size_t free_count; // 空闲的页数
- struct vm_page* page_array; // 页结构数组
- struct list_node free_list; // 节点,该内存中空闲空间的链表
- } pmm_arena_t;
- 接着以qemu-virt的platform为例,分析pmm初始化的过程
- kernel/platform/qemu-virt.c
- [cpp] view plain copy
- // 全局物理内存结构体
- static pmm_arena_t arena = {
- .name = "ram",
- .base = MEMORY_BASE_PHYS,
- .size = DEFAULT_MEMORY_SIZE,
- .flags = PMM_ARENA_FLAG_KMAP,
- };
- ...
- // 该函数为平台的早期初始化,在内核启动时调用
- void platform_early_init(void)
- {
- ...
- /* look for a flattened device tree just before the kernel */
- // 获取fdt结构
- const void *fdt = (void *)KERNEL_BASE;
- int err = fdt_check_header(fdt);
- if (err >= ) {
- /* walk the nodes, looking for 'memory' and 'chosen' */
- int depth = ;
- int offset = ;
- for (;;) {
- offset = fdt_next_node(fdt, offset, &depth);
- if (offset < )
- break;
- /* get the name */
- const char *name = fdt_get_name(fdt, offset, NULL);
- if (!name)
- continue;
- /* look for the properties we care about */
- // 从fdt中查找到内存信息
- if (strcmp(name, "memory") == ) {
- int lenp;
- const void *prop_ptr = fdt_getprop(fdt, offset, "reg", &lenp);
- if (prop_ptr && lenp == 0x10) {
- /* we're looking at a memory descriptor */
- //uint64_t base = fdt64_to_cpu(*(uint64_t *)prop_ptr);
- // 获取内存长度
- uint64_t len = fdt64_to_cpu(*((const uint64_t *)prop_ptr + ));
- /* trim size on certain platforms */
- #if ARCH_ARM
- // 如果是32位arm,只使用内存前1GB
- if (len > **1024U) {
- len = **; /* only use the first 1GB on ARM32 */
- printf("trimming memory to 1GB\n");
- }
- #endif
- /* set the size in the pmm arena */
- // 保存内存长度
- arena.size = len;
- }
- } else if (strcmp(name, "chosen") == ) {
- ...
- }
- }
- }
- /* add the main memory arena */
- // 将改内存区域加入到pmm中
- pmm_add_arena(&arena);
- /* reserve the first 64k of ram, which should be holding the fdt */
- // 标记fdt区域
- pmm_alloc_range(MEMBASE, 0x10000 / PAGE_SIZE, NULL);
- // 标记bootloader_ramdisk区域
- platform_preserve_ramdisk();
- ...
- }
- 内核在接下来初始化堆之前会在内存中构造出出一个VmAspace对象,其代表的是内核空间的抽象
- kernel/kernel/vm/vm.cpp
- [cpp] view plain copy
- void vm_init_preheap(uint level) {
- LTRACE_ENTRY;
- // allow the vmm a shot at initializing some of its data structures
- // 构造代表内核空间的VmAspace对象
- VmAspace::KernelAspaceInitPreHeap();
- // mark all of the kernel pages in use
- LTRACEF("marking all kernel pages as used\n");
- // 标记内核代码所用内存
- mark_pages_in_use((vaddr_t)&_start, ((uintptr_t)&_end - (uintptr_t)&_start));
- // mark the physical pages used by the boot time allocator
- // 标记boot time allocator代码所用内存
- if (boot_alloc_end != boot_alloc_start) {
- LTRACEF("marking boot alloc used from 0x%lx to 0x%lx\n", boot_alloc_start, boot_alloc_end);
- mark_pages_in_use(boot_alloc_start, boot_alloc_end - boot_alloc_start);
- }
- }
- kernel/kernel/vm/vm_aspace.cpp
- [cpp] view plain copy
- void VmAspace::KernelAspaceInitPreHeap() {
- // the singleton kernel address space
- // 构造一个内核空间单例,因为这个函数只会在启动时调用,所以是这个对象是单例
- static VmAspace _kernel_aspace(KERNEL_ASPACE_BASE, KERNEL_ASPACE_SIZE, VmAspace::TYPE_KERNEL,
- "kernel");
- // 初始化
- auto err = _kernel_aspace.Init();
- ASSERT(err >= );
- // save a pointer to the singleton kernel address space
- // 保存单例指针
- VmAspace::kernel_aspace_ = &_kernel_aspace;
- }
- VmAspace::VmAspace(vaddr_t base, size_t size, uint32_t flags, const char* name)
- : base_(base), size_(size), flags_(flags) {
- DEBUG_ASSERT(size != );
- DEBUG_ASSERT(base + size - >= base);
- Rename(name);
- LTRACEF("%p '%s'\n", this, name_);
- }
- status_t VmAspace::Init() {
- DEBUG_ASSERT(magic_ == MAGIC);
- LTRACEF("%p '%s'\n", this, name_);
- // intialize the architectually specific part
- // 标记为内核的空间
- bool is_high_kernel = (flags_ & TYPE_MASK) == TYPE_KERNEL;
- uint arch_aspace_flags = is_high_kernel ? ARCH_ASPACE_FLAG_KERNEL : ;
- // 调用mmu相关的函数
- return arch_mmu_init_aspace(&arch_aspace_, base_, size_, arch_aspace_flags);
- }
- kernel/arch/arm/arm/mmu.c
- [cpp] view plain copy
- status_t arch_mmu_init_aspace(arch_aspace_t *aspace, vaddr_t base, size_t size, uint flags)
- {
- LTRACEF("aspace %p, base 0x%lx, size 0x%zx, flags 0x%x\n", aspace, base, size, flags);
- DEBUG_ASSERT(aspace);
- DEBUG_ASSERT(aspace->magic != ARCH_ASPACE_MAGIC);
- /* validate that the base + size is sane and doesn't wrap */
- DEBUG_ASSERT(size > PAGE_SIZE);
- DEBUG_ASSERT(base + size - > base);
- // 初始化内核空间中页的链表
- list_initialize(&aspace->pt_page_list);
- aspace->magic = ARCH_ASPACE_MAGIC;
- if (flags & ARCH_ASPACE_FLAG_KERNEL) {
- // 设置结构内相关参数,其中转换表的物理内存通过vaddr_to_paddr获取
- // 该函数不详细分析了,实质就是通过转换表进行查询得到的物理地址
- aspace->base = base;
- aspace->size = size;
- aspace->tt_virt = arm_kernel_translation_table;
- aspace->tt_phys = vaddr_to_paddr(aspace->tt_virt);
- } else {
- ...
- }
- LTRACEF("tt_phys 0x%lx tt_virt %p\n", aspace->tt_phys, aspace->tt_virt);
- return NO_ERROR;
- }
- 到此内核空间的结构初始化完成
- 接下来进行内核堆的初始化,Magenta内核中提供了两种堆的实现miniheap以及cmpctmalloc,用户可以自己进行配置。
- 堆的具体实现方法会在之后进行具体的分析
- 堆的初始化完成以后,会调用相应的钩子函数,该函数的主要的作用如下:
- 、在vmm结构中标记内核已使用的虚拟地址
- 、根据内核使用的地址的区域,分别设置内存的保护
- [cpp] view plain copy
- void vm_init_postheap(uint level) {
- LTRACE_ENTRY;
- vmm_aspace_t* aspace = vmm_get_kernel_aspace();
- // we expect the kernel to be in a temporary mapping, define permanent
- // regions for those now
- struct temp_region {
- const char* name;
- vaddr_t base;
- size_t size;
- uint arch_mmu_flags;
- } regions[] = {
- {
- .name = "kernel_code",
- .base = (vaddr_t)&__code_start,
- .size = ROUNDUP((size_t)&__code_end - (size_t)&__code_start, PAGE_SIZE),
- .arch_mmu_flags = ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_EXECUTE,
- },
- {
- .name = "kernel_rodata",
- .base = (vaddr_t)&__rodata_start,
- .size = ROUNDUP((size_t)&__rodata_end - (size_t)&__rodata_start, PAGE_SIZE),
- .arch_mmu_flags = ARCH_MMU_FLAG_PERM_READ,
- },
- {
- .name = "kernel_data",
- .base = (vaddr_t)&__data_start,
- .size = ROUNDUP((size_t)&__data_end - (size_t)&__data_start, PAGE_SIZE),
- .arch_mmu_flags = ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE,
- },
- {
- .name = "kernel_bss",
- .base = (vaddr_t)&__bss_start,
- .size = ROUNDUP((size_t)&__bss_end - (size_t)&__bss_start, PAGE_SIZE),
- .arch_mmu_flags = ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE,
- },
- {
- .name = "kernel_bootalloc",
- .base = (vaddr_t)boot_alloc_start,
- .size = ROUNDUP(boot_alloc_end - boot_alloc_start, PAGE_SIZE),
- .arch_mmu_flags = ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE,
- },
- };
- for (uint i = ; i < countof(regions); ++i) {
- temp_region* region = ®ions[i];
- ASSERT(IS_PAGE_ALIGNED(region->base));
- status_t status = vmm_reserve_space(aspace, region->name, region->size, region->base);
- ASSERT(status == NO_ERROR);
- status = vmm_protect_region(aspace, region->base, region->arch_mmu_flags);
- ASSERT(status == NO_ERROR);
- }
- // mmu_initial_mappings should reflect where we are now, use it to construct the actual
- // mappings. We will carve out the kernel code/data from any mappings and
- // unmap any temporary ones.
- const struct mmu_initial_mapping* map = mmu_initial_mappings;
- for (map = mmu_initial_mappings; map->size > ; ++map) {
- LTRACEF("looking at mapping %p (%s)\n", map, map->name);
- // Unmap temporary mappings except where they intersect with the
- // kernel code/data regions.
- vaddr_t vaddr = map->virt;
- LTRACEF("vaddr 0x%lx, virt + size 0x%lx\n", vaddr, map->virt + map->size);
- while (vaddr != map->virt + map->size) {
- vaddr_t next_kernel_region = map->virt + map->size;
- vaddr_t next_kernel_region_end = map->virt + map->size;
- // Find the kernel code/data region with the lowest start address
- // that is within this mapping.
- for (uint i = ; i < countof(regions); ++i) {
- temp_region* region = ®ions[i];
- if (region->base >= vaddr && region->base < map->virt + map->size &&
- region->base < next_kernel_region) {
- next_kernel_region = region->base;
- next_kernel_region_end = region->base + region->size;
- }
- }
- // If vaddr isn't the start of a kernel code/data region, then we should make
- // a mapping between it and the next closest one.
- if (next_kernel_region != vaddr) {
- status_t status =
- vmm_reserve_space(aspace, map->name, next_kernel_region - vaddr, vaddr);
- ASSERT(status == NO_ERROR);
- if (map->flags & MMU_INITIAL_MAPPING_TEMPORARY) {
- // If the region is part of a temporary mapping, immediately unmap it
- LTRACEF("Freeing region [%016lx, %016lx)\n", vaddr, next_kernel_region);
- status = vmm_free_region(aspace, vaddr);
- ASSERT(status == NO_ERROR);
- } else {
- // Otherwise, mark it no-exec since it's not explicitly code
- status = vmm_protect_region(
- aspace,
- vaddr,
- ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE);
- ASSERT(status == NO_ERROR);
- }
- }
- vaddr = next_kernel_region_end;
- }
- }
- }
- 以上代码中涉及到的几个函数,只是做下简单的介绍,不具体分析:
- vmm_reserve_space:在vmm中标记一块虚拟内存,这块虚拟内存抽象为VmRegion类,拥有自己的底层mmu相关的配置
- vmm_protect_region:对某VmRegion对应的虚拟内存设置内存保护的相关参数
- mmu相关的调整
- mmu相关的调整,由内核新建的bootstrap2线程进行调用arch_init完成
- kernel/arch/arm/arm/arch.c
- [cpp] view plain copy
- void arch_init(void)
- {
- ...
- #if ARM_WITH_MMU
- /* finish intializing the mmu */
- arm_mmu_init();
- #endif
- }
- kernel/arch/arm/arm/mmu.c
- [cpp] view plain copy
- void arm_mmu_init(void)
- {
- /* unmap the initial mapings that are marked temporary */
- // 解除具有MMU_INITIAL_MAPPING_TEMPORARY标志的内存映射
- struct mmu_initial_mapping *map = mmu_initial_mappings;
- while (map->size > ) {
- if (map->flags & MMU_INITIAL_MAPPING_TEMPORARY) {
- vaddr_t va = map->virt;
- size_t size = map->size;
- DEBUG_ASSERT(IS_SECTION_ALIGNED(size));
- while (size > ) {
- arm_mmu_unmap_l1_entry(arm_kernel_translation_table, va / SECTION_SIZE);
- va += MB;
- size -= MB;
- }
- }
- map++;
- }
- arm_after_invalidate_tlb_barrier();
- #if KERNEL_ASPACE_BASE != 0
- /* bounce the ttbr over to ttbr1 and leave 0 unmapped */
- // 重新设置mmu相关的寄存器,禁用ttbcr0,将原先ttbr0的映射移动到ttbr1
- // ttbr1为内核空间使用的寄存器
- uint32_t n = __builtin_clz(KERNEL_ASPACE_BASE) + ;
- DEBUG_ASSERT(n <= );
- uint32_t ttbcr = (<<) | n; /* disable TTBCR0 and set the split between TTBR0 and TTBR1 */
- arm_write_ttbr1(arm_read_ttbr0());
- ISB;
- arm_write_ttbcr(ttbcr);
- ISB;
- arm_write_ttbr0();
- ISB;
- #endif
- }
- 至此Magenta内核有关内存管理的初始化完成。
Magenta源代码笔记(3) —— 内存管理【转】的更多相关文章
- linux kernel学习笔记-5内存管理_转
void * kmalloc(size_t size, gfp_t gfp_mask); kmalloc()第一个参数是要分配的块的大小,第一个参数为分配标志,用于控制kmalloc()的行为. km ...
- XV6学习笔记(2) :内存管理
XV6学习笔记(2) :内存管理 在学习笔记1中,完成了对于pc启动和加载的过程.目前已经可以开始在c语言代码中运行了,而当前已经开启了分页模式,不过是两个4mb的大的内存页,而没有开启小的内存页.接 ...
- redis 源代码分析(一) 内存管理
一,redis内存管理介绍 redis是一个基于内存的key-value的数据库,其内存管理是很重要的,为了屏蔽不同平台之间的差异,以及统计内存占用量等,redis对内存分配函数进行了一层封装,程序中 ...
- redis源代码解读之内存管理————zmalloc文件
本文章主要记录本人在看redis源代码的一些理解和想法.由于功力有限,肯定会出现故障,所以.希望高手给出指正. 第一篇就是内存相关的介绍.由于我喜欢先看一些组件的东西,再看总体的流程. 先上一下代码吧 ...
- Cocos2D-X2.2.3学习笔记3(内存管理)
本章节介绍例如以下: 1.C/C++内存管理机制 2.引用计数机制 3.自己主动释放机制 1.C/C++内存管理机制 相信仅仅要懂oop的都知道NEW这个keyword吧,这个通俗点说事实上就是创建对 ...
- Linux内核设计笔记12——内存管理
内存管理学习笔记 页 页是内核管理内存的基本单位,内存管理单元(MMU,管理内存并把虚拟地址转化为物理地址的硬件)通常以页为单位进行处理,从虚拟内存的角度看,页就是最小单位. struct page{ ...
- COCOS学习笔记--Cocod2dx内存管理(三)-Coco2d-x内存执行原理
通过上两篇博客.我们对Cocos引用计数和Ref类.PoolManager类以及AutoreleasePool类已有所了解,那么接下来就通过举栗子来进一步看看Coco2d-x内存执行原理是如何的. / ...
- cocos2d-x 源代码分析 : Ref (CCObject) 源代码分析 cocos2d-x内存管理策略
从源代码版本号3.x.转载请注明 cocos2d-x 总的文件夹的源代码分析: http://blog.csdn.net/u011225840/article/details/31743129 1.R ...
- 《代码的未来》读书笔记:内存管理与GC那点事儿
一.内存是有限的 近年来,我们的电脑内存都有好几个GB,也许你的电脑是4G,他的电脑是8G,公司服务器内存是32G或者64G.但是,无论内存容量有多大,总归不是无限的.实际上,随着内存容量的增加,软件 ...
随机推荐
- Iframe父子间元素操作
1.在父页面 获取iframe子页面的元素 (在同域的情况下 且在http://下测试,且最好在iframe onload加载完毕后 dosomething...) js写法 a.通过contentW ...
- HashMap原理以及自己实现HashMap
1.HashMap是什么? HashMap是java常用来存储键值对的数据结构,它是以key/value的形式存储的,它不是线程安全的,Key可以为null值. 2.HashMap的实现原理 Hash ...
- docker安装后无法启动问题
问题报错: Error starting daemon: Error initializing network controller: list bridge addresses failed: no ...
- 精读《Epitath 源码 - renderProps 新用法》
1 引言 很高兴这一期的话题是由 epitath 的作者 grsabreu 提供的. 前端发展了 20 多年,随着发展中国家越来越多的互联网从业者涌入,现在前端知识玲琅满足,概念.库也越来越多.虽然内 ...
- JavaScript对象创建的九种方式
1.标准创建对象模式 var person = new Object(); person.name = "Nicholas"; person.age = 29; person.jo ...
- javaWeb开发中常见的问题
1.修改表单提交的时候不好使可能是因为没写对应隐藏域的ID 2.el表达式在js代码中要加“”,例如 "${}" 3.JavaScript中的函数也有重载的特性.如果两个input ...
- Java集合---简介
概念 集合可以理解为一个动态的对象数组,不同的是集合中的对象内容可以任意扩充.Java最基本的集合接口:Collection接口 集合的特点 性能高 容易扩展和修改 Collection的常用子类 L ...
- loj2063 「HAOI2016」字符合并
ref #include <iostream> #include <cstring> #include <cstdio> using namespace std; ...
- 【Restore IP Addresses 】cpp
题目: Given a string containing only digits, restore it by returning all possible valid IP address com ...
- Bugku杂项-convert
一进去就发现一堆二进制数,然后考虑怎么才能把这个和隐写扯上关系.首先,二进制我们肉眼就是看不懂再说什么的,这里就想到了转换,再联想上hex将原始数据转化为16进制.我们可以先把2进制转化为16进制,然 ...