计算机系统基础实训七-MallocLab实验

news2024/11/28 18:57:11

实验目的与要求

1、让学生理解动态内存分配的工作原理;

2、让学生应用指针、系统级编程的相关知识;

3、让学生应用各种动态内存分配器的实现方法;

实验原理与内容

(1)动态内存分配器基本原理

动态内存分配器维护着一个进程的虚拟内存区域,称为堆。分配器将堆视为一组不同大小的块的集合来维护,每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。已分配的块显式地保留为供应用程序使用。空闲块可用来分配。空闲块保持空闲,直到它显式地被应用所分配。一个已分配的块保持已分配状态,直到它被释放,这种释放要么是应用程序显式执行的,要么是内存分配器自身隐式执行的。

分配器有两种基本风格:显式分配器和隐式分配器。两种风格都要求应用显式地分配块。它们的不同之处在于由哪个实体来负责释放已分配的块。

显式动态内存分配器要求应用显式地释放任何已分配的块。例如C程序通过调用malloc函数来分配一个块,通过调用free函数来释放一个块。其中malloc采用的总体策略是:先系统调用sbrk一次,会得到一段较大的并且是连续的空间。进程把系统内核分配给自己的这段空间留着慢慢用。之后调用malloc时就从这段空间中分配,free回收时就再还回来(而不是还给系统内核)。只有当这段空间全部被分配掉时还不够用时,才再次系统调用sbrk。当然,这一次调用sbrk后内核分配给进程的空间和刚才的那块空间一般不会是相邻的。

隐式动态内存分配器也叫做垃圾收集器,例如,诸如Lisp、ML、以及Java之类的高级语言就依赖垃圾收集来释放已分配的块。

(2)隐式空闲链表

对于带边界标记的隐式空闲链表分配器,一个块是由一个字的头部、有效载荷、可能的一些额外的填充,以及在块的结尾处的一个字的脚部组成的。头部编码了这个块的大小(包括头部和所有的填充),以及这个块是已分配的还是空闲的。如果我们强加一个双字的对齐约束条件,那么块大小就总是8的倍数,且块大小的最低3位总是0。因此,我们只需要内存大小的29个高位,释放剩余的3位来编码其他信息。在这种情况中,我们用其中的最低位(已分配位)来指明这个块是已分配的还是空闲的。头部后面就是应用调用malloc时请求的有效载荷。有效载荷后面是一片不使用的填充块,其大小可以是任意的。需要填充有很多原因。比如,填充可能是分配器策略的一部分,用来对付外部碎片。或者也需要用它来满足对齐要求。  

我们将对组织为一个连续的已分配块和空闲块的序列,这种结构称为隐式空闲链表,是因为空闲块是通过头部中的大小字段隐含地连接着的。分配器可以通过遍历堆中所有的块,从而间接地遍历整个空闲块的集合。注意:此时我们需要某种特殊标记的结束块,可以是一个设置了已分配位而大小为零的终止头部。  

Knuth提出了一种边界标记技术,允许在常数时间内进行对前面块的合并。这种思想是在每个块的结尾处添加一个脚部,其中脚部就是头部的一个副本。如果每个块包括这样一个脚部,那么分配器就可以通过检查它的脚部,判断前面一个块的起始位置和状态,这个脚部总是在距当前块开始位置一个字的距离。

(3)显式空闲链表

根据定义,程序不需要一个空闲块的主体,所以实现空闲链表数据结构的指针可以存放在这些空闲块的主体里面。显式空闲链表结构将堆组织成一个双向空闲链表,在每个空闲块的主体中,都包含一个pred(前驱)和succ(后继)指针。使用双向链表而不是隐式空闲链表,使首次适配的分配时间从块总数的线性时间减少到了空闲块数量的线性时间。不过,释放一个块的时间可以是线性的,也可能是个常数,这取决于空闲链表中块的排序策略。一种方法是用后进先出(LIFO)的顺序维护链表,将新释放的块放置在链表的开始处。另一种方法是按照地址顺序来维护链表,其中链表中每个块的地址都小于它后继的地址。

在本实验中,您将使用C语言编写一个显式动态内存分配器,我们鼓励您创造性地进行设计探索,以实现正确、高效和快速的分配器。

实验设备与软件环境

1.Linux操作系统—64位 Ubuntu 18.04

2. C编译环境(gcc)

3. 计算机

实验过程与结果(可贴图)

1、初始化内存管理系统的函数mm_init

通过mem_sbrk系统调用来分配初始的内存堆大小HEAP_INITSIZE。如果分配失败,函数返回-1。成功后,它初始化管理内存的各个数据结构:设置空闲块树的根节点为NULL,表示初始时树为空;内存块链表也初始化为空;通过设置边界标签,确保内存分配不会跨越预设的堆边界;初始化块分割策略的奇偶标志;最后,将除去边界标签占用空间之外的整个初始内存区域作为一个大的空闲块加入到空闲块管理队列中。如果一切顺利,函数返回0,表示初始化成功。

 

2、初始化堆mm_free,作用是释放指向动态分配内存的指针所指向的内存空间。函数首先获取要释放块的大小,并准备合并操作。接着,检查当前块前后是否有已释放的空间:如果后方有空闲块,则将其合并至当前块并调整总大小;如果前方有空闲块,则更新起始位置、合并大小,并同样向前合并。完成相邻空闲块的合并后,更新后的大型空闲内存块会被重新登记到内存管理器的空闲列表中,等待未来的分配请求。这一过程减少了内存碎片,提高了内存分配的效率。

2、内存重分配函数*mm_realloc:负责调整给定指针ptr指向的内存块的大小到size指定的大小,同时尽量保持原有数据的完整性。

① 如果输入的指针ptr为空,函数相当于执行一次新的内存分配,调用mm_malloc(size)来分配指定大小的内存,并返回新分配的内存地址。

② 如果请求的大小size为0,意味着希望释放内存而非重新分配,因此直接调用mm_free(ptr)释放内存并返回NULL。

③ 检查当前块的大小是否已经大于等于请求的大小加上8字节(可能是为了对齐或其他内部管理需要)。如果是,直接返回原指针,无需重新分配。

④ 如果下一个内存块是空闲的,并且合并后的大小能满足新的需求,函数会合并这两个块,更新大小标签,并返回原指针。

⑤ 如果下一个块是空闲的且紧邻内存区域边界,函数尝试扩展内存堆,通过调用mem_sbrk增加内存,并更新大小标签。

⑥ 如果当前块已经是最后一个块,无法直接扩展,函数会尝试增加内存堆的大小以满足新尺寸要求,然后调整当前块的大小并返回原指针。

⑦ 如果以上条件都不满足,即不能在当前位置调整大小或扩展,函数会在内存中另找一个合适的位置(通过调用mm_malloc(size)),复制原数据到新位置,释放旧内存,然后返回新内存地址。

3、自定义的内存分配函数mm_malloc:用于从内存堆中分配指定大小的内存空间。其工作流程涉及查找合适的空闲块、内存堆扩展、块分割及内存管理等。

函数首先根据请求的大小调整并确保内存对齐,然后尝试从现有的空闲块中分配。如果找不到合适的空闲块,则通过mem_sbrk系统调用扩大内存堆。在分配到足够大的块后,若块的剩余部分大于一定阈值,则会分割该块并将剩余部分重新加入空闲块管理结构中,以优化内存使用。最后,函数会设置新分配块的标签,表示该内存已被分配,并在调试模式下打印相关信息。

实验总结

在本次计算机系统基础实验中,我深入了解并实践了动态内存分配器的工作原理,特别是显式分配器的设计与实现。通过C语言,我在Ubuntu系统上编写了具备显式内存分配器,实现了内存的高效管理,从初始化、释放、重分配、调整大小直至优化分配一整套流程,解决了内存碎片化、资源不足、泄露、分配效率低效等问题,提升整体利用,实践展现了内存对指针、系统编程、内存管理的综合应用能力,实验的成果不仅在于实现了分配器本身,更在于对内存管理的深入理解与解决复杂问题的能力提升。

/*
 *  malloc.c:  A Reasonably Efficient Best-Fit Allocator.

 */

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>

#include "memlib.h"
#include "mm.h"

/*  Team Information  */
team_t team = {
    "Tree-based free list (CMU CS 213 students,  Fall 2000)",
    "Vinny Furia",
    "vmf@andrew.cmu.edu",
    "Nick Carter",
    "nbc@andrew.cmu.edu"
};

//int split_parity = 1;

/*  Compilation Options - to be removed eventually   */
#define _DEBUG_          0     /* Prints out lots of crap */


/*  Typedefs for Heap Data Structures  */
typedef struct List {
    void** BLAH;
    struct List* Prev;
    struct List* Next;
    void** BLAH2;
} List;

typedef struct Node {
    struct Node* Left;
    struct Node* Right;
    int Color;
    struct Node* Parent;
} Node;


/*  Named Constants  */
#define HEAP_INITSIZE    8232   /* multiple of 8 */
#define HEAP_GROWSIZE    4096   /* multiple of 8 */
#define REALLOC_GROWSIZE   2048  /* multiple of 8 */
//#define REALLOC_BUBBLE   128     /* blocksize     */
//#define REALLOC_BUBBLEINCREMENT 128
#define RED              1
#define BLACK            0
#define FALSE            0


/*  Masks for Boundary Tags  */
#define SIZE_MASK        0xFFFFFFF8
#define TREE_MASK        4
#define BLOB_MASK        2
#define FREE_MASK        1
#define IN_TREE          (FREE_MASK|TREE_MASK)
#define IN_BLOB          (FREE_MASK|BLOB_MASK)
#define ALLOCATED        0


/*  Named Heap Locations  */
#define treeroot         ((Node**)(char *)mem_heap_lo())
#define blobroot         ((List**)(char *)mem_heap_lo()+1)
#define boundtag_lo      (((int*)((char *)mem_heap_lo()+16)))
#define boundtag_hi      (((int*)((char *)mem_heap_hi()-3 ))) 
#define split_parity     (*((int*)(char *)mem_heap_lo()+12))

/*  Macros for Boundary Tags  */

//  The following 2 are just for use in definitions
#define __LowTag(p)      (*((int*)(p)-1))
#define __HiPrevTag(p)   (*((int*)(p)-2))

//  The next few can be safely used with all block pointers
#define Size(p)          (__LowTag(p) & SIZE_MASK)
#define IsFree(p)        (__LowTag(p) & FREE_MASK)
#define IsInBlob(p)      (__LowTag(p) & BLOB_MASK)
#define IsInTree(p)      (__LowTag(p) & TREE_MASK)
#define NextBlock(p)     ((char *)(p) + Size(p))
#define PrevFree(p)      (__HiPrevTag(p) & FREE_MASK)
#define NextFree(p)      (IsFree(NextBlock(p)))

//  PrevBlock(p) is undefined for the first addressable block.
#define PrevBlock(p)     ((char *)(p) - (__HiPrevTag(p) & SIZE_MASK))


/*  Tree Node Macros  */
#define IsRed(p)        ((p!=NULL) && (((Node*)p)->Color == RED))




/*
 * Invariants:
 *  - The word starting at (mem_heap_lo()) is a pointer to the root
 *    node of the freetree.
 *    If this value is null, then there are no free blocks.
 *  - The word starting at (mem_heap_lo()+8) is a fake upper boundary
 *    tag, with size 0, and flagged as allocated.
 *  - The word starting at (deseg_hi-3) is a fake lower boundary
 *    tag, with size 0, and flagged as allocated.
 *  - Every block has a "front" and "rear" tag.  This tag is 4 bytes
 *    (1 word) being an integer of the size of the block.
 *    The semantics of these tags are as follows:
 *      - By masking off the least significant 3 bits, we get
 *        the size of the block in bytes.
 *      - Combinations of the last 3 bits are interpreted as follows:
 *          0 0 0  =>  Block is allocated
 *          1 1 1  =>  Block is a red node in the free tree.
 *          1 0 1  =>  Block is a black node in the free tree.
 *          0 0 1  =>  Block is on the trailing list of some
 *                     node in the free tree.
 *          0 1 1  =>  Block is free but not in the tree; instead
 *                     it is in the doubly-linked list of recently
 *                     freed nodes (the blob).
 *  - Free blocks have front and rear tags as described above, and
 *    the front tag is followed by three pointers.  These pointers are
 *    (in order):  Left Child, Right Child, List Next.  Each of these
 *    is one word in length (4 bytes).  All Free blocks will be
 *    maintained within a Red-Black tree with blocks of the same
 *    size being in an address ordered linked list.
 *  - Allocated Blocks will have front and rear tags as described
 *    previously with block data between these tags.
 *
 *  - At a
 *
 * Policies:
 *
 *  - Free blocks are coalesced immediately and added to the freetree.
 *  - Allocation is done best-fit.  When there are several candidates for
 *    the best-fit, then the lowest-addressed block is chosen.
 *
 * Conventions:
 *
 *  - Block pointers are treated as (int*)'s
 */

void setTags (void* block, size_t size, int flags)
{
    int* tag1 = (int*)block - 1;
    int* tag2 = (int*)((char *)block+size)-2;
    *tag1 = *tag2 = (size | flags);
}


/*
 * void left_rotate(Node* x)
 *
 *       x        -->        y
 *      / \                 / \
 *     T1  y      -->      x  T3
 *        / \             / \
 *       T2 T3    -->    T1 T2
 *
 * Precondition: x must be non-null with a non-null right child.
 */
void left_rotate (Node* x) {
    Node* y = x->Right;

    x->Right = y->Left;
    if ( y->Left != NULL )
	y->Left->Parent = x;

    //Set the parent to point to y instead of x
    y->Parent = x->Parent;
    if ( x->Parent == NULL )
	*treeroot = y;
    else
	if ( x == x->Parent->Left )
	    //x was on the left of its parent
	    x->Parent->Left = y;
	else
	    //x must have been on the right
	    x->Parent->Right = y;

    y->Left = x;
    x->Parent = y;
}

/*
 * void right_rotate(Node* x)
 *
 *           x      -->       y
 *          / \              / \
 *         y  T3    -->     T1  x
 *        / \                  / \   
 *       T1 T2      -->       T2 T3
 *
 * Precondition: x must be non-null with a non-null left child.
 */
void right_rotate (Node* x) {
    Node* y = x->Left;
    
    x->Left = y->Right;
    if (y->Right != NULL)
	y->Right->Parent = x;
    
    y->Parent = x->Parent;
    
    // Set the parent to point to y instead of x
    if (x->Parent == NULL) 
	*treeroot = y;
    else
	if (x == x->Parent->Left)
	    // x was on the left of its parent
	    x->Parent->Left = y;
	else
	    // x must have been on the right
	    x->Parent->Right = y;
    
    y->Right = x;
    x->Parent = y;
}

/*  Color Functions  */
int isRed (Node* x) {
    return (x != NULL) && (x->Color == RED);
}

int isBlack (Node* x) {
    return (x == NULL) || (x->Color == BLACK);
}

void setblack (Node* x) {
    if (x!=NULL)
	x->Color = BLACK;
}

void setred (Node* x) {
    // NULL nodes are never red.
    x->Color = RED;
}

/*
 * int isLess(Node* x, Node* y)
 *
 * This expression defines the order by which
 * the tree is constructed.  x and y may not be NULL.
 *
 */
int isLess (Node* x, Node *y) {
    return (Size(x) < Size(y)) || ((Size(x) == Size(y)) && x<y);
}

/*
 * int tree_insert(Node* x)
 *
 * Preconditions:
 *     - x should be an established node
 *       (i.e. size flags, IN_TREE set) that
 *       does not appear already in the tree.
 *
 * Postconditions:
 *     Inserts the node into the tree as follows:
 *     - If the tree is currently empty, then we set x
 *       to be the root, color it black, and return 0.
 *     - Otherwise, we insert x as a leaf, color it red,
 *       and return 1.
 *
 */
int tree_insert (Node* x) {
  Node* current = *treeroot;

  //Empty tree --> update root pointer
  if (current == NULL) {
      *treeroot = x;
      x->Parent = NULL;
      x->Right = x->Left = NULL;
      x->Color = BLACK;
      return 0;
  }

  //Non-empty tree --> insert as leaf
  while(1) {
      if (isLess(x,current)) {
	  //x belongs in the left child of current node
	  if (current->Left != NULL)
	      current = current->Left;
	  else {
	      current->Left = x;
	      x->Parent = current;
	      x->Right = x->Left = NULL;
	      x->Color = RED;
	      return 1; 
	  }
      }
      else {
	  //x belongs in the right child of current node
	  if (current->Right != NULL)
	      current = current->Right;
	  else {
	      current->Right = x;
	      x->Parent = current;
	      x->Right = x->Left = NULL;
	      x->Color = RED;
	      return 1;
	  }
      }
  }
}

void freetree_insert (void* ptr, size_t size) {
    Node* x;
    Node* y;

    //Write the tags for the block
    setTags(ptr, size, IN_TREE);
    x = (Node*)ptr;

    //Do a tree insertion
    if (tree_insert(x) == 0){
	// No further work needed.
	return;
    }
    
    /* Move up the tree to restore the red/black property.       */
    /* Invariant: x is red, and red/black properties are every-  */
    /*            where satisfied, except maybe between x and    */
    //            x->Parent.                                     */
    while ( (x->Parent != NULL)
	    && (isRed(x->Parent))
	    && (x->Parent->Parent != NULL)) {
	if ( x->Parent == x->Parent->Parent->Left ) {
	    /* If x's parent is a left, y is x's right 'uncle' */
	    y = x->Parent->Parent->Right;
	    if ( isRed(y) ) {
		/* case 1 - change the colours */
		setblack(x->Parent);  
		setblack(y);
		setred(x->Parent->Parent);
		/* Move x up the tree */
		x = x->Parent->Parent;
	    }
	    else {
		/* y is a black node */
		if ( x == x->Parent->Right ) {
		    /* and x is to the right */ 
		    /* double-rotate . . .  */
		    left_rotate( x->Parent );
		    right_rotate( x->Parent );
		    setblack( x->Left );
		}
		else
		{
		    /* single-rotate */
		    setblack(x);
		    x = x->Parent;
		    right_rotate( x->Parent );
		}
	    }
	}
	else {
	    /* If x's parent is a right, y is x's left 'uncle' */
	    y = x->Parent->Parent->Left;
	    if ( isRed(y) ) {
		/* case 1 - change the colours */
		setblack(x->Parent);
		setblack(y);
		setred(x->Parent->Parent);
		/* Move x up the tree */
		x = x->Parent->Parent;
	    }
	    else {
		/* y is a black node */
		if ( x == x->Parent->Left ) {
		    /* and x is to the left */
		    /* double rotate */
		    right_rotate( x->Parent );
		    left_rotate( x->Parent );
		    setblack(x->Right);
		}
		else {
		    /* single rotate */
		    setblack(x);
		    x = x->Parent;
		    left_rotate( x->Parent );
		}
	    }
	}
    }
    /* Colour the root black */
    setblack(*treeroot);
}


Node* freetree_locate(int size) {
    Node* best = NULL;
    Node* current = *treeroot;

    //Find the smallest (with respect to tree-order) element
    //for which size <= Size(current), assuming that size-
    //insufficiency is preserved by the tree-order.
    while(current != NULL) {
	if (size <= Size(current)) {
	    best = current;
	    current = current->Left;
	}
	else
	    current = current->Right;
    }
    return best;
}

int freetree_locatemax()
{
    Node* n = *treeroot;
    if (n == NULL)
	return 0;
    else
    {
	while (n->Right)
	    n = n->Right;
    }
    return Size(n);
}


void left_child_is2x(Node* x);
void right_child_is2x(Node* x);

//left child is a double-black node.  Fix it.
void left_child_is2x(Node* x){
    Node* sis = x->Right;

    if (sis->Color == RED)
    {
	left_rotate(x);
	x->Color = !(x->Color);
	sis->Color = !(sis->Color);
	sis = x->Right;
    }

    //Now sis is black.  Let's check its children.
    if (isBlack(sis->Right) && isBlack(sis->Left))
    {
	sis->Color = RED;
	if (x->Color == RED)
	{
	    x->Color = BLACK;
	    return;  //done!
	}
	else
	{
	    //move violation up to parent, if any.
	    //if node is root, it's already black, and we're done.
	    if (x->Parent != NULL)
	    {
		if (x->Parent->Left == x)
		    left_child_is2x(x->Parent);
		else
		    right_child_is2x(x->Parent);
	    }
	    return;
	}
    }

    if (isBlack(sis->Right))  //farther child is black
    {
	//make it so that the farther child is red
	right_rotate(sis);
	sis->Color = RED;   //used to be black, old sis
	sis = x->Right;
	sis->Color = BLACK;  //used to be red.  New sis
    }

    //now we know that sis->Right is red. This is fixable.
    left_rotate(x);
    sis->Color = x->Color;      //just to copy.
    x->Color = BLACK;           //was indeterminate.
    sis->Right->Color = BLACK;  //was red.
    return;
}


void right_child_is2x(Node* x){
    Node* sis = x->Left;

    if (sis->Color == RED)
    {
	right_rotate(x);
	x->Color = !(x->Color);
	sis->Color = !(sis->Color);
	sis = x->Left;
    }

    //Now sis is black.  Let's check its children.
    if (isBlack(sis->Right) && isBlack(sis->Left))
    {
	sis->Color = RED;
	if (x->Color == RED)
	{
	    x->Color = BLACK;
	    return;  //done!
	}
	else
	{
	    //move violation up to parent, if any.
	    //if node is root, it's already black, and we're done.
	    if (x->Parent != NULL)
	    {
		if (x->Parent->Left == x)
		    left_child_is2x(x->Parent);
		else
		    right_child_is2x(x->Parent);
	    }
	    return;
	}
    }

    if (isBlack(sis->Left))  //farther child is black
    {
	//make it so that the farther child is red
	left_rotate(sis);
	sis->Color = RED;   //used to be black, old sis
	sis = x->Left;
	sis->Color = BLACK;  //used to be red.  New sis
    }

    //now we know that sis->Left is red. This is fixable.
    right_rotate(x);
    sis->Color = x->Color;      //just to copy.
    x->Color = BLACK;           //was indeterminate.
    sis->Left->Color = BLACK;   //was red.
    return;
}

void freetree_delete( Node* z ) {
    
    /*****************************
     *  delete node z from tree  *
     *****************************/

    if (z == NULL)
	return;
    
    if ((z->Left == NULL || z->Right == NULL) && z->Color == RED)
    {
	Node* child = z->Left ? z->Left : z->Right;  //is black

	if (child)
	    child->Parent = z->Parent;
	
	if (z->Parent == NULL)
	    *treeroot = child;
	else if (z->Parent->Left == z)
	    z->Parent->Left = child;
	else
	    z->Parent->Right = child;
	return;
    }
    else if ((z->Left == NULL || z->Right == NULL) && z->Color == BLACK)
    {
	Node* child = z->Left ? z->Left : z->Right;
	if (child)
	    child->Parent = z->Parent;

	if (z->Parent == NULL)
	{
	    *treeroot = child;
	    setblack(child);
	    return;
	}
	else if (z->Parent->Left == z)
	{
	    z->Parent->Left = child;
	    left_child_is2x(z->Parent);
	    return;
	}
	else
	{
	    z->Parent->Right = child;
	    right_child_is2x(z->Parent);
	    return;
	}
    }
    else if (z->Right->Left == NULL && z->Right->Color == RED)
    {
	//We know that z->Left is non-null
	z->Right->Left = z->Left;
	z->Left->Parent = z->Right;
	z->Right->Parent = z->Parent;
	z->Right->Color = BLACK;

	if (z->Parent == NULL)
	    *treeroot = z->Right;
	else if (z->Parent->Left == z)
	    z->Parent->Left = z->Right;
	else
	    z->Parent->Right = z->Right;
	return;
    }
    else if (z->Right->Left == NULL && z->Right->Color == BLACK)
    {
	z->Right->Left = z->Left;
	z->Left->Parent = z->Right;
	z->Right->Parent = z->Parent;
	z->Right->Color = z->Color;

	if (z->Parent == NULL)
	    *treeroot = z->Right;
	else if (z->Parent->Left == z)
	    z->Parent->Left = z->Right;
	else
	    z->Parent->Right = z->Right;

	right_child_is2x(z->Right);
	return;
    }
    else
    {
	Node* y = z->Right->Left;
	Node  y2;
	while (y->Left)
	    y = y->Left;

	y2 = *y;
	*y = *z;
	if (z->Parent == NULL)
	    *treeroot = y;
	else if (z->Parent->Left == z)
	    z->Parent->Left = y;
	else
	    z->Parent->Right = y;
	y->Left->Parent = y;
	y->Right->Parent = y;

	//now y has replaced z.  Clean up y2, where y used to be.
	y2.Parent->Left = y2.Right;
	if (y2.Right)
	    y2.Right->Parent = y2.Parent;
	if (y2.Color == RED)
	    return;
	else
	{
	    left_child_is2x(y2.Parent);
	    return;
	}
    }
}





















/*
 * Invariant: ptr points to a block that is free.
 * Afterwards, the pointer will be deleted from
 * the relevant data structures, but its tags
 * will not reflect the change.
 */
void delFromWherever (void *ptr)
{
    if (IsInTree(ptr))
	freetree_delete(ptr);
    else if (IsInBlob(ptr))
    {
	//The node is in the blob.  Remove it in O(1) time.
	List* L = ptr;

	if (L->Next != NULL)
	    L->Next->Prev = L->Prev;
	if (L->Prev)
	    L->Prev->Next = L->Next;
	else
	    *blobroot = L->Next;
    }
}

//Takes a block, sets its tags, and puts it in the blob
void queueNewFreeBlock(void* ptr, int size)
{
    //Mark and insert into the blob.
    List* L = ptr;
    
    setTags(ptr, size, IN_BLOB);
    L->Prev = NULL;
    L->Next = *blobroot;
    if (L->Next)
	L->Next->Prev = L;
    *blobroot = L;
}

//takes all items from the blob and inserts into the freetree
void emptyblob()
{
    /*  Move all blob-blocks into the tree  */
    List* N = *blobroot;
    while (N!=NULL)
    {
        List* temp = N->Next;
        freetree_insert(N,Size(N));
        N = temp;
    }
    *blobroot = NULL;
}

int mm_init(void)
{
    // 获取初始所需的内存空间
    if (mem_sbrk(HEAP_INITSIZE) == (void *)-1)
        return -1; // 如果申请失败,返回-1

    // 初始化时,空闲块树中有一个自由块
    *treeroot = NULL; 

    // 内存块链表(Blob)最初为空
    *blobroot = NULL;

    // 设置边界标签,防止内存块与堆边界合并
    *boundtag_lo = 0;
    *boundtag_hi = 0;

    // 初始化块分割的奇偶标志
    split_parity = 0;

    // 将除边界标签外的初始内存区域加入到空闲块队列中,作为一大块可用内存
    queueNewFreeBlock(boundtag_lo + 2, HEAP_INITSIZE - 24);

    return 0; // 初始化成功,返回0
}

void mm_free (void *ptr)
{
    //内存块的合并
    int new_size     = Size(ptr);
    void *new_block  = ptr;
    void *prev_block;
    void *next_block;
    if (NextFree(ptr))
    {
	next_block = NextBlock(ptr);
	new_size += Size(next_block);
	delFromWherever(next_block);
    }
    if (PrevFree(ptr))
    {
	prev_block = PrevBlock(ptr);
	new_size += Size(prev_block);
	new_block = prev_block;
	delFromWherever(prev_block);
    }
    //加入到空闲内存块的管理队列
    queueNewFreeBlock(new_block,new_size);
}


void *mm_realloc(void *ptr, size_t size)
{
    void* next_block = NextBlock(ptr); // 获取当前块的下一个内存块
    void* ptr2;
    
    // 如果ptr为NULL,表现如malloc,分配指定大小的内存
    if (ptr == NULL) {
        return mm_malloc(size);
    }
    
    // 如果请求的大小为0,释放原内存并返回NULL
    if (size == 0) {
        mm_free(ptr);
        return NULL;
    }

    // 如果当前块大小足够,直接返回原指针
    if (Size(ptr) >= (size + 8)) {
        return ptr;
    }
        
    // 检查是否能通过合并下一个空闲块满足新大小需求
    if (IsFree(next_block) && (Size(next_block) + Size(ptr) >= size + 8)) {
        size = Size(next_block) + Size(ptr); // 合并两个块的大小
        delFromWherever(next_block); // 从管理结构中移除下一个块
        setTags(ptr, size, 0); // 更新当前块的大小和状态标签
        return ptr;
    }
    
    // 尝试扩展到下一个块之后,如果它是最后一个块
    if (IsFree(next_block) && (NextBlock(next_block) > (char *)boundtag_hi)) {
        delFromWherever(next_block); // 移除下一个空闲块的记录
        setTags(ptr, Size(ptr) + Size(next_block), 0); // 扩展当前块的大小
        next_block = NextBlock(next_block); // 移动到原本的下一个块之后的位置
    }
    
    // 如果当前块已是最后一块
    if (next_block > (void *)boundtag_hi) {
        // 计算最小扩展大小并留出额外增长空间
        int min_size = ((size + 15) & -8) + 8;
        int grow_size = min_size - Size(ptr) + REALLOC_GROWSIZE;
        grow_size &= REALLOC_GROWSIZE; // 确保增长量符合特定对齐或策略
        
        // 尝试扩展内存堆
        if (mem_sbrk(grow_size) == (void *)-1) {
            // 扩展失败,返回NULL
            return NULL;
        }
        *boundtag_hi = 0; // 更新边界标记
        
        setTags(ptr, Size(ptr) + grow_size, ALLOCATED); // 更新块的大小和状态
        return ptr;
    }
    
    // 如果上述条件都不满足,需要分配新块并复制数据
    ptr2 = mm_malloc(size);
    memcpy(ptr2, ptr, Size(ptr) - 8); // 复制数据(注意:实际拷贝大小可能需调整)
    mm_free(ptr); // 释放原内存
    
    #if _DEBUG_
        // 调试输出
        printf("REALLOC: 原内存位于%p,新内存位于%p...\n", ptr, ptr2);
        printAllBlocks(); // 打印所有内存块信息
    #endif //_DEBUG_
    
    return ptr2; // 返回新分配的内存地址
}


void *mm_malloc (size_t size)
{
    void* block; // 指向分配的内存块
    int block_size; // 分配块的总大小
    void* leftover_block = NULL; // 用于存储分割后剩余的内存块
    int leftover_size; // 剩余内存块的大小

    size = (size+7)&-8;    //将请求的大小调整为8字节对齐,并加上8字节用于内部管理标签
    size += 8;   
    // 确保请求的大小至少为24字节
    if (size < 24)
        size = 24;  
    //printf("malloc(0x%x)\n",size);

    // 清空或重置内存管理中的某个数据结构(blob)
    emptyblob();
    
    // 在空闲块树中查找适合分配的块
    block = freetree_locate(size);
    
// 如果未找到合适的块
    if (block == NULL)
    {
        // 扩展堆内存
        block = (char *)mem_heap_hi()+1; // 新块起始位置
        block_size = size; // 请求的大小
        block_size += HEAP_GROWSIZE; // 额外增加预设的堆增长量
        block_size &= -HEAP_GROWSIZE;  // 确保增长后的大小满足特定对齐要求
        // 尝试通过系统调用增加堆内存
        if (mem_sbrk(block_size) == (void *)-1)
        {
             // 内存不足,无法满足请求
            return NULL;
        }
        *boundtag_hi = 0; //*((int*)(mem_heap_hi()-3)) = NULL;      // 更新堆的上界标记
      // 如果新块前面的块也是空闲的,则合并这两个块
        if (IsFree(PrevBlock(block)))
        {
            block = PrevBlock(block);
            block_size += Size(block);
            delFromWherever(block); // 从管理结构中移除已合并的前一块
        }
    }
    else // 找到了合适的空闲块
    {
        block_size = Size(block); // 获取找到的块的大小
        delFromWherever(block); // 从空闲列表中移除这个块
    }

     // 如果分配的块比实际需要的大很多(超过24字节),考虑分割
    if ((block_size - size)>24)  
    {
         // 根据split_parity策略决定分割方向
        if (split_parity)
        {
            leftover_size  = block_size - size;
            block_size     = size;   // 分割后当前块的大小
            leftover_block = (char *)block + block_size;  // 分割后剩余部分的地址
            queueNewFreeBlock(leftover_block,leftover_size); // 剩余部分重新加入空闲块管理
        }
        else
        {
            leftover_size = block_size - size;
            block_size    = size;
            leftover_block = (char *)block + leftover_size; 
            queueNewFreeBlock(block,leftover_size); // 当前块成为剩余部分,重新加入管理
            block = leftover_block; // 更新block为实际分配的块地址
        }
        split_parity = !split_parity; // 切换下一次分割的方向
    }
    
// 设置块的大小和分配状态标签
    setTags(block,block_size,0); 
    //printBlock(block);

// 调试代码:打印堆信息、所有块信息及红黑树验证
#if _DEBUG_
    if (printHeap());
    printAllBlocks();
    isRedBlack();
#endif //_DEBUG_
    // 返回分配的内存块地址
    return block;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1851328.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python入门基础知识(错误和异常)

本文部分内容来自菜鸟教程Python 基础教程 | 菜鸟教程 (runoob.com) 本人负责概括总结代码实现。 以此达到快速复习目的 目录 语法错误 异常 异常处理 try/except try/except...else try-finally 语句 抛出异常 用户自定义异常 内置异常类型 常见的标准异常类型 语法…

Java赋值运算符

Java赋值运算符分为以下&#xff1a; 符号 作用 说明 赋值 int a 10,把10赋值给变量a 加后赋值 ab,将ab的值赋值给变量a - 减后赋值 a-b,将a-b的值赋值给变量a* 乘后赋值 a*b,将a*b的值赋值给变量a / 除后赋值 a/b,将a/b的值赋值给变量a % 取余赋值 a%b,将a%b的值赋值给变量…

全面国产化之路-信创

概叙 信创&#xff0c;即信息技术应用创新产业&#xff0c;这个词儿最早来源于“信创工委会”&#xff08;全称是信息技术应用创新工作委员会&#xff09;&#xff0c;是在2016年由24家专业从事软硬件关键技术研究及应用的国内单位共同发起成立的一个非营利性社会组织。后…

qt+halcon实战

注意建QT工程项目用的是MSVC&#xff0c;如果选成MinGW,则会报错 INCLUDEPATH $$PWD/include INCLUDEPATH $$PWD/include/halconcppLIBS $$PWD/lib/x64-win64/halconcpp.lib LIBS $$PWD/lib/x64-win64/halcon.lib#include "halconcpp/HalconCpp.h" #include &quo…

一个漂亮的网站收藏函数

<!DOCTYPE html> <html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>网站收藏</title><style>body …

会自动清除的文件——tempfile

原文链接&#xff1a;http://www.juzicode.com/python-tutorial-tempfile/ 在某些不需要持久保存文件的场景下&#xff0c;可以用tempfile模块生成临时文件或者文件夹&#xff0c;这些临时文件或者文件夹在使用完之后就会自动删除。 NamedTemporaryFile用来创建临时文件&…

Gradle学习-1

1、APK构建流程 2、Gradle的安装 &#xff08;1&#xff09;安装Java JDK JAVA JDK 下载地址下载安装后需要配置环境变量gradle是运行在Java虚拟机上的&#xff0c;所以需要配置Java JDK &#xff08;2&#xff09;安装 Gradle Gradle下载官网下载安装后需要配置环境变量 …

跨行业数据资产共享与协同:构建一体化数据共享平台,解锁数据资产潜力,促进多行业数据流通与深度应用,共创数字化转型新篇章,引领行业发展新趋势,开启智慧互联新纪元

一、引言 随着信息技术的飞速发展&#xff0c;数据已成为推动社会进步和经济发展的关键要素。然而&#xff0c;在传统行业领域&#xff0c;数据往往被限制在各自的“孤岛”中&#xff0c;难以实现跨行业的流通与共享。这不仅限制了数据的价值发挥&#xff0c;也阻碍了行业的创…

show/hide信号演示

代码&#xff1a; #include <gtk-2.0/gtk/gtk.h> #include <gtk-2.0/gdk/gdkkeysyms.h> #include <glib-2.0/glib.h> #include <stdio.h>gint delete_event(GtkWidget *window, GdkEvent *event, gpointer data) {gtk_widget_hide(window);return TRU…

Vue78-缓存路由组件

一、需求 路由切走的时候&#xff0c;组件会被销毁&#xff0c;路由切回来&#xff0c;组件被挂载&#xff01; 需要&#xff1a;路由切走的时候&#xff0c;组件不会被销毁。 二、代码实现 若是不加include属性&#xff0c;则在<router-view>里面展示的路由&#xff0c…

【一】【算法】经典树状数组和并查集,详细解析,初步认识,【模板】树状数组 1,树状数组并查集

【模板】树状数组 1 题目描述 如题&#xff0c;已知一个数列&#xff0c;你需要进行下面两种操作&#xff1a; 将某一个数加上 x x x 求出某区间每一个数的和 输入格式 第一行包含两个正整数 n , m n,m n,m&#xff0c;分别表示该数列数字的个数和操作的总个数。 第二…

webpack安装sass

package.json文件 {"devDependencies": {"sass-loader": "^7.2.0","sass": "^1.22.10","fibers": "^4.0.1"} }这个不用webpack.config.js module.exports {module: {rules: [{test: /\.s[ac]ss$/i,u…

日元预计明年开始上涨

被称为“日元先生”的前大藏省&#xff08;现财务省&#xff09;财务官榊原英资预测&#xff0c;美元兑日元汇率将在今年底或2025年初逐步升至130。他认为&#xff0c;通缩时代已经过去&#xff0c;通货膨胀即将来临。 《日本经济新闻》6月5日报道&#xff0c;日本财务省于5月3…

AI大眼萌探索 AI 新世界:Ollama 使用指南【1】

在人工智能的浪潮中&#xff0c;Ollama 的出现无疑为 Windows 用户带来了一场革命。这款工具平台以其开创性的功能&#xff0c;简化了 AI 模型的开发与应用&#xff0c;让每一位爱好者都能轻松驾驭 AI 的强大力量。大家好&#xff0c;我是AI大眼萌&#xff0c;今天我们将带大家…

全志 Android 11:实现响应全局按键

一、篇头 最近实现热键想功能&#xff0c;简单总结了下全志平台Android 11 的响应全局热键的方法。 二、需求 实现全局热键&#xff0c;响应F-、AF、F三个按键&#xff0c;AF只用于启动调焦界面&#xff0c;F-和F除了可以启动调焦界面外&#xff0c;还用于调整镜头的焦距&…

RN组件库 - Button 组件

从零构建 React Native 组件库&#xff0c;作为一个前端er~谁不想拥有一个自己的组件库呢 1、定义 Button 基本类型 type.ts import type {StyleProp, TextStyle, ViewProps} from react-native; import type {TouchableOpacityProps} from ../TouchableOpacity/type; import…

【活动】TSRC反爬虫专项正式启动!

活动时间 即日起 ~ 2024年7月5日 18:00 测试范围&#xff1a;微信公众号、腾讯新闻等 测试域名&#xff1a;mp.weixin.qq.com 微信公众号相关接口 1. 微信公众号文章列表 2. 历史文章 3. 文章详细内容 注&#xff1a;详情报名后公布。反爬虫专项将不定期上线新业务&#xf…

Java比较运算符

关系运算符和比较运算符适用于条件判断类型。 相当于布尔值&#xff0c;只有True和False两个 符号 说明ab,判断a的值是否等于b的值&#xff0c;条件成立为true,不成立为false ! a!b,判断a和b的值是否不相等&#xff0c;条件成立为true,不成立为false > …

【字符串 状态机动态规划】1320. 二指输入的的最小距离

本文涉及知识点 动态规划汇总 字符串 状态机动态规划 LeetCode1320. 二指输入的的最小距离 二指输入法定制键盘在 X-Y 平面上的布局如上图所示&#xff0c;其中每个大写英文字母都位于某个坐标处。 例如字母 A 位于坐标 (0,0)&#xff0c;字母 B 位于坐标 (0,1)&#xff0…

java之url任意跳转漏洞

1 漏洞介绍 URLRedirect url重定向漏洞也称url任意跳转漏洞&#xff0c;网站信任了用户的输入导致恶意攻击&#xff0c;url重定向主要用来钓鱼&#xff0c;比如url跳转中最常见的跳转在登陆口&#xff0c;支付口&#xff0c;也就是一旦登陆将会跳转任意自己构造的网站&#xf…