kern_malloc.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 754 行 · 第 1/2 页
C
754 行
/* $NetBSD: kern_malloc.c,v 1.55 2000/11/24 00:34:32 chs Exp $ *//* * Copyright (c) 1996 Christopher G. Demetriou. All rights reserved. * Copyright (c) 1987, 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)kern_malloc.c 8.4 (Berkeley) 5/20/95 */#include "opt_lockdebug.h"#include <sys/param.h>#include <sys/proc.h>#include <sys/map.h>#include <sys/kernel.h>#include <sys/malloc.h>#include <sys/systm.h>#include <uvm/uvm_extern.h>static struct vm_map_intrsafe kmem_map_store;vm_map_t kmem_map = NULL;#include "opt_kmempages.h"#ifdef NKMEMCLUSTERS#error NKMEMCLUSTERS is obsolete; remove it from your kernel config file and use NKMEMPAGES instead or let the kernel auto-size#endif/* * Default number of pages in kmem_map. We attempt to calculate this * at run-time, but allow it to be either patched or set in the kernel * config file. */#ifndef NKMEMPAGES#define NKMEMPAGES 0#endifint nkmempages = NKMEMPAGES;/* * Defaults for lower- and upper-bounds for the kmem_map page count. * Can be overridden by kernel config options. */#ifndef NKMEMPAGES_MIN#define NKMEMPAGES_MIN NKMEMPAGES_MIN_DEFAULT#endif#ifndef NKMEMPAGES_MAX#define NKMEMPAGES_MAX NKMEMPAGES_MAX_DEFAULT#endif#include "opt_kmemstats.h"#include "opt_malloclog.h"#ifdef OSKIT#include <oskit/gdb.h>#include "oskit_uvm_internal.h"#endifstruct kmembuckets bucket[MINBUCKET + 16];struct kmemstats kmemstats[M_LAST];struct kmemusage *kmemusage;char *kmembase, *kmemlimit;const char *memname[] = INITKMEMNAMES;#ifdef MALLOCLOG#ifndef MALLOCLOGSIZE#define MALLOCLOGSIZE 100000#endifstruct malloclog { void *addr; long size; int type; int action; const char *file; long line;} malloclog[MALLOCLOGSIZE];long malloclogptr;static void domlog __P((void *a, long size, int type, int action, const char *file, long line));static void hitmlog __P((void *a));static voiddomlog(a, size, type, action, file, line) void *a; long size; int type; int action; const char *file; long line;{ malloclog[malloclogptr].addr = a; malloclog[malloclogptr].size = size; malloclog[malloclogptr].type = type; malloclog[malloclogptr].action = action; malloclog[malloclogptr].file = file; malloclog[malloclogptr].line = line; malloclogptr++; if (malloclogptr >= MALLOCLOGSIZE) malloclogptr = 0;}static voidhitmlog(a) void *a;{ struct malloclog *lp; long l;#define PRT \ if (malloclog[l].addr == a && malloclog[l].action) { \ lp = &malloclog[l]; \ printf("malloc log entry %ld:\n", l); \ printf("\taddr = %p\n", lp->addr); \ printf("\tsize = %ld\n", lp->size); \ printf("\ttype = %s\n", memname[lp->type]); \ printf("\taction = %s\n", lp->action == 1 ? "alloc" : "free"); \ printf("\tfile = %s\n", lp->file); \ printf("\tline = %ld\n", lp->line); \ } for (l = malloclogptr; l < MALLOCLOGSIZE; l++) PRT for (l = 0; l < malloclogptr; l++) PRT}#endif /* MALLOCLOG */#ifdef DIAGNOSTIC/* * This structure provides a set of masks to catch unaligned frees. */long addrmask[] = { 0, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff,};/* * The WEIRD_ADDR is used as known text to copy into free objects so * that modifications after frees can be detected. */#define WEIRD_ADDR ((unsigned) 0xdeadbeef)#ifdef DEBUG#define MAX_COPY PAGE_SIZE#else#define MAX_COPY 32#endif/* * Normally the freelist structure is used only to hold the list pointer * for free objects. However, when running with diagnostics, the first * 8 bytes of the structure is unused except for diagnostic information, * and the free list pointer is at offst 8 in the structure. Since the * first 8 bytes is the portion of the structure most often modified, this * helps to detect memory reuse problems and avoid free list corruption. */struct freelist { int32_t spare0; int16_t type; int16_t spare1; caddr_t next;};#else /* !DIAGNOSTIC */struct freelist { caddr_t next;};#endif /* DIAGNOSTIC *//* * Allocate a block of memory */#ifdef MALLOCLOGvoid *_malloc(size, type, flags, file, line) unsigned long size; int type, flags; const char *file; long line;#elsevoid *malloc(size, type, flags) unsigned long size; int type, flags;#endif /* MALLOCLOG */{ struct kmembuckets *kbp; struct kmemusage *kup; struct freelist *freep; long indx, npg, allocsize; int s; caddr_t va, cp, savedlist;#ifdef DIAGNOSTIC int32_t *end, *lp; int copysize; const char *savedtype;#endif#ifdef KMEMSTATS struct kmemstats *ksp = &kmemstats[type]; if (__predict_false(((unsigned long)type) > M_LAST)) panic("malloc - bogus type");#endif indx = BUCKETINDX(size); kbp = &bucket[indx]; s = splmem();#ifdef KMEMSTATS while (ksp->ks_memuse >= ksp->ks_limit) { if (flags & M_NOWAIT) { splx(s); return ((void *) NULL); } if (ksp->ks_limblocks < 65535) ksp->ks_limblocks++; tsleep((caddr_t)ksp, PSWP+2, memname[type], 0); } ksp->ks_size |= 1 << indx;#endif#ifdef DIAGNOSTIC copysize = 1 << indx < MAX_COPY ? 1 << indx : MAX_COPY;#endif if (kbp->kb_next == NULL) { kbp->kb_last = NULL; if (size > MAXALLOCSAVE) allocsize = roundup(size, PAGE_SIZE); else allocsize = 1 << indx; npg = btoc(allocsize); va = (caddr_t) uvm_km_kmemalloc(kmem_map, uvmexp.kmem_object, (vsize_t)ctob(npg), (flags & M_NOWAIT) ? UVM_KMF_NOWAIT : 0); if (__predict_false(va == NULL)) { /* * Kmem_malloc() can return NULL, even if it can * wait, if there is no map space avaiable, because * it can't fix that problem. Neither can we, * right now. (We should release pages which * are completely free and which are in buckets * with too many free elements.) */ if ((flags & M_NOWAIT) == 0) panic("malloc: out of space in kmem_map"); splx(s); return ((void *) NULL); }#ifdef KMEMSTATS kbp->kb_total += kbp->kb_elmpercl;#endif kup = btokup(va); kup->ku_indx = indx; if (allocsize > MAXALLOCSAVE) { if (npg > 65535) panic("malloc: allocation too large"); kup->ku_pagecnt = npg;#ifdef KMEMSTATS ksp->ks_memuse += allocsize;#endif goto out; }#ifdef KMEMSTATS kup->ku_freecnt = kbp->kb_elmpercl; kbp->kb_totalfree += kbp->kb_elmpercl;#endif /* * Just in case we blocked while allocating memory, * and someone else also allocated memory for this * bucket, don't assume the list is still empty. */ savedlist = kbp->kb_next; kbp->kb_next = cp = va + (npg << PAGE_SHIFT) - allocsize; for (;;) { freep = (struct freelist *)cp;#ifdef DIAGNOSTIC /* * Copy in known text to detect modification * after freeing. */ end = (int32_t *)&cp[copysize]; for (lp = (int32_t *)cp; lp < end; lp++) *lp = WEIRD_ADDR; freep->type = M_FREE;#endif /* DIAGNOSTIC */ if (cp <= va) break; cp -= allocsize; freep->next = cp; } freep->next = savedlist; if (kbp->kb_last == NULL) kbp->kb_last = (caddr_t)freep; } va = kbp->kb_next; kbp->kb_next = ((struct freelist *)va)->next;#ifdef DIAGNOSTIC freep = (struct freelist *)va; savedtype = (unsigned)freep->type < M_LAST ? memname[freep->type] : "???"; if (kbp->kb_next) { int rv; vaddr_t addr = (vaddr_t)kbp->kb_next; vm_map_lock(kmem_map); rv = uvm_map_checkprot(kmem_map, addr, addr + sizeof(struct freelist), VM_PROT_WRITE); vm_map_unlock(kmem_map); if (__predict_false(rv == 0)) {#ifdef OSKIT gdb_breakpoint();#endif printf( "%s %ld of object %p size %ld %s %s (invalid addr %p)\n", "Data modified on freelist: word", (long)((int32_t *)&kbp->kb_next - (int32_t *)kbp), va, size, "previous type", savedtype, kbp->kb_next);#ifdef MALLOCLOG hitmlog(va);#endif kbp->kb_next = NULL; } } /* Fill the fields that we've used with WEIRD_ADDR */#if BYTE_ORDER == BIG_ENDIAN freep->type = WEIRD_ADDR >> 16;#endif#if BYTE_ORDER == LITTLE_ENDIAN freep->type = (short)WEIRD_ADDR;#endif end = (int32_t *)&freep->next + (sizeof(freep->next) / sizeof(int32_t)); for (lp = (int32_t *)&freep->next; lp < end; lp++) *lp = WEIRD_ADDR; /* and check that the data hasn't been modified. */ end = (int32_t *)&va[copysize]; for (lp = (int32_t *)va; lp < end; lp++) { if (__predict_true(*lp == WEIRD_ADDR)) continue;#ifdef OSKIT
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?