allkillers 发表于 2016-03-01 15:55

内存分配的小例子请教

int main(void)
{
        void* p,*p1,*p2,*p3,*p4,*p5,*p6,*p7;

        printf("sbrk(0) is %u\n",sbrk(0));

        p = malloc(1024);
        printf("sbrk(0) is %u after malloc 1024\n",sbrk(0));

        p1 = malloc(2*1024);
        p2 = malloc(4*1024);
        p3 = malloc(8*1024);
        p4 = malloc(16*1024);
        p5 = malloc(32*1024);
        p6 = malloc(64*1024);
        p7 = malloc(128*1024);

        printf(" %u,%u %u %u %u %u %u %u\n",p,p1,p2,p3,p4,p5,p6,p7);
        printf("sbrk(0)=%u\n",sbrk(0));

        return 0;
}

运行结果:
sbrk(0) is 17338368
sbrk(0) is 17473536 after malloc 1024
17338384,17339424 17341488 17345600 17353808 17370208 17402992 506179600
sbrk(0)=17473536

问题:
1.为什么p1和p0差1040,而不是1024,?
2.为什么p7 malloc 128k后的地址在远端506179600?
3.为什么一系列malloc后,sbrk(0)仍然在17473536?
4.在这个例子中如何反映出伙伴系统的分配策略的,始终没发现,请大神指导。

Buddy_Zhang1 发表于 2016-03-01 16:31

HI:
1.为什么p1和p0差1040,而不是1024,?
    因为在用户空间使用 malloc 分配时,系统先从 glib 的内存中分配,而glib 的内存是从 Slab 或者 Slub 中分配,从上面的数值可知,该 glib 在 slab 中使用的 alignment是16,
    所以你分配的内存地址都是按 16 进行对齐.

2.为什么p7 malloc 128k后的地址在远端506179600?
    malloc() 在分配内存的时候,经过 glib 层到达 slab 层. Slub 层会使用 kmalloc 对内存进行分配,由于 128K 为 32 page 组成,已经超出 SLAB 分配的最大页数,页超出了 PCP 分配器最大的页数,
    此时 kernel 将从 Buddy 分配器中进行分配,所以地址上跳跃这么大很正常.
   
    内存分配的层次为: Glib->Slab->PCP->Buddy

3.为什么一系列malloc后,sbrk(0)仍然在17473536?
    分配内存之后不对内存进行写操作,系统不会真实的分配物理内存!

4.在这个例子中如何反映出伙伴系统的分配策略的,始终没发现,请大神指导。
    如果要反映 Buddy 分配器的策略,首先要突破 SLAB 和 PCP 分配器,具体实现就是分配的内存必须比 PCP 缓存页大,这样内核才从 Buddy 中分配内存!

Buddy_Zhang1 发表于 2016-03-01 16:31

本帖最后由 Buddy_Zhang1 于 2016-03-01 16:31 编辑

{:qq23:} {:qq23:} {:qq23:} {:qq23:}

nswcfd 发表于 2016-03-01 17:48

这是用户态(libc)的内存分配策略,跟kernel的buddy system没有直接的关系。
libc的kernel的接口是sbrk或者mmap

nswcfd 发表于 2016-03-01 17:49

sorry, 是brk

allkillers 发表于 2016-03-01 20:12

首先感谢你回复的这么详细·。对我帮助很大。但有一些点我还是有疑问。
针对回答1:
(1)slab是内核为较高频率出现的内核结构cache的,其大小都是固定的某个结构体的大小,用户态随机malloc若干个byte应该不会从slab中分配才对,malloc大小是随机的,slab不可能做到这样的cache。
(2)16字节对齐的问题,多了16字节是对其的,那没有这16字节也是对齐的,不太明白。

针对回答3:
你的回答本身我是认同。但在本例中我认为malloc成功就一定代表物理内存也分配成功,否则malloc成功了,没有映射物理页,用户去写的时候有可能就分配不到了,那就比较麻烦了。在运行结果中看到:
sbrk(0) is 17338368
sbrk(0) is 17473536 after malloc 1024
这里我也只是malloc,而没有写,sbrk也会发生偏移。
针对回答4:
(1)伙伴系统所管理的块大小有1/2/4/8/16/32....个page的,也就是说最小管理1个page,所以分配1个page应该也会从伙伴分配。
(2)我在实例中发现,不管我怎么分配空间,包括改变分配大小,改变分配顺序,地址始终是接续的,不符合伙伴的策略啊。

Buddy_Zhang1 发表于 2016-03-01 16:31 static/image/common/back.gif
HI:
1.为什么p1和p0差1040,而不是1024,?
    因为在用户空间使用 malloc 分配时,系统先从 glib 的内存中 ...

allkillers 发表于 2016-03-01 20:22

恕我刚学习,比较菜。
请教下你说的libc的分配和伙伴没直接关系,不太明白,我觉得不管在哪分配,最终都是受内核的映射管理,最终都是要按内核的策略来分配的。伙伴策略就是为了解决虚存碎片的。物理上的碎片页都可以充分利用,再分散的物理页也可以映射到连续的虚地址,而虚地址太多空洞就麻烦了。你能举个简单例子说明下你的意思吗?有时候语言很难领会,一个helloworld可以解释很多东西。10回复 4# nswcfd


   

Buddy_Zhang1 发表于 2016-03-01 21:29

(1)slab是内核为较高频率出现的内核结构cache的,其大小都是固定的某个结构体的大小,用户态随机malloc若干个byte应该不会从slab中分配才对,malloc大小是随机的,slab不可能做到这样的cache。
         Glib 在能使用 malloc 之前就已经从内核申请一定量的内存来充当缓存,malloc 分配的都是这些缓存.但从 malloc 的缓存来源角度讲,这段内存也是从 slab 或者 buddy 中分配来的.
         但由于 malloc 是用户空间的函数,只有 malloc 的缓存用到一定的底线,才会向内核发起的申请.请注意,不是用 malloc 分配内存就这的会从内核分配物理内存给你用,这是策略性问题,
         只有对 malloc 分配的内存进行写操作,才会真正的为它分配真实的物理内存.
(2)16字节对齐的问题,多了16字节是对其的,那没有这16字节也是对齐的,不太明白。
         这个问题是内存对齐问题,只有是内存分配器,都要使用各自的内存对齐策略.

(3)伙伴系统所管理的块大小有1/2/4/8/16/32....个page的,也就是说最小管理1个page,所以分配1个page应该也会从伙伴分配。
         从 buddy 中分配内存已经是内核空间的问题,当需要分配一个 page 大小的内存时,使用 kmalloc() 进行分配,kmalloc 会根据传入其中的参数 GFP 标志来计算相关的分配策略,
         这些值包括从哪一个 struct zone 上分配内存,也包括这个页的 migrate 类型,以及分配内存的 size.有了这些值之后,内核首先在 SLAB 中查找有没有匹配 size 的缓存,如果有,
         就使用 kmem_cache_alloc() 进行分配,如果没有,内核就从 PCP 分配器中分配内存,也就是 Per-CPU Allocator,更传统的说就是内核在 SLAB 和 Buddy 中缓存了一个分 page,这些
         page 保存在 Hot 和 cold 链表中,随着内核的更新,内核将这两个链表合成一个链表,hot page 保存在表头,cold page 保存在链表尾,如果这个链表中有空闲的页,就从这里分配内存.
         如果 PCP allocator 里没有空闲页,最后才从 Buddy 里通过 get_page_from_freelist() 函数,结合之前 GFP 标志获得 zone 和 migratetype 来获得空闲 page.

(4)我在实例中发现,不管我怎么分配空间,包括改变分配大小,改变分配顺序,地址始终是接续的,不符合伙伴的策略啊。
          您只在用户空间使用 malloc 分配,它只会返回 0 ~ 3G 的一段连续虚拟地址给您,是不能直接从 buddy 中反应出来的,毕竟里面还有很多堆,匿名映射,文件映射,swap 等等内容,
          不是简单就可以看到的,如果您想了解 Buddy Allocator 的话.直接到内核空间研究就可以!

Buddy_Zhang1 发表于 2016-03-01 21:35

allkillers 发表于 2016-03-01 20:22 static/image/common/back.gif
恕我刚学习,比较菜。
请教下你说的libc的分配和伙伴没直接关系,不太明白,我觉得不管在哪分配,最终都是 ...

如果您想通过代码来解释这个问题的话,我可以给您代码.但不是很好理解,毕竟要看懂内存管理部分的代码是建立在有一定的内存管理理论的基础上.
不过您真想看,我也可以写一些给您.

nswcfd 发表于 2016-03-02 11:14

回复 7# allkillers


#4楼是说,libc有自己的内存管理机制(比如预分配、对象池等),可能多次malloc调用才会触发一次底层的brk或者mmap调用,
可以用strace来观察一下mmap或brk的调用情况。

并且brk或mmap也不直接跟buddy打交道,它们负责关系address space,只有真正产生缺页的时候,才会通过buddy system分配physical page。

当然,brk/mmap的实现过程中,kernel会用到一些管理数据结构,例如vm_struct等,这些结构的分配直接跟slab打交道,也就直接或间接的跟buddy交互。
页: [1] 2 3
查看完整版本: 内存分配的小例子请教