内核技术中文网»首页 论坛 圈点 查看内容

0 评论

0 收藏

分享

Linux--brk系统调用

  brk()系统调用完成了数据段大小改变的功能,当然包括增加(malloc,申请)和减小(free,释放)两部分了。

    这一系统调用在一般应用中不会出现,但是可以确定一定是被使用最多的,因为其被malloc()调用,malloc()库函数的操作后续给出,但据说,是lib库为应用程序提供了内存管理的方法,当其管理的内存不足的时候,库向内核批量申请一段内存,当然要满足页面对齐的条件,即所分配的空间应该为页面大小的整数倍。

在查看进程的内存使用情况时,我们也可以通过top得到VSZ的值,也可以使用如下命令得到进程的其他参数:在查看进程的内存使用情况时,我们也可以通过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会降低?为什么有些系统中,

原文作者:黄雷雷

原文地址:https://blog.51cto.com/scottgh/1710007(版权归原文作者所有,侵权联系删除

回复

举报 使用道具

全部回复
暂无回帖,快来参与回复吧
主题 1545
回复 0
粉丝 2
扫码获取每晚技术直播链接
快速回复 返回顶部 返回列表