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會降低?爲什麼有些系統中,