sys_brk分析 linux1.2.0版本,linux内存管理之sys_brk实现分析

分析完linux内存管理的基本概念与实现之后,就可以接着分析用户空间与内核空间的交互操作了。Brk系统调用属于那种常用但是“可见度”不高的操作,常用于用户空间堆的管理(请参阅本站的>一文)。

Brk在用户空间的接口为int brk(void *end_data_segment)。它通过系统调用进入内核空间。在内核的相应接口为sys_brk().

闲言少叙,言归正传。转入相应的代码。同以往一样,linux内核代码版本为2.6.21

//sys_brk:用来扩大或者缩小进程的数据段边界,brk为新的数据段边界).

asmlinkage unsigned long sys_brk(unsigned long brk)

{

unsigned long rlim, retval;

unsigned long newbrk, oldbrk;

struct mm_struct *mm = current->mm;

down_write(&mm->mmap_sem);

//参数有效性判断。

//代码段非法访问,

if (brk < mm->end_code)

goto out;

//页框对齐

newbrk = PAGE_ALIGN(brk);

oldbrk = PAGE_ALIGN(mm->brk);

//如果新边界与旧边界相等,不用进行空间的伸缩操作,直接赋值即可

if (oldbrk == newbrk)

goto set_brk;

//如果新边界比现在的边界要小,那说明要执行收缩操作

//缩短堆

if (brk <= mm->brk) {

if (!do_munmap(mm, newbrk, oldbrk-newbrk))

goto set_brk;

goto out;

}

//运行到这里的话,说明要执行的是数据段的伸展操作

//不能超过数据段上限

rlim = current->rlim[RLIMIT_DATA].rlim_cur;

if (rlim < RLIM_INFINITY && brk - mm->start_data > rlim)

goto out;

/* Check against existing mmap mappings. */

//伸展空间已经有映射了

if (find_vma_intersection(mm, oldbrk, newbrk+PAGE_SIZE))

goto out;

/* 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_munmap()。代码如下:

int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)

{

unsigned long end;

struct vm_area_struct *mpnt, *prev, *last;

if ((start & ~PAGE_MASK) || start > TASK_SIZE || len > TASK_SIZE-start)

return -EINVAL;

if ((len = PAGE_ALIGN(len)) == 0)

return -EINVAL;

//找到第一个结束地址大于start的VMA。Prev是前一个VMA

mpnt = find_vma_prev(mm, start, &prev);

if (!mpnt)

return 0;

//在没有定义CONFIG_HUGETLB_PAGE条件下,is_vm_hugetlb_page()为0

//略过这段代码

if (is_vm_hugetlb_page(mpnt)) {

int ret = is_aligned_hugepage_range(start, len);

if (ret)

return ret;

}

//现在的堆尾点不可能落在空洞里

//start:新的边界地址。Len:收缩的长度。Start+len即为旧的边界地址。

//所以start+len肯定是属于进程的线性地址

end = start + len;

if (mpnt->vm_start >= end)

return 0;

//如果start大于mpnt的起始地址,就会把mpnt一分为二

if (start > mpnt->vm_start) {

if (split_vma(mm, mpnt, start, 0))

return -ENOMEM;

prev = mpnt;

}

//找到最后的一个vma

last = find_vma(mm, end);

//把最后一个线性区一分为二的情况

if (last && end > last->vm_start) {

if (split_vma(mm, last, end, 1))

return -ENOMEM;

}

mpnt = prev? prev->vm_next: mm->mmap;

//将mpnt对的区间vma从进程描述符组中删除

detach_vmas_to_be_unmapped(mm, mpnt, prev, end);

spin_lock(&mm->page_table_lock);

//更新页表项,释放页框

unmap_region(mm, mpnt, prev, start, end);

spin_unlock(&mm->page_table_lock);

//到现在为止,所有要释放的vma都挂在mpnt上。Unmap_vma_list为对要删除的vma链的处理

unmap_vma_list(mm, mpnt);

return 0;

}

为了弄清楚收缩的整个过程,有必要详细的分析一下函数所调用的各个子函数。

Split_vma:将一个vma劈为成两个:

//参数含义:

//mm:进程的内存描述符vma:要劈分的vma addr:为界线地址new_below:为0时,vma为下一半 为1时,//vma为上一半

int split_vma(struct mm_struct * mm, struct vm_area_struct * vma,

unsigned long addr, int new_below)

{

struct mempolicy *pol;

struct vm_area_struct *new;

//如果进程的vma总数超过了限制值

if (mm->map_count >= sysctl_max_map_count)

return -ENOMEM;

//新申请一个vma

new = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);

if (!new)

return -ENOMEM;

//将新的vma赋值为旧的vma,使其两者相等

*new = *vma;

//new_below为1的时候,vma为上一半,对应的new为下一半

if (new_below)

new->vm_end = addr;

else {

//new_below为0时,vma为下一半,new为上一半

new->vm_start = addr;

new->vm_pgoff += ((addr - vma->vm_start) >> PAGE_SHIFT);

}

pol = mpol_copy(vma_policy(vma));

if (IS_ERR(pol)) {

kmem_cache_free(vm_area_cachep, new);

return PTR_ERR(pol);

}

vma_set_policy(new, pol);

if (new->vm_file)

get_file(new->vm_file);

//如果定义了open操作

if (new->vm_ops && new->vm_ops->open)

new->vm_ops->open(new);

//经过前面的初始化之后,再由vma_adjust调整vma的边界

if (new_below) {

unsigned long old_end = vma->vm_end;

vma_adjust(vma, addr, vma->vm_end, vma->vm_pgoff +

((addr - new->vm_start) >> PAGE_SHIFT), new);

if (vma->vm_flags & VM_EXEC)

arch_remove_exec_range(mm, old_end);

} else

vma_adjust(vma, vma->vm_start, addr, vma->vm_pgoff, new);

return 0;

}

转入vma_adjust():

void vma_adjust(struct vm_area_struct *vma, unsigned long start,

unsigned long end, pgoff_t pgoff, struct vm_area_struct *insert)

{

……

//调整vma的起始边界和结束边界

vma->vm_start = start;

vma->vm_end = end;

vma->vm_pgoff = pgoff;

……

//将新的vma,插入到进程的vma链

__insert_vm_struct(mm, insert);

……

}

第二个要为析的函数是:detach_vmas_to_be_unmapped()

它主要是将要删除的vma链到一起,同时将要删除的vma从mm中脱链

//参数说明:

/*

Mm:进程的内存描述符

Vma:要删除的起始vma

Prev:vma的前一个vma区

End:结束地址

*/

static void

detach_vmas_to_be_unmapped(struct mm_struct *mm, struct vm_area_struct *vma,

struct vm_area_struct *prev, unsigned long end)

{

struct vm_area_struct **insertion_point;

struct vm_area_struct *tail_vma = NULL;

insertion_point = (prev ? &prev->vm_next : &mm->mmap);

do {

//从红黑对中释放掉vma

rb_erase(&vma->vm_rb, &mm->mm_rb);

//更新vma计数

mm->map_count--;

tail_vma = vma;

vma = vma->vm_next;

} while (vma && vma->vm_start < end);

//将要删除的vma从链表中脱落

*insertion_point = vma;

//最后无素后向指针置NULL

tail_vma->vm_next = NULL;

//由于进行了删除操作。Mmap_cache失效了,置NULL

mm->mmap_cache = NULL;/* Kill the cache. */

}

接下来要分析的调用函数是unmap_vma_list()

它主要对删除的vma链进行处理。具体代码如下示:

//参数说明:

//mm:进程的内存描述符

//mpnt:要删除的链表的头节点

static void unmap_vma_list(struct mm_struct *mm,

struct vm_area_struct *mpnt)

{

//遍历链表的每个元素,然后对每一个vma,进行unmap_vma处理

do {

struct vm_area_struct *next = mpnt->vm_next;

unmap_vma(mm, mpnt);

mpnt = next;

} while (mpnt != NULL);

//debug用,忽略

validate_mm(mm);

}

转向unmap_vma():

static void unmap_vma(struct mm_struct *mm, struct vm_area_struct *area)

{

size_t len = area->vm_end - area->vm_start;

//更新mm的total_vm

area->vm_mm->total_vm -= len >> PAGE_SHIFT;

if (area->vm_flags & VM_LOCKED)

area->vm_mm->locked_vm -= len >> PAGE_SHIFT;

vm_stat_unaccount(area);

area->vm_mm->unmap_area(area);

remove_vm_struct(area);

}

在remove_vm_struct中:

static void remove_vm_struct(struct vm_area_struct *vma)

{

……

//将vma描述符释放

kmem_cache_free(vm_area_cachep, vma);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值