/* * Find bitmap of according memory region 查找相应内存区域的位图 * This is a linear search but it is expected that we have only a few * of them. It should be just one region in most cases * 线性搜索。在大多数情况下,它应该只是一个区域 */ for (memr = b->memr_head; memr != NULL; memr = memr->next) { if ((page_va >= memr->first_page) && (page_va < (memr->first_page + (memr->nr_pages << __PAGE_SHIFT)))) return memr; }
/* * In case there was no memory region found, the allocator * is in a really bad state. It means that the specified page * region is not covered by our allocator. */ memr = map_get_memr(b, first_page); UK_ASSERT(memr != NULL); UK_ASSERT((first_page + (nr_pages << __PAGE_SHIFT)) <= (memr->first_page + (memr->nr_pages << __PAGE_SHIFT)));
/* 计算特定值的函数, 返回传入数的下一个2次幂的对数,如6的下一个2次幂就是8,8的对数为3 */ /* return log of the next power of two of passed number */ staticinlineunsignedlongnum_pages_to_order(unsignedlong num_pages) { UK_ASSERT(num_pages != 0);
/* ukarch_flsl has undefined behavior when called with zero */ if (num_pages == 1) return0;
/* ukarch_flsl(num_pages - 1) returns log of the previous power of two * of num_pages. ukarch_flsl is called with `num_pages - 1` and not * `num_pages` to handle the case where num_pages is already a power * of two. */ return ukarch_flsl(num_pages - 1) + 1; }
/* Find smallest order which can satisfy the request. 即找到最小的可用块*/ for (i = order; i < FREELIST_SIZE; i++) { if (!FREELIST_EMPTY(b->free_head[i])) break; } if (i == FREELIST_SIZE) goto no_memory;
/* We are commited to merging, unlink the chunk 为了方便下一步的合并操作,所以需要将该块从原链表摘下 */ *(to_merge_ch->pprev) = to_merge_ch->next; to_merge_ch->next->pprev = to_merge_ch->pprev;
order++; }
/* Link the new chunk 将我们合并好的大的内存块重新放回空闲内存块链表*/ freed_ch->level = order; freed_ch->next = b->free_head[order]; freed_ch->pprev = &b->free_head[order]; freed_ct->level = order;
UK_ASSERT(a != NULL); b = (struct uk_bbpalloc *)&a->priv;
/* Find biggest order that has still elements available */ order = FREELIST_SIZE; for (i = 0; i < FREELIST_SIZE; i++) { if (!FREELIST_EMPTY(b->free_head[i])) order = i; } if (order == FREELIST_SIZE) return0; /* no memory left */
min = round_pgup((uintptr_t)base); /* 将基址与页地址向上对齐 */ max = round_pgdown((uintptr_t)base + (uintptr_t)len); /* 将要分配的地址末尾与页地址向下对齐 */ if (max < min) { uk_pr_err("%"__PRIuptr": Failed to add memory region %"__PRIuptr"-%"__PRIuptr": Invalid range after applying page alignments\n", (uintptr_t) a, (uintptr_t) base, (uintptr_t) base + (uintptr_t) len); return -EINVAL; }
range = max - min; /* 能分配的地址范围,range被分为了三个部分,一个是memr内存区块管理占用空间,一个是bitmap位图占用空间,以及实际要分配出去的内存页的占用空间 min <--------------------------range------------------------->max --------------------------------------------------------------- | | | | --------------------------------------------------------------- ^ ^ ^ memr bitmap位图实际存储 实际可分配内存页 */
/* We should have at least one page for bitmap tracking * and one page for data. * 如果除了memr内存区域和一个mapword以外,一个内存页的空间都没有的话,说明内存不够,无法分配 */ if (range < round_pgup(sizeof(*memr) + BYTES_PER_MAPWORD) + __PAGE_SIZE) { uk_pr_err("%"__PRIuptr": Failed to add memory region %"__PRIuptr"-%"__PRIuptr": Not enough space after applying page alignments\n", (uintptr_t) a, (uintptr_t) base, (uintptr_t) base + (uintptr_t) len); return -EINVAL; }
memr = (struct uk_bbpalloc_memr *)min;
/* * The number of pages is found by solving the inequality: * * sizeof(*memr) + bitmap_size + page_num * page_size <= range * * where: bitmap_size = page_num / BITS_PER_BYTE memr->nr_pages: 用于存储实际的可分配页数。计算公式为:总比特数/一个内存页所占的比特数+1 后面的+1是为了为bitmap让出空间,因为一个页需要一个位用来记录分配情况 memr->mm_alloc_bitmap: 用于指向bitmap实际占用的地址的首地址,具体地址为min(基址) + sizeof(*memr)(内存管理块的大小) memr_size: 用于计算memr和bitmap占用的总地址空间(同时与内存页地址向上对齐) memr->mm_alloc_bitmap_size: bitmap实际占用的地址大小,用memr_size减去memr占用的空间即可 * */ memr->nr_pages = BITS_PER_BYTE * (range - sizeof(*memr)) / (BITS_PER_BYTE * __PAGE_SIZE + 1); memr->mm_alloc_bitmap = (unsignedlong *) (min + sizeof(*memr)); memr_size = round_pgup(sizeof(*memr) + DIV_ROUND_UP(memr->nr_pages, BITS_PER_BYTE)); memr->mm_alloc_bitmap_size = memr_size - sizeof(*memr);
min += memr_size; /* 基址移动到可分配页的首地址 */ range -= memr_size; /* 更新可分配页的总地址空间 */
/* * Initialize region's bitmap */ memr->first_page = min; /* add to list */ memr->next = b->memr_head; b->memr_head = memr;
/* All allocated by default. */ memset(memr->mm_alloc_bitmap, (unsignedchar) ~0, memr->mm_alloc_bitmap_size);
/* free up the memory we've been given to play with */ map_free(b, min, memr->nr_pages);
count = 0; /* 下面的这个while循环有点类似于一个数分解成各个二次幂数的过程 例如 100 = 64 + 32 +4,先找100的最高位次幂,即64,然后剩下100-64 = 36 再从36里头找最高位次幂,即32,然后剩下36-32 = 4,4正好是最高位次幂,所以将100划分成了64 + 32 + 4三个数字 下面的循环内依次找到了当前可分配内存大小的最高位次幂,并将其创建一个新的空闲内存块,加入链表对应的位置中。 */ while (range != 0) { /* * Next chunk is limited by alignment of min, but also * must not be bigger than remaining range. */ for (i = __PAGE_SHIFT; (1UL << (i + 1)) <= range; i++) if (min & (1UL << i)) break;
uk_pr_debug("%"__PRIuptr": Add allocate unit %"__PRIuptr" - %"__PRIuptr" (order %lu)\n", (uintptr_t)a, min, (uintptr_t)(min + (1UL << i)), (i - __PAGE_SHIFT));
min = round_pgup((uintptr_t)base); /* 内存区域的基址 */ max = round_pgdown((uintptr_t)base + (uintptr_t)len); /* 内存区域的末尾地址 */ UK_ASSERT(max > min);
/* Allocate space for allocator descriptor 为内存分配器分配地址 */ metalen = round_pgup(sizeof(*a) + sizeof(*b));
/* enough space for allocator available? 判断是否足够为内存分配器分配空间*/ if (min + metalen > max) { uk_pr_err("Not enough space for allocator: %"__PRIsz" B required but only %"__PRIuptr" B usable\n", metalen, (max - min)); returnNULL; }
a = (struct uk_alloc *)min; /* 将内存分配器指向基址 */ uk_pr_info("Initialize binary buddy allocator %"__PRIuptr"\n", (uintptr_t)a); min += metalen; /* 基址跳过内存分配器的占用空间 */ memset(a, 0, metalen); /* 内存分配器初始化 */ b = (struct uk_bbpalloc *)&a->priv; /* 初始化伙伴算法空闲内存数组,将每个链表的free_head[i]指向free_tail[i] */ for (i = 0; i < FREELIST_SIZE; i++) { b->free_head[i] = &b->free_tail[i]; b->free_tail[i].pprev = &b->free_head[i]; b->free_tail[i].next = NULL; } b->memr_head = NULL;