linux内存管理:kmap、vmap、ioremap
目录
散列表也是哈希表
kmap实现
page_address_map
pkmap_count
page_address_slot
哈希函数
kmap函数实现
kmap_init
kmap
kmap_high
page_address
map_new_virtual
set_page_address
kunmap
kunmap_high(struct page *page)函数实现
vmap实现
vmap函数
vunmap函数
ioremap
ioremap函数
__arm_ioremap_pfn_caller函数
pfn_valid(pfn)
后记
说明:
- kernel版本:5.9.8
- 调试工具:gdb + qemu + kernel-O0
- 平台信息:ARM Vexpress ARMv7 4核 SMP
- 使用工具:vim + cscope + visio
- vmap()用于相对长时间的映射,可同时映射多个pages,
- kmap()和kmap_atomic()则用于相对短时间的映射,只能映射单个page.
- 而 ioremap 函数,其功能是将给定的物理地址映射为虚拟地址。
注意此处的物理地址并不是真正内存的物理地址,而是cpu上的io memory。
本篇文章重点从以下方面分析:
- 1、散列表
- 2、kmap
- 3、vmap
- 4、ioremap
散列表也是哈希表
散列表:根据给定的关键字来计算关键字在表中的地址的数据结构,也就说,散列表建立了关键字和存储地址之间的一种直接映射关系。
散列函数:一个把查找表中的关键字映射成该关键字对应的地址的函数,记为Hash(key)=Addr
冲突:散列函数可能会把两个或者两个以上不同关键字映射同一地址,称这种情况为冲突,这些发生碰撞的不同关键字称为同义词。
构造散列函数的tips:
- 1、散列函数的定义域必须包含全部需要存储的关键字,而值域的范围则依赖于散列表的大小或地址范围
- 2、散列函数计算出来的地址应该能等概率,均分布在整个地址空间,从而减少冲突的发生。
- 3、散列函数应该尽量简单,能够在较短的时间内就计算出任一关键字对应的散列地址
常用Hash函数的构造方法:
- 1、直接定址法
- 2、除留余数法
- 3、数字分析法
- 4、平方取中法
kmap实现
用于相对短时间的映射,只能映射单个page.
page_address_map
struct page_address_map { struct page *page;void *virtual;struct list_head list;
};
struct page_address_map:散列表中关键字(key=page)对应的结构,散列表中的结点
- page:管理一个高端的物理page
- virtual:page物理页面对应的虚拟地址
- list:链接到page_address_htable[i]对应lh的头部的结点
pkmap_count
#define PTRS_PER_PTE 512
#define LAST_PKMAP PTRS_PER_PTE
static int pkmap_count[LAST_PKMAP]
- 1、pkmap_count[i]=0为空闲的虚拟地址可以直接使用的
- 2、pkmap_count[i]=1已经被使用过且被释放了但是还在缓存中
- 3、pkmap_count[i]>=2正在使用中
- 4、pkmap_count[i]描述的虚拟地址为0xbfe00000+4k*i
来一张图描述pkmap_count和虚拟地址之间的对应关系
page_address_slot
static struct page_address_slot {struct list_head lh; /* List of page_address_maps */spinlock_t lock; /* Protect this bucket's list */
} ____cacheline_aligned_in_smp page_address_htable[1<<PA_HASH_ORDER];
- page_address_slot:散列表的描述结构,page_address_htable为散列表
- lh:链接哈希表中的成员结点,此哈希表通过链表解决冲突问题
哈希函数
#define hash_long(val, bits) hash_32(val, bits)
#define hash_32 hash_32_generic
#define __hash_32 __hash_32_generic
#define PA_HASH_ORDER 7
#define GOLDEN_RATIO_32 0x61C88647
static inline u32 __hash_32_generic(u32 val)
{return val * GOLDEN_RATIO_32;
}
static inline u32 hash_32_generic(u32 val, unsigned int bits)
{/* High bits are more random, so use them. */return __hash_32(val) >> (32 - bits);
}
static inline u32 hash_ptr(const void *ptr, unsigned int bits)
{return hash_long((unsigned long)ptr, bits);
}
static struct page_address_slot *page_slot(const struct page *page)
{return &page_address_htable[hash_ptr(page, PA_HASH_ORDER)];
}
哈希函数通过以上的各种转换之后的表达式为:
Hash(page)=page*GOLDEN_RATIO_32>>(32-PA_HASH_ORDER)
哈希函数的选择遵循构造散列函数的tips
再来一张图描述page_address_slot和page_address_map之间的关系
kmap函数实现
page如果是ZONE_NORMAL,则直接返回虚拟地址即可。
page如果是ZONE_HIGHMEM,则分以下情况:
- 1、如果page在page_address_htable中直接返回虚拟地址
- 2、如果page不在page_address_htable中,则分以下情况:
- 如果只有pkmap_count[0]为0,则会进行一次flush_all_zero_pkmaps(),然后直接退出,因为pkmap_count[0]为0,则直接返回pkmap_count[0]对应的虚拟地址。
- 如果有pkmap_count[i]的数组为0,i非0,i的下标最小,则直接拿到pkmap_count[i]对应的虚拟地址。
- 如果全部非0,则分以下情况
- 1、有可以的释放的,则通过flush_all_zero_pkmaps()释放,先判断pkmap_count[0]释放释放满足要求,如果不满足要求,然后再循环一次判断,发现有空闲的,则获取对应的虚拟地址。
- 2、如果没有可以释放的,即都是在使用的,则需要加入等待队列,直到有空闲的释放被唤醒。
kmap_init
static void __init kmap_init(void)
{
#ifdef CONFIG_HIGHMEMpkmap_page_table = early_pte_alloc(pmd_off_k(PKMAP_BASE),PKMAP_BASE, _PAGE_KERNEL_TABLE);
#endif
}
从上可以看出上面对[KMAP_BASE,PAGE_OFFSET]进行PGD页表的填充,页表的port为_PAGE_KERNEL_TABLE,虚拟地址为FIXADDR_START,early_pte_alloc通过early_alloc函数pte_t*pte = alloc (PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE)申请 512 * 4 + 512 *4其中硬件页表和软件各占一半,512硬件PTE表项,一个PTE表项对应4k,刚好2MB对应[KMAP_BASE,PAGE_OFFSET]大小,初始化完成之后,一级页表PGD表项由KMAP_BASE起始地址的PGD表项(PGD[0],PGD[1])指向对应pte的基地址。此后如果在[KMAP_BASE,PAGE_OFFSET]要映射对应的物理地址,只需要填充PTE表项即可。
kmap
static inline void *kmap(struct page *page)
{void *addr;might_sleep();if (!PageHighMem(page))addr = page_address(page);elseaddr = kmap_high(page);kmap_flush_tlb((unsigned long)addr);return addr;
}
1、判断page页面是否是ZONE_HIGHMEM,如果不是,则认为是ZONE_NORMAL,因为ZONE_NORMAL为低端ZONE,则为线性映射,直接获取虚拟addr通过lowmem_page_address函数
2、如果给是page指向ZONE_HIGHMEM的物理页面,需要kamp_high做物理地址和虚拟地址的映射
3、获取到映射完成的虚拟地址之后刷TLB后,返回虚拟地址
kmap_high
void *kmap_high(struct page *page)
{ unsigned long vaddr;lock_kmap();vaddr = (unsigned long)page_address(page);if (!vaddr)vaddr = map_new_virtual(page);pkmap_count[PKMAP_NR(vaddr)]++;BUG_ON(pkmap_count[PKMAP_NR(vaddr)] < 2);unlock_kmap();return (void*) vaddr;
}
- 1、已经确定page是ZONE_HIGHMEM,通过page_address函数的hash表找到对应的虚拟地址,如果虚拟地址返回为NULL,则重新进行映射
- 2、对pkmap_count[PKMAP_NR(vaddr)]++到2,说明此page对应虚拟地址已经被分配出去使用了
- 3、返回映射成功的虚拟地址。
接下来重点分析page_address函数
page_address
void *page_address(const struct page *page)
{unsigned long flags;void *ret;struct page_address_slot *pas;if (!PageHighMem(page))return lowmem_page_address(page);pas = page_slot(page);ret = NULL;spin_lock_irqsave(&pas->lock, flags);if (!list_empty(&pas->lh)) {struct page_address_map *pam;list_for_each_entry(pam, &pas->lh, list) {if (pam->page == page) {ret = pam->virtual;goto done;}}}
done:spin_unlock_irqrestore(&pas->lock, flags);return ret;
}
- 1、此page已经确定是ZONE_HIGHMEM,则直接通过page_slot(page)函数(哈希函数)获取到此page的储存器地址
- 2、因为page可能产生冲突,则需要通过lh链表头遍历此page是否在此链表中,如果存在则直接返回虚拟地址即可,说明之前此page已经建立好了映射,虚拟地址直接使用即可,如果未找到,则需要通过map_new_virtual(page)函数重新映射。
map_new_virtual
static inline unsigned long map_new_virtual(struct page *page)
{unsigned long vaddr;int count;unsigned int last_pkmap_nr;unsigned int color = get_pkmap_color(page);
start:count = get_pkmap_entries_count(color);/* Find an empty entry */for (;;) {last_pkmap_nr = get_next_pkmap_nr(color);if (no_more_pkmaps(last_pkmap_nr, color)) {flush_all_zero_pkmaps();count = get_pkmap_entries_count(color);}if (!pkmap_count[last_pkmap_nr])break; /* Found a usable entry */if (--count)continue;{DECLARE_WAITQUEUE(wait, current);wait_queue_head_t *pkmap_map_wait = get_pkmap_wait_queue_head(color);__set_current_state(TASK_UNINTERRUPTIBLE);add_wait_queue(pkmap_map_wait, &wait);unlock_kmap();schedule();remove_wait_queue(pkmap_map_wait, &wait);lock_kmap();/* Somebody else might have mapped it while we slept */if (page_address(page))return (unsigned long)page_address(page);/* Re-start */goto start;}}vaddr = PKMAP_ADDR(last_pkmap_nr);set_pte_at(&init_mm, vaddr,&(pkmap_page_table[last_pkmap_nr]), mk_pte(page, kmap_prot));pkmap_count[last_pkmap_nr] = 1;set_page_address(page, (void *)vaddr); return vaddr;
}
static inline int no_more_pkmaps(unsigned int pkmap_nr, unsigned int color)
{ return pkmap_nr == 0;
}
static inline unsigned int get_pkmap_color(struct page *page)
{return 0;
}
static inline int get_pkmap_entries_count(unsigned int color)
{return LAST_PKMAP;
}
static inline unsigned int get_next_pkmap_nr(unsigned int color)
{ static unsigned int last_pkmap_nr;last_pkmap_nr = (last_pkmap_nr + 1) & LAST_PKMAP_MASK;return last_pkmap_nr;
}
get_pkmap_color(page)函数仅在特定体系结构(mips和xtensa)中使用,并且由于未在ARM中使用而返回0
color=0,count=512,last_pkmap_nr=1开始循环遍历pkmap_count [last_pkmap_nr]是否为0,如果为0,说明此PTE还未填充或者PTE无效。现在可以拿来用填充,如果512个表项全为非0,即所有的PTE都映射了page,即已经经过kmap和kumap,或者在kunmap中,但是没有实际的解映射,只是pkmap_count[last_pkmap_nr]--。此page对应的物理地址和对应的虚拟地址通过hashtable存储。
count=512,last_pkmap_nr=1count=511,last_pkmap_nr=2…count=1,last_pkmap_nr=512
然后此时if(no_more_pkmaps(last_pkmap_nr, color))成立
- 1、先flush_cache_kmaps()
- 2、循环遍历pkmap_count[i],如果等于1,则说明page已经kumap了,把pkmap_count[i] = 0,然后通过pte_clear清空pte和物理地址的页表的映射关系
- 3、然后通过set_page_address(page, NULL)函数把page从hashtable删除,同时need_flush置1,然后延时刷TLB
然后再判断pkmap_count[0]是否满足要求,如果不满足再循环检测是否有空闲的pkmap_count[last_pkmap_nr]
count=512 last_pkmap_nr=0;count=511 last_pkmap_nr=1;count=510 last_pkmap_nr=2;count=1 last_pkmap_nr=511
如果count=0,还是无法找到空闲的pkmap_count[last_pkmap_nr]
则把先创建一个等待队列,然后把此任务加入到等待队列中,同时把此任务设置为不可屏蔽中断状态,等待唤醒,如果有其他任务释放了pkmap_count[last_pkmap_nr],即是把pkmap_count[last_pkmap_nr]设置为1了,会wakeup此任务,然后从等待队列中删除此任务,然后先尝试在哈希表中是否能找到对应的page,如果找不到,需要重新start即是上述流程。<为何需要先page_address,有可能同一个page在其他高优先级的进程kmap了也没拿到,但是等有空闲得到时候,高优先级任务可能优先拿到,占了一个pkmap_count,低优先级的任务也要占一个pkmap_count,导致同一个page占了两个pkmap_count>
- 4、通过last_pkmap_nr找到对应的虚拟地址,然后用通过set_pte_at函数填充PTE页表建立映射。
- 5、把pkmap_count[last_pkmap_nr] = 1表示此PTE已经被填充
- 6、通过set_page_address(page, (void *)vaddr)函数把page加入哈希表中
set_page_address
void set_page_address(struct page *page, void *virtual)
{unsigned long flags;struct page_address_slot *pas;struct page_address_map *pam;BUG_ON(!PageHighMem(page));pas = page_slot(page);if (virtual) { /* Add */pam = &page_address_maps[PKMAP_NR((unsigned long)virtual)];pam->page = page;pam->virtual = virtual;spin_lock_irqsave(&pas->lock, flags);list_add_tail(&pam->list, &pas->lh);spin_unlock_irqrestore(&pas->lock, flags);} else { /* Remove */spin_lock_irqsave(&pas->lock, flags);list_for_each_entry(pam, &pas->lh, list) {if (pam->page == page) {list_del(&pam->list);spin_unlock_irqrestore(&pas->lock, flags);goto done;}}spin_unlock_irqrestore(&pas->lock, flags);}
done:return;
}
- 1、通过page找到对应存储page_address_map的地址
- 2、如果虚拟地址非NULL,则说明是add,如果为空则为remove
- 3、如果是add的话即先通过虚拟地址找到对应的page_address_maps[PKMAP_NR((unsigned long)virtual)]然后更新page_address_map中的page和virtual,通过page_address_map的list添加到page_address_slot中的lh的头部中list_add_tail(&pam->list, &pas->lh);
- 4、如果是remove的话,找到对应存储地址,然后遍历page_address_slot中的page和当前page是否相等,如果相等就直接list_del
kunmap
释放过程
- 1、page如果是ZONE_NORMAL则直接返回
- 2、page如果是ZONE_HIGHMEM, pkmap_count[nr],然后判断等待队列是否为空,如果非空,则唤醒等待队列中的任务。
static inline void kunmap(struct page *page)
{might_sleep();if (!PageHighMem(page))return;kunmap_high(page);
}
- 1、如果是指向ZONE_NORMAL的page,则直接返回
- 2、如果是指向ZONE_HIGHMEM的page ,则调用 kunmap_high(page)
kunmap_high(struct page *page)函数实现
void kunmap_high(struct page *page)
{unsigned long vaddr;unsigned long nr; unsigned long flags;int need_wakeup;unsigned int color = get_pkmap_color(page);wait_queue_head_t *pkmap_map_wait;lock_kmap_any(flags);vaddr = (unsigned long)page_address(page);BUG_ON(!vaddr);nr = PKMAP_NR(vaddr);/** A count must never go down to zero* without a TLB flush!*/need_wakeup = 0;switch (--pkmap_count[nr]) {case 0:BUG();case 1:/** Avoid an unnecessary wake_up() function call.* The common case is pkmap_count[] == 1, but* no waiters.* The tasks queued in the wait-queue are guarded* by both the lock in the wait-queue-head and by* the kmap_lock. As the kmap_lock is held here,* no need for the wait-queue-head's lock. Simply* test if the queue is empty.*/pkmap_map_wait = get_pkmap_wait_queue_head(color);need_wakeup = waitqueue_active(pkmap_map_wait);}unlock_kmap_any(flags);/* do wake-up, if needed, race-free outside of the spin lock */ if (need_wakeup)wake_up(pkmap_map_wait);
}
- 1、通过get_pkmap_color(page)函数获取color=0,--pkmap_count[nr]表示释放
- 2、通过page找到vaddr地址,如果释放不存在的page则BUG_ON,然后通过PKMAP_NR(addr)找到数组下标nr
- 3、通过get_pkmap_wait_queue_head(color)获取等待队列的头,然后查看等待队列中是否有任务需要唤醒,如果有则把need_wakeup稍后去唤醒,如果空,则无需唤醒。
vmap实现
vmap()用于相对长时间的映射,可同时映射多个pages,
vmap函数
void *vmap(struct page **pages, unsigned int count,unsigned long flags, pgprot_t prot)
{struct vm_struct *area;unsigned long size; /* In bytes */might_sleep();if (count > totalram_pages())return NULL;size = (unsigned long)count << PAGE_SHIFT;area = get_vm_area_caller(size, flags, __builtin_return_address(0));if (!area)return NULL;if (map_kernel_range((unsigned long)area->addr, size, pgprot_nx(prot),pages) < 0) {vunmap(area->addr);return NULL;}return area->addr;
}
1、先在vmalloc区域获取size大小的虚拟地址
2、找到虚拟地址后通过map_kernel_range函数把虚拟地址和物理地址绑定映射,即是填充PGD和PTE页表
vunmap函数
void vunmap(const void *addr)
{ BUG_ON(in_interrupt());might_sleep();if (addr)__vunmap(addr, 0);
}
1、如果在中断上下文,直接BUG_ON
2、然后通过__vunmap函数释放,因为第二个参数为0,即不释放对应物理地址对应的物理page
ioremap
ioremap 函数,其功能是将给定的物理地址映射为虚拟地址。
ioremap函数
void __iomem *ioremap(resource_size_t res_cookie, size_t size)
{return arch_ioremap_caller(res_cookie, size, MT_DEVICE,__builtin_return_address(0));
}
void __iomem * (*arch_ioremap_caller)(phys_addr_t, size_t, unsigned int, void *) =__arm_ioremap_caller;
void __iomem *__arm_ioremap_caller(phys_addr_t phys_addr, size_t size, unsigned int mtype, void *caller)
{ phys_addr_t last_addr;unsigned long offset = phys_addr & ~PAGE_MASK;unsigned long pfn = __phys_to_pfn(phys_addr);/** Don't allow wraparound or zero size*/last_addr = phys_addr + size - 1;if (!size || last_addr < phys_addr)return NULL;return __arm_ioremap_pfn_caller(pfn, offset, size, mtype,caller);
}
- 1、phys_addr找到对应物理地址的页内偏移
- 2、由phys_addr找到物理页帧号
- 3、做些简单参数检查size非0及环绕
__arm_ioremap_pfn_caller函数
static void __iomem * __arm_ioremap_pfn_caller(unsigned long pfn,unsigned long offset, size_t size, unsigned int mtype, void *caller)
{const struct mem_type *type;int err;unsigned long addr;struct vm_struct *area;phys_addr_t paddr = __pfn_to_phys(pfn);
#ifndef CONFIG_ARM_LPAE/** High mappings must be supersection aligned*/if (pfn >= 0x100000 && (paddr & ~SUPERSECTION_MASK))return NULL;
#endiftype = get_mem_type(mtype);if (!type)return NULL;/** Page align the mapping size, taking account of any offset. */size = PAGE_ALIGN(offset + size);/** Try to reuse one of the static mapping whenever possible.*/if (size && !(sizeof(phys_addr_t) == 4 && pfn >= 0x100000)) {struct static_vm *svm;svm = find_static_vm_paddr(paddr, size, mtype);if (svm) {addr = (unsigned long)svm->vm.addr;addr += paddr - svm->vm.phys_addr;return (void __iomem *) (offset + addr);}}/** Don't allow RAM to be mapped with mismatched attributes - this* causes problems with ARMv6+*/if (WARN_ON(pfn_valid(pfn) && mtype != MT_MEMORY_RW))return NULL;area = get_vm_area_caller(size, VM_IOREMAP, caller);if (!area)return NULL;addr = (unsigned long)area->addr;area->phys_addr = paddr;err = ioremap_page_range(addr, addr + size, paddr,__pgprot(type->prot_pte));if (err) {vunmap((void *)addr);return NULL;}flush_cache_vmap(addr, addr + size);return (void __iomem *) (offset + addr);
}
- 1、由pfn获取物理地址
- 2、如果物理地址大于4G则需要16MB对齐
- 3、由get_mem_type(mtype)函数获取内存类型,设置页表权限
[MT_DEVICE] = { /* Strongly ordered / ARMv6 shareddevice */.prot_pte = PROT_PTE_DEVICE | L_PTE_MT_DEV_SHARED |L_PTE_SHARED,.prot_l1 = PMD_TYPE_TABLE,.prot_sect = PROT_SECT_DEVICE | PMD_SECT_S,.domain = DOMAIN_IO,},
- 4、如果页内偏移为0xff0,size为2,则只需要4k虚拟地址即可,如果size为0x10,则需要两4k大小的虚拟地址映射。
- 5、无static mapping暂不分析
- 6、mtype !=MT_MEMORY_RW成立,pfn_valid函数稍后重点分析
- 7、通过get_vm_area_caller函数获取虚拟地址
- 8、通过虚拟地址和物理地址及页表属性建立映射,填充PGD PTE页表。
- 9、flush_cache_vmap刷cachevmap
pfn_valid(pfn)
int pfn_valid(unsigned long pfn)
{phys_addr_t addr = __pfn_to_phys(pfn);if (__phys_to_pfn(addr) != pfn) return 0;return memblock_is_map_memory(addr);
}
bool __init_memblock memblock_is_map_memory(phys_addr_t addr)
{ int i = memblock_search(&memblock.memory, addr);if (i == -1)return false;return !memblock_is_nomap(&memblock.memory.regions[i]);
}
static int __init_memblock memblock_search(struct memblock_type *type, phys_addr_t addr)
{unsigned int left = 0, right = type->cnt;do {unsigned int mid = (right + left) / 2;if (addr < type->regions[mid].base)right = mid;else if (addr >= (type->regions[mid].base +type->regions[mid].size))left = mid + 1;elsereturn mid;} while (left < right);return -1;
}
static inline bool memblock_is_nomap(struct memblock_region *m)
{ return m->flags & MEMBLOCK_NOMAP;
}
- 1、由pfn找到物理地址
- 2、memblock_is_map_memory由此函数判断memory是否是map区域
如果在memblock.memory区域找到了,说明是memblock.memory区域,否则不是memory区域返回false=0,即WARN_ON不执行异常,如果是memblock.memory区域则需要判断此memblock是否是MEMBLOCK_NOMAP属性,如果是WARN_ON不执行异常,如果不是则触发异常警告。
void (*arch_iounmap)(volatile void __iomem *) = __iounmap;void iounmap(volatile void __iomem *cookie)
{arch_iounmap(cookie);
}
void __iounmap(volatile void __iomem *io_addr)
{void *addr = (void *)(PAGE_MASK & (unsigned long)io_addr);struct static_vm *svm;/* If this is a static mapping, we must leave it alone */svm = find_static_vm_vaddr(addr);if (svm)return;vunmap(addr);
}
- 1、无static_vm直接vunmap(addr)
- 2、如果在中断上下文,直接BUG_ON
- 3、然后通过__vunmap函数释放,因为第二个参数为0,即不释放对应物理地址对应的物理page
后记
本文重点分析了kmap vmap ioreamp的实现,其中vmap,ioremap中的部分函数在vmalloc的实现中已经分析过。对于kmap vmap ioremap的差异对比和使用场景这里暂未仔细做分析,因为如果你知道它的实现细节了,它的使用注意事项也就了然于胸了。从对内存管理的刚开始的懵懂到现在有种面朝大海春暖花开的感觉。
linux内存管理:kmap、vmap、ioremap相关推荐
- 【原创】(十二)Linux内存管理之vmap与vmalloc
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- Linux内存管理:Fixmaps(固定映射地址)和ioremap
目录 Fixmaps和ioremap 映射 ioremap工作原理 早期ioremap的使用 Links 相关阅读 Fix-Mapped地址是一组特殊的编译时地址,其对应的物理地址不必是线性地址减__ ...
- 高端内存映射之kmap持久内核映射--Linux内存管理(二十)
日期 内核版本 架构 作者 GitHub CSDN 2016-09-29 Linux-4.7 X86 & arm gatieme LinuxDeviceDrivers Linux内存管理 在内 ...
- Linux内存管理:memblock(引导期间管理内存区域)
目录 介绍 内存块 内存块初始化 Memblock API 获取有关内存区域的信息 Memblock调试 链接 相关阅读 看原文:<Linux内存管理:memblock> 介绍 内存管理是 ...
- linux内存管理子系统采用基于内存区域,Linux 内存管理之highmem简介
一.Linux内核地址空间 一般来说Linux 内核按照 3:1 的比率来划分虚拟内存(X86等):3 GB 的虚拟内存用于用户空间,1GB 的内存用于内核空间.当然有些体系结构如MIPS使用2:2 ...
- Linux内存管理之高端内存映射
一:引子 我们在前面分析过,在linux内存管理中,内核使用3G->4G的地址空间,总共1G的大小.而且有一部份用来做非连续空间的物理映射(vmalloc).除掉这部份空间之外,只留下896M大 ...
- 一文掌握 Linux 内存管理
作者:dengxuanshi,腾讯 IEG 后台开发工程师 以下源代码来自 linux-5.10.3 内核代码,主要以 x86-32 为例. Linux 内存管理是一个很复杂的"工程&quo ...
- linux内存管理之malloc
对于内核的内存管理,像kmalloc,vmalloc,kmap,ioremap等比较熟悉.而对用户层的管理机制不是很熟悉,下面就从malloc的实现入手.( 这里不探讨linux系统调用的实现机制. ...
- Linux内存管理知识总结(一)
以下源代码来自 linux-5.10.3 内核代码,主要以 x86-32 为例 Linux 内存管理是一个很复杂的"工程",它不仅仅是对物理内存的管理,也涉及到虚拟内存管理.内存交 ...
最新文章
- Bitmap too larget to be uploaded into a texture的解决方法
- 温铁军、林毅夫、陈平,从学术、现实等多方面来分析,谁的价值高?
- linux数据库都备份什么,Linux运维学习之数据库备份与恢复
- “国货之光” 完美日记的微服务实践和优化思路
- leetcode329. 矩阵中的最长递增路径(dfs)
- tableau linux无网络安装_四十二、Linux网络管理,软件安装,进程管理总结
- 妲己机器人怎么升级固件_台湾重金设计的3D妲己,亮瞎了
- linux 智联 网卡设置,Linux初学者DNS配置指南(四)配置Bind常见问题
- python手动安装包_python pip如何手动安装二进制包
- unity 改变ui文字_如何在Unity中实现逐字打印UI中的Text文字
- 通过ODBC连接PostgreSQL和Greenplum
- 计算机黑屏闪光标,电脑开机黑屏只有光标在闪的解决方法
- hdmi接口和计算机连接,hdmi接口,教您hdmi接口怎么连接电视
- 做好里程碑就是项目成功了一半
- linux下phylip软件构建NJ树,利用phylip构建进化树详解
- 《相关性准则——大数据时代的高效能之道》一一1.6 相关性准则
- 从零到一学习计算机视觉:朋友圈爆款背后的计算机视觉技术与应用 | 公开课笔记...
- 华为如何关闭系统更新提示
- 蓝桥杯第十届国赛C++研究生组 试题 A: 三升序列
- 不同公司系统的对接心得
热门文章
- linux ftp 警告暗号话,ssh,FTP到远程服务器时,显示自定义的警告信息
- vue动态加载静态资源
- [BZOJ1998][Hnoi2010]Fsk物品调度
- 解决fatal: unable to connect to github.com问题
- WPF读写config配置文件
- BCD码和十六进制的区别【转】
- 给大家发一个DDOS防御包算法公式
- 1-1-Html技术
- 襄阳汽车职业学院计算机专业,襄阳汽车职业技术学院毕业设计模板.docx
- java udp文件_Java对文件的操作及UDP,TCP