内核代码阅读(10) - 内核缓冲区的管理slab下

简介: 内核缓冲区的管理slab下

专用缓冲区 kmem_cache_free 缓冲区的释放

static inline void kmem_cache_free_one(kmem_cache_t *cachep, void *objp)
    {
        slab_t* slabp;
        CHECK_PAGE(virt_to_page(objp));
        slabp = GET_PAGE_SLAB(virt_to_page(objp));
        {
                unsigned int objnr = (objp-slabp->s_mem)/cachep->objsize;
                slab_bufctl(slabp)[objnr] = slabp->free;
                slabp->free = objnr;
        }
        STATS_DEC_ACTIVE(cachep);
        if (slabp->inuse-- == cachep->num)
                goto moveslab_partial;
        if (!slabp->inuse)
                goto moveslab_free;
        return;
    moveslab_partial:
        {
                struct list_head *t = cachep->firstnotfull;
                cachep->firstnotfull = &slabp->list;
                if (slabp->list.next == t)
                        return;
                list_del(&slabp->list);
                list_add_tail(&slabp->list, t);
                return;
        }
    moveslab_free:
        {
                struct list_head *t = cachep->firstnotfull->prev;
                list_del(&slabp->list);
                list_add_tail(&slabp->list, &cachep->slabs);
                if (cachep->firstnotfull == &slabp->list)
                        cachep->firstnotfull = t->next;
                return;
        }
    }
1) slabp = GET_PAGE_SLAB(virt_to_page(objp));
   通过要释放的物理地址,可以获取到对应的 struct page结构, 通过page结构的list可以找到objp所属的slab。
2) unsigned int objnr = (objp-slabp->s_mem)/cachep->objsize;
   slab_bufctl(slabp)[objnr] = slabp->free;
   计算objp在slab中的下标。
   更新slab中的空闲链接数组。
3) if (slabp->inuse-- == cachep->num)
           goto moveslab_partial;
   更新inuse计数器。

通用缓冲区的分配 kmalloc

void * kmalloc (size_t size, int flags)
    {
        cache_sizes_t *csizep = cache_sizes;
        for (; csizep->cs_size; csizep++) {
                if (size > csizep->cs_size)
                        continue;
                return __kmem_cache_alloc(flags & GFP_DMA ?
                         csizep->cs_dmacachep : csizep->cs_cachep, flags);
        }
        return NULL;
    }
1) cache_sizes 是一个cache_sizes_t的结构数组。
   typedef struct cache_sizes {
       size_t                 cs_size;
       kmem_cache_t        *cs_cachep;
       kmem_cache_t        *cs_dmacachep;
   } cache_sizes_t;
2) for 寻转在cache_sizes数组中找到第一个 csizep->cs_size大于size的kmem_cache_t类型的指针。
3) 然后通过 kmem_cache_alloc 分配。

kmem_cache_reap slab的回收

void kmem_cache_reap(int gfp_mask)
    {
        searchp = clock_searchp;
        do {
                unsigned int pages;
                struct list_head* p;
                unsigned int full_free;
                if (searchp->flags & SLAB_NO_REAP)
                        goto next;
                spin_lock_irq(&searchp->spinlock);
                if (searchp->growing)
                        goto next_unlock;
                if (searchp->dflags & DFLGS_GROWN) {
                        searchp->dflags &= ~DFLGS_GROWN;
                        goto next_unlock;
                }
                full_free = 0;
                p = searchp->slabs.prev;
                while (p != &searchp->slabs) {
                        slabp = list_entry(p, slab_t, list);
                        if (slabp->inuse)
                                break;
                        full_free++;
                        p = p->prev;
                }
                pages = full_free * (1<<searchp->gfporder);
                if (searchp->ctor)
                        pages = (pages*4+1)/5;
                if (searchp->gfporder)
                        pages = (pages*4+1)/5;
                if (pages > best_pages) {
                        best_cachep = searchp;
                        best_len = full_free;
                        best_pages = pages;
                        if (full_free >= REAP_PERFECT) {
                                clock_searchp = list_entry(searchp->next.next,
                                                        kmem_cache_t,next);
                                goto perfect;
                        }
                }
                searchp = list_entry(searchp->next.next,kmem_cache_t,next);
        } while (--scan && searchp != clock_searchp);
        clock_searchp = searchp;
        if (!best_cachep)
                goto out;
        spin_lock_irq(&best_cachep->spinlock);
    perfect:
        best_len = (best_len*4 + 1)/5;
        for (scan = 0; scan < best_len; scan++) {
                struct list_head *p;
                if (best_cachep->growing)
                        break;
                p = best_cachep->slabs.prev;
                if (p == &best_cachep->slabs)
                        break;
                slabp = list_entry(p,slab_t,list);
                if (slabp->inuse)
                        break;
                list_del(&slabp->list);
                if (best_cachep->firstnotfull == &slabp->list)
                        best_cachep->firstnotfull = &best_cachep->slabs;
                STATS_INC_REAPED(best_cachep);
                spin_unlock_irq(&best_cachep->spinlock);
                kmem_slab_destroy(best_cachep, slabp);
                spin_lock_irq(&best_cachep->spinlock);
        }
        spin_unlock_irq(&best_cachep->spinlock);
    out:
        up(&cache_chain_sem);
        return;
    }
1) searchp = clock_searchp;
   并不是每次扫描所有的slab队列。clock_searchp 会记住上一次扫描的位置。
2) kmem_slab_destroy(best_cachep, slabp);
相关文章
|
3月前
|
Ubuntu Linux 数据安全/隐私保护
内核实验(七):使用内核KFIFO环形缓冲区机制
本文通过一个内核模块实验,演示了如何在Linux内核中使用KFIFO环形缓冲区机制,包括定义KFIFO、编写驱动程序以及在Qemu虚拟机中进行编译、部署和测试,展示了KFIFO在无需额外加锁的情况下如何安全地在读者和写者线程间进行数据传输。
135 0
内核实验(七):使用内核KFIFO环形缓冲区机制
|
6月前
|
消息中间件 存储 缓存
Linux内存映射mmap
Linux内存映射mmap
80 0
|
存储 索引 Windows
驱动开发:内核物理内存寻址读写
在某些时候我们需要读写的进程可能存在虚拟内存保护机制,在该机制下用户的`CR3`以及`MDL`读写将直接失效,从而导致无法读取到正确的数据,本章我们将继续研究如何实现物理级别的寻址读写。首先,驱动中的物理页读写是指在驱动中直接读写物理内存页(而不是虚拟内存页)。这种方式的优点是它能够更快地访问内存,因为它避免了虚拟内存管理的开销,通过直接读写物理内存,驱动程序可以绕过虚拟内存的保护机制,获得对系统中内存的更高级别的访问权限。
7066 1
驱动开发:内核解析内存四级页表
当今操作系统普遍采用64位架构,CPU最大寻址能力虽然达到了64位,但其实仅仅只是用到了48位进行寻址,其内存管理采用了`9-9-9-9-12`的分页模式,`9-9-9-9-12`分页表示物理地址拥有四级页表,微软将这四级依次命名为PXE、PPE、PDE、PTE这四项。关于内存管理和分页模式,不同的操作系统和体系结构可能会有略微不同的实现方式。9-9-9-9-12的分页模式是一种常见的分页方案,其中物理地址被分成四级页表:PXE(Page Directory Pointer Table Entry)、PPE(Page Directory Entry)、PDE(Page Table Entry)
10834 0
|
运维 Linux 开发者
Kernel SIG直播:让人头疼的“内核内存被改”和“内存泄露”怎么解?|第13期
内存发生故障问题让你抓狂?明天(周三)将有效地解决线上内核内存被改和内存泄露这两大难题。
Kernel SIG直播:让人头疼的“内核内存被改”和“内存泄露”怎么解?|第13期
|
Linux 调度
block设备驱动之内核机制
block设备驱动之内核机制
block设备驱动之内核机制