malloc.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 1,140 行 · 第 1/2 页
C
1,140 行
return p;}/* * Allocate a page of fragments */static __inline__ intmalloc_make_chunks(int bits){ struct pginfo *bp; void *pp; int i, k, l; /* Allocate a new bucket */ pp = malloc_pages(malloc_pagesize); if (!pp) return 0; /* Find length of admin structure */ l = offsetof(struct pginfo, bits[0]); l += sizeof bp->bits[0] * (((malloc_pagesize >> bits)+MALLOC_BITS-1) / MALLOC_BITS); /* Don't waste more than two chunks on this */ if ((1<<(bits)) <= l+l) { bp = (struct pginfo *)pp; } else { bp = (struct pginfo *)imalloc(l); if (!bp) { ifree(pp); return 0; } } bp->size = (1<<bits); bp->shift = bits; bp->total = bp->free = malloc_pagesize >> bits; bp->page = pp; /* set all valid bits in the bitmap */ k = bp->total; i = 0; /* Do a bunch at a time */ for(;k-i >= MALLOC_BITS; i += MALLOC_BITS) bp->bits[i / MALLOC_BITS] = ~0; for(; i < k; i++) bp->bits[i/MALLOC_BITS] |= 1<<(i%MALLOC_BITS); if (bp == bp->page) { /* Mark the ones we stole for ourselves */ for(i=0;l > 0;i++) { bp->bits[i/MALLOC_BITS] &= ~(1<<(i%MALLOC_BITS)); bp->free--; bp->total--; l -= (1 << bits); } } /* MALLOC_LOCK */ page_dir[ptr2index(pp)] = bp; bp->next = page_dir[bits]; page_dir[bits] = bp; /* MALLOC_UNLOCK */ return 1;}/* * Allocate a fragment */static void *malloc_bytes(size_t size){ int i,j; u_int u; struct pginfo *bp; int k; u_int *lp; /* Don't bother with anything less than this */ if (size < malloc_minsize) size = malloc_minsize; /* Find the right bucket */ j = 1; i = size-1; while (i >>= 1) j++; /* If it's empty, make a page more of that size chunks */ if (!page_dir[j] && !malloc_make_chunks(j)) return 0; bp = page_dir[j]; /* Find first word of bitmap which isn't empty */ for (lp = bp->bits; !*lp; lp++) ; /* Find that bit, and tweak it */ u = 1; k = 0; while (!(*lp & u)) { u += u; k++; } *lp ^= u; /* If there are no more free, remove from free-list */ if (!--bp->free) { page_dir[j] = bp->next; bp->next = 0; } /* Adjust to the real offset of that chunk */ k += (lp-bp->bits)*MALLOC_BITS; k <<= bp->shift; if (malloc_junk) memset((u_char*)bp->page + k, SOME_JUNK, bp->size); return (u_char *)bp->page + k;}/* * Allocate a piece of memory */static void *imalloc(size_t size){ void *result; if (suicide) abort(); if ((size + malloc_pagesize) < size) /* Check for overflow */ result = 0; else if (size <= malloc_maxsize) result = malloc_bytes(size); else result = malloc_pages(size); if (malloc_abort && !result) wrterror("allocation failed.\n"); if (malloc_zero && result) memset(result, 0, size); return result;}/* * Change the size of an allocation. */static void *irealloc(void *ptr, size_t size){ void *p; u_long osize, index; struct pginfo **mp; int i; if (suicide) abort(); index = ptr2index(ptr); if (index < malloc_pageshift) { wrtwarning("junk pointer, too low to make sense.\n"); return 0; } if (index > last_index) { wrtwarning("junk pointer, too high to make sense.\n"); return 0; } mp = &page_dir[index]; if (*mp == MALLOC_FIRST) { /* Page allocation */ /* Check the pointer */ if ((u_long)ptr & malloc_pagemask) { wrtwarning("modified (page-) pointer.\n"); return 0; } /* Find the size in bytes */ for (osize = malloc_pagesize; *++mp == MALLOC_FOLLOW;) osize += malloc_pagesize; if (!malloc_realloc && /* unless we have to, */ size <= osize && /* .. or are too small, */ size > (osize - malloc_pagesize)) { /* .. or can free a page, */ return ptr; /* don't do anything. */ } } else if (*mp >= MALLOC_MAGIC) { /* Chunk allocation */ /* Check the pointer for sane values */ if (((u_long)ptr & ((*mp)->size-1))) { wrtwarning("modified (chunk-) pointer.\n"); return 0; } /* Find the chunk index in the page */ i = ((u_long)ptr & malloc_pagemask) >> (*mp)->shift; /* Verify that it isn't a free chunk already */ if ((*mp)->bits[i/MALLOC_BITS] & (1<<(i%MALLOC_BITS))) { wrtwarning("chunk is already free.\n"); return 0; } osize = (*mp)->size; if (!malloc_realloc && /* Unless we have to, */ size < osize && /* ..or are too small, */ (size > osize/2 || /* ..or could use a smaller size, */ osize == malloc_minsize)) { /* ..(if there is one) */ return ptr; /* ..Don't do anything */ } } else { wrtwarning("pointer to wrong page.\n"); return 0; } p = imalloc(size); if (p) { /* copy the lesser of the two sizes, and free the old one */ if (!size || !osize) ; else if (osize < size) memcpy(p, ptr, osize); else memcpy(p, ptr, size); ifree(ptr); } return p;}/* * Free a sequence of pages */static __inline__ voidfree_pages(void *ptr, int index, struct pginfo *info){ int i; struct pgfree *pf, *pt=0; u_long l; void *tail; if (info == MALLOC_FREE) { wrtwarning("page is already free.\n"); return; } if (info != MALLOC_FIRST) { wrtwarning("pointer to wrong page.\n"); return; } if ((u_long)ptr & malloc_pagemask) { wrtwarning("modified (page-) pointer.\n"); return; } /* Count how many pages and mark them free at the same time */ page_dir[index] = MALLOC_FREE; for (i = 1; page_dir[index+i] == MALLOC_FOLLOW; i++) page_dir[index + i] = MALLOC_FREE; l = i << malloc_pageshift; if (malloc_junk) memset(ptr, SOME_JUNK, l); if (malloc_hint) madvise(ptr, l, MADV_FREE); tail = (char *)ptr+l; /* add to free-list */ if (!px) px = imalloc(sizeof *pt); /* This cannot fail... */ px->page = ptr; px->end = tail; px->size = l; if (!free_list.next) { /* Nothing on free list, put this at head */ px->next = free_list.next; px->prev = &free_list; free_list.next = px; pf = px; px = 0; } else { /* Find the right spot, leave pf pointing to the modified entry. */ tail = (char *)ptr+l; for(pf = free_list.next; pf->end < ptr && pf->next; pf = pf->next) ; /* Race ahead here */ if (pf->page > tail) { /* Insert before entry */ px->next = pf; px->prev = pf->prev; pf->prev = px; px->prev->next = px; pf = px; px = 0; } else if (pf->end == ptr ) { /* Append to the previous entry */ pf->end = (char *)pf->end + l; pf->size += l; if (pf->next && pf->end == pf->next->page ) { /* And collapse the next too. */ pt = pf->next; pf->end = pt->end; pf->size += pt->size; pf->next = pt->next; if (pf->next) pf->next->prev = pf; } } else if (pf->page == tail) { /* Prepend to entry */ pf->size += l; pf->page = ptr; } else if (!pf->next) { /* Append at tail of chain */ px->next = 0; px->prev = pf; pf->next = px; pf = px; px = 0; } else { wrterror("freelist is destroyed.\n"); } } /* Return something to OS ? */ if (!pf->next && /* If we're the last one, */ pf->size > malloc_cache && /* ..and the cache is full, */ pf->end == malloc_brk && /* ..and none behind us, */ malloc_brk == sbrk(0)) { /* ..and it's OK to do... */ /* * Keep the cache intact. Notice that the '>' above guarantees that * the pf will always have at least one page afterwards. */ pf->end = (char *)pf->page + malloc_cache; pf->size = malloc_cache; brk(pf->end); malloc_brk = pf->end; index = ptr2index(pf->end); last_index = index - 1; for(i=index;i <= last_index;) page_dir[i++] = MALLOC_NOT_MINE; /* XXX: We could realloc/shrink the pagedir here I guess. */ } if (pt) ifree(pt);}/* * Free a chunk, and possibly the page it's on, if the page becomes empty. */static __inline__ voidfree_bytes(void *ptr, int index, struct pginfo *info){ int i; struct pginfo **mp; void *vp; /* Find the chunk number on the page */ i = ((u_long)ptr & malloc_pagemask) >> info->shift; if (((u_long)ptr & (info->size-1))) { wrtwarning("modified (chunk-) pointer.\n"); return; } if (info->bits[i/MALLOC_BITS] & (1<<(i%MALLOC_BITS))) { wrtwarning("chunk is already free.\n"); return; } if (malloc_junk) memset(ptr, SOME_JUNK, info->size); info->bits[i/MALLOC_BITS] |= 1<<(i%MALLOC_BITS); info->free++; mp = page_dir + info->shift; if (info->free == 1) { /* Page became non-full */ mp = page_dir + info->shift; /* Insert in address order */ while (*mp && (*mp)->next && (*mp)->next->page < info->page) mp = &(*mp)->next; info->next = *mp; *mp = info; return; } if (info->free != info->total) return; /* Find & remove this page in the queue */ while (*mp != info) { mp = &((*mp)->next);#ifdef EXTRA_SANITY if (!*mp) wrterror("(ES): Not on queue\n");#endif /* EXTRA_SANITY */ } *mp = info->next; /* Free the page & the info structure if need be */ page_dir[ptr2index(info->page)] = MALLOC_FIRST; vp = info->page; /* Order is important ! */ if(vp != (void*)info) ifree(info); ifree(vp);}static voidifree(void *ptr){ struct pginfo *info; int index; /* This is legal */ if (!ptr) return; if (!malloc_started) { wrtwarning("malloc() has never been called.\n"); return; } /* If we're already sinking, don't make matters any worse. */ if (suicide) return; index = ptr2index(ptr); if (index < malloc_pageshift) { wrtwarning("junk pointer, too low to make sense.\n"); return; } if (index > last_index) { wrtwarning("junk pointer, too high to make sense.\n"); return; } info = page_dir[index]; if (info < MALLOC_MAGIC) free_pages(ptr, index, info); else free_bytes(ptr, index, info); return;}/* * These are the public exported interface routines. */void *malloc(size_t size){ register void *r; THREAD_LOCK(); malloc_func = " in malloc():"; if (malloc_active++) { wrtwarning("recursive call.\n"); malloc_active--; return (0); } if (!malloc_started) malloc_init(); if (malloc_sysv && !size) r = 0; else r = imalloc(size); UTRACE(0, size, r); malloc_active--; THREAD_UNLOCK(); if (malloc_xmalloc && !r) wrterror("out of memory.\n"); return (r);}voidfree(void *ptr){ THREAD_LOCK(); malloc_func = " in free():"; if (malloc_active++) { wrtwarning("recursive call.\n"); malloc_active--; return; } else { ifree(ptr); UTRACE(ptr, 0, 0); } malloc_active--; THREAD_UNLOCK(); return;}void *realloc(void *ptr, size_t size){ register void *r; THREAD_LOCK(); malloc_func = " in realloc():"; if (malloc_active++) { wrtwarning("recursive call.\n"); malloc_active--; return (0); } if (ptr && !malloc_started) { wrtwarning("malloc() has never been called.\n"); ptr = 0; } if (!malloc_started) malloc_init(); if (malloc_sysv && !size) { ifree(ptr); r = 0; } else if (!ptr) { r = imalloc(size); } else { r = irealloc(ptr, size); } UTRACE(ptr, size, r); malloc_active--; THREAD_UNLOCK(); if (malloc_xmalloc && !r) wrterror("out of memory.\n"); return (r);}#endif /* !OSKIT */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?