• mm/kmalloc.c


    /*
     *  linux/mm/kmalloc.c
     *
     *  Copyright (C) 1991, 1992  Linus Torvalds & Roger Wolff.
     *
     *  Written by R.E. Wolff Sept/Oct '93.
     *
     */

    #include <linux/mm.h>
    #include <asm/system.h>
    #include <linux/delay.h>

    #define GFP_LEVEL_MASK 0xf

    /* I want this low enough for a while to catch errors.
       I want this number to be increased in the near future:
            loadable device drivers should use this function to get memory */

    //最大分配的数量4k
    #define MAX_KMALLOC_K 4    


    /* This defines how many times we should try to allocate a free page before
       giving up. Normally this shouldn't happen at all. */
       //获取空闲页尝试次数 4
    #define MAX_GET_FREE_PAGE_TRIES 4


    /* Private flags. */

    #define MF_USED 0xffaa0055
    #define MF_FREE 0x0055ffaa


    /*
     * Much care has gone into making these routines in this file reentrant.
     *
     * The fancy bookkeeping of nbytesmalloced and the like are only used to
     * report them to the user (oooohhhhh, aaaaahhhhh....) are not
     * protected by cli(). (If that goes wrong. So what?)
     *
     * These routines restore the interrupt status to allow calling with ints
     * off.
     */

    /*
     * A block header. This is in front of every malloc-block, whether free or not.
     */
     //block首部结构体,此结构体在malloc-block前端
    struct block_header {
        unsigned long bh_flags;                  //block首部标志
        union {                                  
            unsigned long ubh_length;            //block长度
            struct block_header *fbh_next;       //下一个block长度
        } vp;
    };


    #define bh_length vp.ubh_length                   //长度
    #define bh_next   vp.fbh_next                     //下一个指针
    #define BH(p) ((struct block_header *)(p))        //转化为block头部结构体


    /*
     * The page descriptor is at the front of every page that malloc has in use.
     */
     //页描述符在每一页内存前
    struct page_descriptor {
        struct page_descriptor *next;               //下一个页描述符指针
        struct block_header *firstfree;             //block头
        int order;                                  //序号
        int nfree;                                  //空闲数量
    };

    //将指定位置转为页描述符结构
    #define PAGE_DESC(p) ((struct page_descriptor *)(((unsigned long)(p)) & PAGE_MASK))


    /*
     * A size descriptor describes a specific class of malloc sizes.
     * Each class of sizes has its own freelist.
     */
     //长度描述符
    struct size_descriptor {
        struct page_descriptor *firstfree;    //页描述符指针
        int size;                             //大小
        int nblocks;                          //block数量

        int nmallocs;                        //分配数量
        int nfrees;                          //释放数量
        int nbytesmalloced;                  //按位分配
        int npages;                          //页数量
    };

    //描述符数组
    struct size_descriptor sizes[] = {
        { NULL,  32,127, 0,0,0,0 },
        { NULL,  64, 63, 0,0,0,0 },
        { NULL, 128, 31, 0,0,0,0 },
        { NULL, 252, 16, 0,0,0,0 },
        { NULL, 508,  8, 0,0,0,0 },
        { NULL,1020,  4, 0,0,0,0 },
        { NULL,2040,  2, 0,0,0,0 },
        { NULL,4080,  1, 0,0,0,0 },
        { NULL,   0,  0, 0,0,0,0 }
    };


    #define NBLOCKS(order)          (sizes[order].nblocks)
    #define BLOCKSIZE(order)        (sizes[order].size)


    //初始化
    long kmalloc_init (long start_mem,long end_mem)
    {
        int order;

    /*
     * Check the static info array. Things will blow up terribly if it's
     * incorrect. This is a late "compile time" check.....
     */
     //检测静态信息数组,如果这里发生错误,后续爆发会很可怕
        for (order = 0;BLOCKSIZE(order);order++)
        {
            //校验静态数组所分配的空间是否大于一页内存 size * 块数 + 描述符大小
            if ((NBLOCKS (order)*BLOCKSIZE(order) + sizeof (struct page_descriptor)) >
                PAGE_SIZE)
            {
                printk ("Cannot use %d bytes out of %d in order = %d block mallocs ",
                    NBLOCKS (order) * BLOCKSIZE(order) +
                            sizeof (struct page_descriptor),
                    (int) PAGE_SIZE,
                    BLOCKSIZE (order));
                panic ("This only happens if someone messes with kmalloc");
            }
        }
        return start_mem;
    }


    //根据大小获取序号
    int get_order (int size)
    {
        int order;

        /* Add the size of the header */
        size += sizeof (struct block_header);
        for (order = 0;BLOCKSIZE(order);order++)
            if (size <= BLOCKSIZE (order))
                return order;
        return -1;
    }

    //分配内存
    void * kmalloc (size_t size, int priority)
    {
        unsigned long flags;
        int order,tries,i,sz;
        struct block_header *p;
        struct page_descriptor *page;
        extern unsigned long intr_count;

    /* Sanity check... */
    //校验
        if (intr_count && priority != GFP_ATOMIC) {
            printk("kmalloc called nonatomically from interrupt %08lx ",
                ((unsigned long *)&size)[-1]);
            priority = GFP_ATOMIC;
        }
        //如果分配的空间大于4k
    if (size > MAX_KMALLOC_K * 1024)
         {
             //输出相关信息,拒绝分配过大空间
         printk ("kmalloc: I refuse to allocate %d bytes (for now max = %d). ",
                    size,MAX_KMALLOC_K*1024);
         return (NULL);
         }
    //根据需要分配的空间获取序号
    order = get_order (size);
    if (order < 0)
        {
        printk ("kmalloc of too large a block (%d bytes). ",size);
        return (NULL);
        }
    //保存标志寄存器
    save_flags(flags);

    /* It seems VERY unlikely to me that it would be possible that this
       loop will get executed more than once. */
       //希望只执行一次
    tries = MAX_GET_FREE_PAGE_TRIES;
    while (tries --)
        {
        /* Try to allocate a "recently" freed memory block */
        //尽可能分配最近的空闲内存块
        cli ();
        //
        if ((page = sizes[order].firstfree) &&                  //页描述符指针不空并且页面的block头不空
            (p    =  page->firstfree))
            {
                //内存块头中的标志为空闲
            if (p->bh_flags == MF_FREE)
                {
                    //页描述符中第一个空闲位置为下一个内存块头
                page->firstfree = p->bh_next;
                //当前页所能分配的数量减少一个
                page->nfree--;
                //如果剩余分配数量为0
                if (!page->nfree)
                    {
                        //则当前数组指向下一页,以待下次分配
                    sizes[order].firstfree = page->next;
                    page->next = NULL;
                    }
                restore_flags(flags);
                //设置相关参数
                sizes [order].nmallocs++;
                sizes [order].nbytesmalloced += size;
                p->bh_flags =  MF_USED; /* As of now this block is officially in use */
                p->bh_length = size;
                //这里完成分配返回
                return p+1; /* Pointer arithmetic: increments past header */            
                }
            //此时内存块在空闲链表中,但是指针p的位置不空闲,返回空值,分配失败
            printk ("Problem: block on freelist at %08lx isn't free. ",(long)p);
            return (NULL);
            }
        //恢复标志寄存器
        restore_flags(flags);


        /* Now we're in trouble: We need to get a new free page..... */
        //现在我们有麻烦了,我们需要获取一个新的空闲页
        //根据order确定sz
        sz = BLOCKSIZE(order); /* sz is the size of the blocks we're dealing with */

        /* This can be done with ints on: This is private to this invocation */
        //申请一页内存,取内存页前面的页描述符
        page = (struct page_descriptor *) __get_free_page (priority & GFP_LEVEL_MASK);
        //如果获取的描述符为空,则申请失败
        if (!page)
            {
            printk ("Couldn't get a free page..... ");
            return NULL;
            }
    #if 0
        printk ("Got page %08x to use for %d byte mallocs....",(long)page,sz);
    #endif
        //申请内存页成功,则此类型页面增加一页
        sizes[order].npages++;

        /* Loop for all but last block: */
        //循环初始化内存块
        for (i=NBLOCKS(order),p=BH (page+1);i > 1;i--,p=p->bh_next)
            {
            p->bh_flags = MF_FREE;
            p->bh_next = BH ( ((long)p)+sz);
            }
        /* Last block: */
        //初始化最后一个内存块
        p->bh_flags = MF_FREE;
        p->bh_next = NULL;
        //初始化页描述符
        page->order = order;
        page->nfree = NBLOCKS(order);
        page->firstfree = BH(page+1);
    #if 0
        printk ("%d blocks per page ",page->nfree);
    #endif
        /* Now we're going to muck with the "global" freelist for this size:
           this should be uniterruptible */
        cli ();
        /*
         * sizes[order].firstfree used to be NULL, otherwise we wouldn't be
         * here, but you never know....
         */
        page->next = sizes[order].firstfree;
        sizes[order].firstfree = page;
        restore_flags(flags);
        }

    /* Pray that printk won't cause this to happen again :-) */
    //分配失败的提示

    printk ("Hey. This is very funny. I tried %d times to allocate a whole "
            "new page for an object only %d bytes long, but some other process "
            "beat me to actually allocating it. Also note that this 'error' "
            "message is soooo very long to catch your attention. I'd appreciate "
            "it if you'd be so kind as to report what conditions caused this to "
            "the author of this kmalloc: wolff@dutecai.et.tudelft.nl. "
            "(Executive summary: This can't happen) ",
                    MAX_GET_FREE_PAGE_TRIES,
                    size);
    return NULL;
    }

    //释放
    void kfree_s (void *ptr,int size)
    {
        //标志
    unsigned long flags;
    //顺序
    int order;
    //内存块头指针
    register struct block_header *p=((struct block_header *)ptr) -1;
    //页描述符指针
    struct page_descriptor *page,*pg2;

    //根据参数获取内存块头部,根据头部获取内存页的页描述符
    page = PAGE_DESC (p);
    //根据页描述符指针获取到order
    order = page->order;
    if ((order < 0) ||
        (order > sizeof (sizes)/sizeof (sizes[0])) ||
        (((long)(page->next)) & ~PAGE_MASK) ||
        (p->bh_flags != MF_USED))
        {
        printk ("kfree of non-kmalloced memory: %p, next= %p, order=%d ",
                    p, page->next, page->order);
        return;
        }
        //
    if (size &&
        size != p->bh_length)
        {
        printk ("Trying to free pointer at %p with wrong size: %d instead of %lu. ",
            p,size,p->bh_length);
        return;
        }
        //获取内存块的大小
    size = p->bh_length;
    //释放
    p->bh_flags = MF_FREE; /* As of now this block is officially free */

    save_flags(flags);
    cli ();
    //初始化相关参数
    p->bh_next = page->firstfree;
    page->firstfree = p;
    page->nfree ++;

    if (page->nfree == 1)
       { /* Page went from full to one free block: put it on the freelist */
       if (page->next)
            {
            printk ("Page %p already on freelist dazed and confused.... ", page);
            }
       else
            {
            page->next = sizes[order].firstfree;
            sizes[order].firstfree = page;
            }
       }

    /* If page is completely free, free it */
    //内存页完全空闲的处理,实际上就是释放此内存页面
    if (page->nfree == NBLOCKS (page->order))
        {
    #if 0
        printk ("Freeing page %08x. ", (long)page);
    #endif
        if (sizes[order].firstfree == page)
            {
            sizes[order].firstfree = page->next;
            }
        else
            {
            for (pg2=sizes[order].firstfree;
                    (pg2 != NULL) && (pg2->next != page);
                            pg2=pg2->next)
                /* Nothing */;
            if (pg2 != NULL)
                pg2->next = page->next;
            else
                printk ("Ooops. page %p doesn't show on freelist. ", page);
            }
        free_page ((long)page);
        }
    restore_flags(flags);

    //正常处理
    sizes[order].nfrees++;      /* Noncritical (monitoring) admin stuff */
    sizes[order].nbytesmalloced -= size;
    }

  • 相关阅读:
    Servlet 易错点和注意点
    Spring 完成自动注入(autowire)
    Java 定时调度Timer&Quartz
    常用Linux命令杂记
    Spring 使用AOP——基于注解配置
    Spring 使用AOP——xml配置
    Spring 使用纯注解方式完成IoC
    Spring 简单使用IoC与DI——XML配置
    让多个HTML页面 使用 同一段HTML代码
    Feture、ListenableFuture、ComplatableFuture
  • 原文地址:https://www.cnblogs.com/xiaofengwei/p/3773992.html
Copyright © 2020-2023  润新知