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 + -
显示快捷键?