浅析linux内核内存管理之kmalloc
linux-dash
A beautiful web dashboard for Linux
项目地址:https://gitcode.com/gh_mirrors/li/linux-dash
免费下载资源
·
在SLAB的高速缓存中有普通高速缓存和专用高速缓存,平时用kmem_cache_create创建的是专用高速缓存,比如存放task_struct,mm_struct的高速缓存。普通高速缓存主要供kmalloc使用。第一个高速缓存叫kmem_cache,存放在cache_cache变量中,这个cache专门用于为其他cache分配描述符。
- static kmem_cache_t cache_cache = {
- .lists = LIST3_INIT(cache_cache.lists),
- .batchcount = 1,
- .limit = BOOT_CPUCACHE_ENTRIES,
- .objsize = sizeof(kmem_cache_t),
- .flags = SLAB_NO_REAP,
- .spinlock = SPIN_LOCK_UNLOCKED,
- .name = "kmem_cache",
- #if DEBUG
- .reallen = sizeof(kmem_cache_t),
- #endif
- };
- struct cache_sizes malloc_sizes[] = {
- #define CACHE(x) { .cs_size = (x) },
- #include <linux/kmalloc_sizes.h>
- { 0, }
- #undef CACHE
- };
- static inline void *kmalloc(size_t size, int flags)
- {
- if (__builtin_constant_p(size)) {
- int i = 0;
- #define CACHE(x) \
- if (size <= x) \
- goto found; \
- else \
- i++;
- #include "kmalloc_sizes.h"
- #undef CACHE
- {
- extern void __you_cannot_kmalloc_that_much(void);
- __you_cannot_kmalloc_that_much();
- }
- found:
- return kmem_cache_alloc((flags & GFP_DMA) ?
- malloc_sizes[i].cs_dmacachep :
- malloc_sizes[i].cs_cachep, flags);
- }
- return __kmalloc(size, flags);
- }
- #if (PAGE_SIZE == 4096)
- CACHE(32)
- #endif
- CACHE(64)
- #if L1_CACHE_BYTES < 64
- CACHE(96)
- #endif
- CACHE(128)
- #if L1_CACHE_BYTES < 128
- CACHE(192)
- #endif
- CACHE(256)
- CACHE(512)
- CACHE(1024)
- CACHE(2048)
- CACHE(4096)
- CACHE(8192)
- CACHE(16384)
- CACHE(32768)
- CACHE(65536)
- CACHE(131072)
- #ifndef CONFIG_MMU
- CACHE(262144)
- CACHE(524288)
- CACHE(1048576)
- #ifdef CONFIG_LARGE_ALLOCS
- CACHE(2097152)
- CACHE(4194304)
- CACHE(8388608)
- CACHE(16777216)
- CACHE(33554432)
- #endif /* CONFIG_LARGE_ALLOCS */
- #endif /* CONFIG_MMU */
- #define CACHE(x) { .cs_size = (x) }
- #define CACHE(x) \
- if (size <= x) \
- goto found; \
- else \
- i++;
kmalloc()主要调用__kmalloc()进行分配:
- void * __kmalloc (size_t size, int flags)
- {
- struct cache_sizes *csizep = malloc_sizes;
- for (; csizep->cs_size; csizep++) {
- if (size > csizep->cs_size)
- continue;
- #if DEBUG
- /* This happens if someone tries to call
- * kmem_cache_create(), or kmalloc(), before
- * the generic caches are initialized.
- */
- BUG_ON(csizep->cs_cachep == NULL);
- #endif
- return __cache_alloc(flags & GFP_DMA ?
- csizep->cs_dmacachep : csizep->cs_cachep, flags);
- }
- return NULL;
- }
调用kfree释放分配到的内存:
- void kfree (const void *objp)
- {
- kmem_cache_t *c;
- unsigned long flags;
- if (!objp)
- return;
- local_irq_save(flags);
- kfree_debugcheck(objp);
- c = GET_PAGE_CACHE(virt_to_page(objp));
- __cache_free(c, (void*)objp);
- local_irq_restore(flags);
- }
kmalloc的第一个参数是要分配的块的大小,第二个参数是分配标志(flags)。
最常用的标志是GFP_KERNEL,它表示内存分配的(最终总是调用get_free_pages来实现实际的分配,这就是GFP_前缀的由来)是代表运行在内核空间的进程执行的。换句话说,这意味着调用它的函数正代表某个进程执行系统调用。使用GFP_KERNEL允许kmalloc在空闲内存较少时把当前进程转入休眠以等待一个页面。因此,使用GFP_KERNEL分配内存的函数必须是可重入的。在当前进程休眠时,内核会采取适当的行动,或者是把缓冲区的内存刷写到硬盘上,或者是从一个用户进程换出内存,以获取一个内存页面。
GFP分配标志并不是始终适用,有时kmalloc是在进程上下文之外被调用的,例如在中断处理例程,tasklet以及内核定时器中调用。这种情况下current进程就不应该休眠,驱动程序则应该换用GFP_ATOMIC标志。内核通常会为原子性的分配预留一些空闲页面。使用GFP_ATOMIC标志时,kmalloc甚至可以调用最后一个空闲页面。不过如果连最后一页都没有了,分配就返回失败。
下边来看一些常用的符号,这些符号都包含在<linux/gfp.h>:
GFP_ATOMIC
用于在中断处理例程或其他运行于进程上下文之外的代码中分配内存,不会休眠。
GFP_KERNEL
内核内存的通常分配方法,可能引起休眠。
GFP_USER
用于为用户空间分配内存,可能会引起休眠。
GFP_NOIO
GFP_NOFS
这两个标志的功能类似于GFP_KERNEL,但是为内核分配内存的工作方式添加了一些限制。具有GFP_NOFS标志的分配不允许执行任何文件系统调用,而GFP_NOIO禁止任何I/O的初始化。
__GFP_DMA
该标志请求分配发生在可进行DMA的内存区段中。
__GFP_HIGHMEM
这个标志表明分配的内存科位于高端内存。
__GFP_COLD
表示从冷高速缓存分配,即per cpu pages的cold page。
下面一个测试程序,模拟mmu查表过程:
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/slab.h>
- #include <linux/gfp.h>
- #include <asm/pgtable.h>
- #include <asm/page.h>
- #include <linux/sched.h>
- #include <linux/mm.h>
- #include <linux/highmem.h>
- long unsigned int vaddr;
- static int __init kmalloc_test_init(void){
- pte_t *pte_tmp = NULL;
- pmd_t *pmd_tmp = NULL;
- pud_t *pud_tmp = NULL;
- pgd_t *pgd_tmp = NULL;
- long unsigned int paddr;
- printk("kmalloc test init!\n");
- vaddr = (long unsigned int)kmalloc(4, GFP_KERNEL);
- pgd_tmp = pgd_offset(current->mm, vaddr);
- if(pgd_present(*pgd_tmp))
- {
- pud_tmp = pud_offset(pgd_tmp, vaddr);
- if(pud_present(*pud_tmp))
- {
- pmd_tmp = pmd_offset(pud_tmp, vaddr);
- if(pmd_present(*pmd_tmp))
- {
- if(!pmd_large(*pmd_tmp)){
- pte_tmp = pte_offset_kernel(pmd_tmp, vaddr);
- if(pte_present(*pte_tmp))
- {
- paddr = (pte_val(*pte_tmp) & PAGE_MASK) | (vaddr & ~PAGE_MASK);
- printk("physical address of 0x%lx is 0x%lx\n", vaddr, paddr);
- printk("__pa(vaddr) is 0x%lx\n", __pa(vaddr));
- }
- else
- {
- printk("pte entry is not present!\n");
- return -1;
- }
- }
- else
- {
- paddr = (pmd_val(*pmd_tmp) & PMD_MASK) | (vaddr & ~PMD_MASK);
- printk("Use Large Page PSE = 1\n");
- printk("physical address of 0x%lx is 0x%lx\n", vaddr, paddr);
- printk("__pa(vaddr) is 0x%lx\n", __pa(vaddr));
- }
- }
- else
- {
- printk("pte entry is not present!\n");
- }
- }
- else
- {
- printk("pud entry is not present!\n");
- return -1;
- }
- }
- else
- {
- printk("pgd entry is not present!\n");
- return -1;
- }
- return 0;
- }
- static void __exit kmalloc_test_exit(void){
- printk("kmalloc test exit!\n");
- if(vaddr)
- kfree(vaddr);
- }
- module_init(kmalloc_test_init);
- module_exit(kmalloc_test_exit);
- MODULE_LICENSE("GPL");
测试结果:
- [ 212.054880] kmalloc test init!
- [ 212.054883] Use Large Page PSE = 1
- [ 212.054884] physical address of 0xf6692b80 is 0x36692b80
- [ 212.054886] __pa(vaddr) is 0x36692b80
- kmalloc是基于slab的,所以速度比较快。vmalloc的内部会调用到kmalloc,但是只是分配vm_struct描述符,和分配nr_pages指针数组,这个数组里的每个元素指向vm_struct对应的非连续内存区域的每一个页描述符结构。真正的分页是通过alloc_page一页一页的从buddy system分配。所以物理地址是不连续的,一页一页分配物理地址不一定啥地方了。kmalloc分配的页已经映射好了,而vmalloc分配后一级一级建立页表很是麻烦。
- 高端内存使用的是小页,所以使用vmalloc的时候不会打印出"Use Large Page PSE = 1"。
- 在中断上下文中可以使用kmalloc,前提是使用GFP_ATOMIC标志,而中断上下文中不能用vmalloc替代kmalloc,vmalloc调用kmalloc和alloc_page都使用了GFP_KERNEL标志,这个标志可能因此进程休眠。
- 用vmalloc分配页修改的是内核页表部分,并没有修改进程的相关项,在访问的时候需要通过page fault来同步,而kmalloc就不需要这一过程。
GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e
added ecosystem file for PM2 4 年前
5def40a3
Add host customization support for the NodeJS version 4 年前
更多推荐
已为社区贡献2条内容
所有评论(0)