Linux內存窺視--brk

    brk()系統調用完成了數據段大小改變的功能,當然包括增加(malloc,申請)和減小(free,釋放)兩部分了。

    這一系統調用在一般應用中不會出現,但是可以確定一定是被使用最多的,因爲其被malloc()調用,malloc()庫函數的操作後續給出,但據說,是lib庫爲應用程序提供了內存管理的方法,當其管理的內存不足的時候,庫向內核批量申請一段內存,當然要滿足頁面對齊的條件,即所分配的空間應該爲頁面大小的整數倍。

    在查看進程的內存使用情況時,我們也可以通過top得到VSZ的值,也可以使用如下命令得到進程的其他參數:

root@catalyst_24FD52F24E00:/tmp/log# cat /proc/`pidof snmpd`/status

Name:   snmpd

State:  S (sleeping)

Tgid:   4258

Pid:    4258

PPid:   1

TracerPid:      0

Uid:    0       0       0       0

Gid:    0       0       0       0

FDSize: 32

Groups:

VmPeak:     3748 kB

VmSize:     3748 kB

VmLck:         0 kB

VmHWM:      1308 kB

VmRSS:      1308 kB

VmData:      968 kB

VmStk:       136 kB

VmExe:       716 kB

VmLib:      1768 kB

VmPTE:        16 kB

VmSwap:        0 kB

Threads:        1

SigQ:   0/475

SigPnd: 00000000000000000000000000000000

ShdPnd: 00000000000000000000000000000000

SigBlk: 00000000000000000000000000000000

SigIgn: 00000000000000000000000000001004

SigCgt: 0000000000000000000000004000c003

CapInh: 0000000000000000

CapPrm: ffffffffffffffff

CapEff: ffffffffffffffff

CapBnd: ffffffffffffffff

Cpus_allowed:   1

Cpus_allowed_list:      0

voluntary_ctxt_switches:        32233

nonvoluntary_ctxt_switches:     12093

root@catalyst_24FD52F24E00:/tmp/log# 

    其中的VmPeak反應的是該程序所使用所有內存的大小,在內核中,通過mm->total_vm反映出來:

[fs/proc/task_mmu.c: task_mem()]

void task_mem(struct seq_file *m, struct mm_struct *mm)

{

unsigned long data, text, lib, swap;

unsigned long hiwater_vm, total_vm, hiwater_rss, total_rss;


/*

* Note: to minimize their overhead, mm maintains hiwater_vm and

* hiwater_rss only when about to *lower* total_vm or rss.  Any

* collector of these hiwater stats must therefore get total_vm

* and rss too, which will usually be the higher.  Barriers? not

* worth the effort, such snapshots can always be inconsistent.

*/

hiwater_vm = total_vm = mm->total_vm;

...

seq_printf(m,

"VmPeak:\t%8lu kB\n"

"VmSize:\t%8lu kB\n"

"VmLck:\t%8lu kB\n"

"VmHWM:\t%8lu kB\n"

"VmRSS:\t%8lu kB\n"

"VmData:\t%8lu kB\n"

"VmStk:\t%8lu kB\n"

"VmExe:\t%8lu kB\n"

"VmLib:\t%8lu kB\n"

"VmPTE:\t%8lu kB\n"

"VmSwap:\t%8lu kB\n",

hiwater_vm << (PAGE_SHIFT-10),

...

}

    該函數主要是在進程任務的proc下生成對應的status統計信息。

    要想明確獲知VmPeak的來源,當然離不開malloc()庫函數了,而這一函數將會調用brk()系統調用,下面,開始吧。


    brk()系統調用的部分過程如下:

[mm/mmap.c: brk()]

SYSCALL_DEFINE1(brk, unsigned long, brk)

{

...

/* Ok, looks good - let it rip. */

if (do_brk(oldbrk, newbrk-oldbrk) != oldbrk)

goto out;


set_brk:

mm->brk = brk;

out:

retval = mm->brk;

up_write(&mm->mmap_sem);

return retval;

}

    由上面的調用過程不難發現,brk()主要通過do_brk()完成其主要的功能。

    do_brk()的部分代碼如下:

[mm/mmap.c: brk()->do_brk()]

/*

 *  this is really a simplified "do_mmap".  it only handles

 *  anonymous maps.  eventually we may be able to do some

 *  brk-specific accounting here.

 */

unsigned long do_brk(unsigned long addr, unsigned long len)

{

...

/*

* Clear old maps.  this also does some error checking for us

*/

 munmap_back:

vma = find_vma_prepare(mm, addr, &prev, &rb_link, &rb_parent);

if (vma && vma->vm_start < addr + len) {

if (do_munmap(mm, addr, len))

return -ENOMEM;

goto munmap_back;

}


/* Check against address space limits *after* clearing old maps... */

if (!may_expand_vm(mm, len >> PAGE_SHIFT))

return -ENOMEM;


if (mm->map_count > sysctl_max_map_count)

return -ENOMEM;


if (security_vm_enough_memory(len >> PAGE_SHIFT))

return -ENOMEM;


/* Can we just expand an old private anonymous mapping? */

vma = vma_merge(mm, prev, addr, addr + len, flags,

NULL, NULL, pgoff, NULL);

if (vma)

goto out;


/*

* create a vma struct for an anonymous mapping

*/

vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);

if (!vma) {

vm_unacct_memory(len >> PAGE_SHIFT);

return -ENOMEM;

}

INIT_LIST_HEAD(&vma->anon_vma_chain);

vma->vm_mm = mm;

vma->vm_start = addr;

vma->vm_end = addr + len;

vma->vm_pgoff = pgoff;

vma->vm_flags = flags;

vma->vm_page_prot = vm_get_page_prot(flags);

vma_link(mm, vma, prev, rb_link, rb_parent);

out:

perf_event_mmap(vma);

mm->total_vm += len >> PAGE_SHIFT;

if (flags & VM_LOCKED) {

if (!mlock_vma_pages_range(vma, addr, addr + len))

mm->locked_vm += (len >> PAGE_SHIFT);

}

return addr;

}

    上面的代碼完成了釋放、內存申請、內存空間映射關係建立等功能,後續對這些部分進行分解。

    此時關注於另一點:mm中的total_vm成員。

    由上面的代碼中發現,total_vm在最後會增加此次調整的長度,而且其單位頁面大小,在這裏2.6.36爲4KB。

    下面一點沒有進行對應的驗證:由上面的計算來看,應該是在free的時候,total_vm的值會變小纔對,因爲此時len爲負值,所以total_vm應該降低;但是前文說過,lib庫爲應用程序提供了一個內存管理的方法,只有在其內存不足的時候,才向內核申請內存,然後返回給應用程序其所期望的空間大小,如此,則lib庫並不會將應用程序free的內存釋放到系統的剩餘內存中,所以將會導致total_vm會存在持續增長的情況。那麼,此時就存在一個問題了,是lib庫導致了total_vm的增長麼?total_vm的真正作用是什麼?在什麼時候,total_vm會降低?爲什麼有些系統中,



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章