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

错别字,关系 -> 管理。

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

想观察伙伴系统的效果,可以跳过libc/用户态,写个简单的kernel module验证。

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

本帖最后由 nswcfd 于 2016-03-02 12:04 编辑

把楼主的例子修改一下

$ cat brk.c#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
        void *p0, *p, *lastp;
        void *b0, *b, *lastb;

        lastb = b0 = sbrk(0);
        lastp = p0 = NULL;

#define do_alloc(x) do { \
        p = malloc(x); \
        if (!p0) lastp = p0 = p; \
        b = sbrk(0);\
        printf("p=%p, p-lastp=%08x, p-p0=%08x, b=%p, b-lastb=%08x, b-b0=%08x\n", \
                p, p-lastp, p-p0, b, b-lastb, b-b0); \
        lastp = p; \
        lastb = b;\
} while (0)

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

#define K *1024
        do_alloc(1 K);
        do_alloc(2 K);
        do_alloc(4 K);
        do_alloc(8 K);
        do_alloc(16 K);
        do_alloc(32 K);
        do_alloc(64 K);
        do_alloc(128 K);

      return 0;
}$ ./brk
sbrk(0) is 0x1262000
p=0x1262010, p-lastp=00000000, p-p0=00000000, b=0x1283000, b-lastb=00021000, b-b0=00021000
p=0x1262420, p-lastp=00000410, p-p0=00000410, b=0x1283000, b-lastb=00000000, b-b0=00021000
p=0x1262c30, p-lastp=00000810, p-p0=00000c20, b=0x1283000, b-lastb=00000000, b-b0=00021000
p=0x1263c40, p-lastp=00001010, p-p0=00001c30, b=0x1283000, b-lastb=00000000, b-b0=00021000
p=0x1265c50, p-lastp=00002010, p-p0=00003c40, b=0x1283000, b-lastb=00000000, b-b0=00021000
p=0x1269c60, p-lastp=00004010, p-p0=00007c50, b=0x1283000, b-lastb=00000000, b-b0=00021000
p=0x1271c70, p-lastp=00008010, p-p0=0000fc60, b=0x1283000, b-lastb=00000000, b-b0=00021000
p=0x7f8c2a495010, p-lastp=292233a0, p-p0=29233000, b=0x1283000, b-lastb=00000000, b-b0=00021000

$ strace ./brk
execve("./brk", ["./brk"], [/* 28 vars */]) = 0
brk(0)                                  = 0xa7e000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f977ffe1000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=91750, ...}) = 0
mmap(NULL, 91750, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f977ffca000
close(3)                              = 0
open("/lib64/libc.so.6", O_RDONLY)      = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360\355\241\245:\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1922152, ...}) = 0
mmap(0x3aa5a00000, 3745960, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x3aa5a00000
mprotect(0x3aa5b8a000, 2093056, PROT_NONE) = 0
mmap(0x3aa5d89000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x189000) = 0x3aa5d89000
mmap(0x3aa5d8e000, 18600, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x3aa5d8e000
close(3)                              = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f977ffc9000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f977ffc8000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f977ffc7000
arch_prctl(ARCH_SET_FS, 0x7f977ffc8700) = 0
mprotect(0x3aa5d89000, 16384, PROT_READ) = 0
mprotect(0x3aa541f000, 4096, PROT_READ) = 0
munmap(0x7f977ffca000, 91750)         = 0
brk(0)                                  = 0xa7e000
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 4), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f977ffe0000
write(1, "sbrk(0) is 0xa7e000\n", 20sbrk(0) is 0xa7e000
)   = 20
brk(0xa9f000)                           = 0xa9f000
write(1, "p=0xa7e010, p-lastp=00000000, p-"..., 89p=0xa7e010, p-lastp=00000000, p-p0=00000000, b=0xa9f000, b-lastb=00021000, b-b0=00021000
) = 89
write(1, "p=0xa7e420, p-lastp=00000410, p-"..., 89p=0xa7e420, p-lastp=00000410, p-p0=00000410, b=0xa9f000, b-lastb=00000000, b-b0=00021000
) = 89
write(1, "p=0xa7ec30, p-lastp=00000810, p-"..., 89p=0xa7ec30, p-lastp=00000810, p-p0=00000c20, b=0xa9f000, b-lastb=00000000, b-b0=00021000
) = 89
write(1, "p=0xa7fc40, p-lastp=00001010, p-"..., 89p=0xa7fc40, p-lastp=00001010, p-p0=00001c30, b=0xa9f000, b-lastb=00000000, b-b0=00021000
) = 89
write(1, "p=0xa81c50, p-lastp=00002010, p-"..., 89p=0xa81c50, p-lastp=00002010, p-p0=00003c40, b=0xa9f000, b-lastb=00000000, b-b0=00021000
) = 89
write(1, "p=0xa85c60, p-lastp=00004010, p-"..., 89p=0xa85c60, p-lastp=00004010, p-p0=00007c50, b=0xa9f000, b-lastb=00000000, b-b0=00021000
) = 89
write(1, "p=0xa8dc70, p-lastp=00008010, p-"..., 89p=0xa8dc70, p-lastp=00008010, p-p0=0000fc60, b=0xa9f000, b-lastb=00000000, b-b0=00021000
) = 89
mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f977ffa6000
write(1, "p=0x7f977ffa6010, p-lastp=7f5183"..., 95p=0x7f977ffa6010, p-lastp=7f5183a0, p-p0=7f528000, b=0xa9f000, b-lastb=00000000, b-b0=00021000
) = 95
exit_group(0)                           = ?


nswcfd 发表于 2016-03-02 12:22

可见,整个过程中,只发生了一次brk调用和一次mmap调用。

这次brk(strace版本),heap空间从0xa7e00提升到0xa9f00,增加了0x21000,即132K,足以容纳后续1K ~ 64K的分配。

至于每次分配都多出0x10字节,那应该是libc用于维护各个buffer的管理开销(比如把已分配空间link起来),或者是出于检查越界等操作增加的额外保护空间(red zone)。

最后一次分配128K的时候,现有的brk空间不够了,libc有两种选择,继续brk扩大heap空间(这样的话返回地址跟之前是连续的),或者通过mmap在线性空间里找一段未用的(注意mmap的size同样是132K),这里选择的后者,所以p7一下子跳了很远。

貌似libc有个可配的参数,size小于某个值采取brk策略,大于采取mmap策略。

使用mmap,释放之后可以很容易归还给os;使用brk,如果heap上有漏洞,则空间无法及时归还os。例如p7没有释放,则p1~p6都free的情况下,libc也无法通过brk把p1~p6的空间归还给os。

allkillers 发表于 2016-03-02 12:40

可能我的理解有误,所以我想先明确以下几点:
第一,os解决碎片或者空洞问题,是解决虚存地址空间的碎片,还是解决物理地址空间的碎片?我一直认为是为了解决虚存地址的碎片。因为用户一般是申请虚存上连续的地址,物理地址不连续没关系,换句话说物理地址碎片再多也没关系。
第二,mmap 或brk只是分配虚存地址空间,而并没有映射真正的物理空间是吗?
第三,我的理解slab只是缓存固定大小的内核结构,一般的的brk的大小都是用户随意指定的,slab中怎么可能有刚好符合大小的块?

看来需要学习的太多太多了。
回复 10# nswcfd


   

nswcfd 发表于 2016-03-03 12:23

2. linux尽可能的lazy,把page的分配推迟到缺页中断
3. 如果跳过libc直接使用brk,所有的地址和大小都是page对齐的,可以认为跟slab没有关系
1. 这个话题有点大,有一些是用来解决(或降低)物理碎片的问题,例如XXX_MOVEABLE (http://blog.csdn.net/dog250/article/details/6108028)

Buddy_Zhang1 发表于 2016-03-04 14:17

本帖最后由 Buddy_Zhang1 于 2016-03-04 14:28 编辑

如果您想研究伙伴系统如何分配物理页,我可以给您一个测试代码来了解.

#include <linux/kernel.h>
#include <linux/mmzone.h>
#include <linux/mm.h>

void TestCase_Get_Buddy_Page(void)
{
    struct pglist_data *pgdat;
    struct zonelist *zonelist;
    struct zone *zone;
    struct free_area *area;
    struct page *page;
    int migratetype;
    int order,current_order;

    pgdat = NODE_DATA(0);
    zonelist = pgdat->node_zonelists;
    migratetype = MIGRATE_MOVABLE;
    order = 4; // alloc 64 page.

    first_zones_zonelist(zonelist,0,NULL,&zone);
    area = &zone->free_area;
   
    for(current_order = order ; current_order < MAX_ORDER ; current_order++) {
          int size = 1 << current_order;
         
          if(list_empty(&area->free_list))
                continue;
   
         page = list_entry(area->free_list.next,
                                          struct page,lru);
         list_del(&page->lru);
         rmv_page_order(page);
         area->nr_free--;

         while(order < current_order) {
               area--;
               current_order--;
               size >>= 1;
               list_add(&page.lru,&area->free_list);
               set_page_order(&page,current_order);
               area->nr_free++;
         }
         break;
    }
    /* Get page */
    if(!PageBuddy(page))
       printk(KERN_INFO "Get page from buddy System\n");
}

allkillers 发表于 2016-03-09 08:51

最近工作比较忙,没上来看。非常感谢你的耐心解答,我刚好在看相关的书。对你讲的大概明白了一些。等我看完书再来仔细研读的的回答。
其实,我一直有误解:
1. 之前一直认为伙伴系统是为了解决虚存碎片的,现在才发现它其实是解决物理碎片的。所以我的例子打印出来的是虚存地址,本身和伙伴没有直接关系。但我还是有疑问,书中提到,一般情况下,分配的空间在物理地址上也是连续的,为啥在物理地址也要连续,因为有页表映射,我觉得物理地址不连续也没关系啊,只要虚拟地址连续即可,难道一些特定场合必须要求物理地址也连续?DMA是要求连续物理地址的,这个我知道。不知道理解的对不对。

2. 看到struct page结构,里面的list成员以及lru成员。我具体怎么知道这个list是什么list,因为同样是list却可以连入任何list里面,lru是什么lru,是clean list还是dirty list,书上说通过flag来区分,还有比如zone->pageset->pcp->list,这其实就是page list,这时的page里的list就是zone->pageset->pcp的链表吗?这时page里的lru又是什么,会是null?

回复 17# Buddy_Zhang1


   

Buddy_Zhang1 发表于 2016-03-09 11:49

回复 18# allkillers


    1. 之前一直认为伙伴系统是为了解决虚存碎片的,现在才发现它其实是解决物理碎片的。所以我的例子打印出来的是虚存地址,本身和伙伴没有直接关系。但我还是有疑问,书中提到,一般情况下,分配的空间在物理地址上也是连续的,为啥在物理地址也要连续,因为有页表映射,我觉得物理地址不连续也没关系啊,只要虚拟地址连续即可,难道一些特定场合必须要求物理地址也连续?DMA是要求连续物理地址的,这个我知道。不知道理解的对不对。

      内核使用 "物理连续或者不连续" 都是根据相应的策略要求使用的,但虚拟地址一定是连续的.
      对于内核空间,只要 ZONE_NOMAL 物理内存充足,一般都使用物理地址和虚拟地址都连续的方式.如果 ZONE_NOMAL 的物理内存吃紧,内核就将 ZONE_HIGHMEM 的物理内存通过不同的方式
      映射到内核空间 3G ~ 4G 虚拟地址其中的某一段上,但这段区域绝对不是 ZONE_NOMAL 对应的虚拟区.
      内核可以使用 vmalloc 去获得虚拟地址连续但物理地址不连续的区域,这些区域的物理地址都属于 ZONE_HIGHMEM.

2. 看到struct page结构,里面的list成员以及lru成员。我具体怎么知道这个list是什么list,因为同样是list却可以连入任何list里面,lru是什么lru,是clean list还是dirty list,书上说通过flag来区分,还有比如zone->pageset->pcp->list,这其实就是page list,这时的page里的list就是zone->pageset->pcp的链表吗?这时page里的lru又是什么,会是null?

   简单的说 struct page 的 lru 成员就是负责被各个内存分配器管理.
   1. 假如这个 struct page 属于 Buddy System,那么内核通过将该 struct page 的 lru 加入到对应的 free_area 链表上,可以参照下面代码;
            struct page *page;/* Page 已知 */
            struct zone *zone;
            int migratetype;
            int order;

            zone = page_zone(page);
            migratetype = get_pageblock_migratetype(page);
            order = page_order(page);

            list_add(&page->lru,
                  &zone->free_area.free_list);   /* page 所属的 free_list */
      2. 加入这个 struct page 属于 PCP(per_cpu_pages) 分配器,也就是 hot 或 cold 页.那么这个 struct page 的 lru 就会加入到 PCP 的 冷热页链表中.可参照下面代码.
         struct page *page; /* 已知 */
         struct zone *zone;
         struct per_cpu_pages *pcp;
         int migratetype;
         int cold = 0; /* 如果这个页属于 hot 页 cold 为0,如果这个页属于 cold 页那么 cold 为 1*/

         zone = page_zone(page);
         migratetype = get_pageblock_migratetype(page);
         pcp = &zone->pageset->pcp;

         if(cold)
                list_add_tail(&page->lru,&pcp->lists);
         else
                list_add(&page->lru,&pcp->lists);
         pcp->count++;

       3. 如果这个 struct page 属于 SLUB 分配器,那么 struct page 的 lru 就会被加入到 struct kmem_cache->node.partial 链表里面.可以参照如下代码:
            struct page *page = alloc_page(page);
            struct kmem_cache *cache;

             cache = kmem_cache_create("Buddy",8,8,0,NULL);

             list_add(&page->lru,&cache->node.partial);

{:yxh45:}
            
      

allkillers 发表于 2016-03-10 11:08

膜拜,你怎么可以研究这么细,学习的路还很长。以后要多请教。加个好友先。回复 19# Buddy_Zhang1


   
页: 1 [2] 3
查看完整版本: 内存分配的小例子请教