/*
* 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;
}