堆栈内存如何增加? [英] How does a stack memory increase?

查看:146
本文介绍了堆栈内存如何增加?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在典型的C程序中,Linux内核提供84K-〜100K的内存.当进程使用给定的内存时,内核如何为堆栈分配更多的内存.

IMO,当进程占用堆栈中的所有内存并现在使用下一个连续内存时,理想情况下,它应该是页面错误,然后内核处理页面错误. 内核是否在这里为给定进程提供了更多的堆栈内存,Linux内核中的哪个数据结构标识了进程的堆栈大小?

解决方案

根据操作系统(Linux实时vs.普通)和下面的语言运行时系统,使用了许多不同的方法:

1)动态,按页面错误

通常将一些实际页面预分配给较高的地址,并将初始sp分配给该地址.堆栈向下生长,堆栈向上生长.如果某个页面错误发生在堆栈底部下方的某个位置,则会分配和映射丢失的中间页面.有效地自动从顶部向底部增加堆栈.通常最多可以执行这种自动分配,在环境中(ulimit),exe标头或无法通过程序通过系统调用动态调整该最大分配(rlimit).特别是在不同的操作系统之间,这种可调整性差异很大.通常,页面错误距堆栈底部有多远"也有一个限制,认为页面错误是可以的,并且会自动发生增长.请注意,并非所有系统的堆栈都向下增长:在HPUX下,它(用于?)向上增长,因此我不确定PA-Risc上的linux会做什么(有人可以对此发表评论).

2)固定大小

其他操作系统(尤其是在嵌入式和移动环境中)的定义是固定大小,或者在exe标头中指定,或者在创建程序/线程时指定.特别是在嵌入式实时控制器中,这通常是一个配置参数,单个控制任务会获得修复堆栈(以避免线程失控占用更高优先级控制任务的内存).当然,在这种情况下,也可能仅虚拟分配内存,直到真正需要为止.

3)分页,意大利面和类似食品

这些机制倾向于被遗忘,但仍在某些运行时系统中使用(我知道Lisp/Scheme和Smalltalk系统).这些根据需要动态分配和增加堆栈.但是,不是作为单个连续的段,而是作为多页块的链接链.为了处理段边界,它要求编译器生成不同的函数进入/退出代码.因此,此类方案通常是由语言支持系统而不是OS本身(过去曾有过-叹气)实现的.原因是,当您在交互式环境中有很多(例如1000个)线程时,预分配1Mb只会填充您的虚拟地址空间,并且您不能支持以前未知单个线程的线程需求的系统(即通常是在动态环境中,在这种环境中,用户可能会将评估代码输入到单独的工作空间中).因此无法像上面的方案1那样进行动态分配,因为会有其他线程带有它们自己的堆栈.堆栈由较小的段(例如8-64k)组成,这些段从池中分配和释放,并链接到堆栈段链中.还可能需要这种方案来为诸如连续性,协程等之类的事物提供高性能支持.

现代的unixes/linuxes和(我想,但不是100%肯定的)Windows使用方案1)来执行exe的主线程,并使用方案2)来执行其他(p-)线程,这些线程需要使用最初是线程创建者.大多数嵌入式系统和控制器都使用固定(但可配置)的预分配(在许多情况下甚至是物理预分配).

错字

In a typical C program, the linux kernel provides 84K - ~100K of memory. How does the kernel allocate more memory for the stack when the process uses the given memory.

IMO when the process takes up all the memory of the stack and now uses the next contiguous memory, ideally it should page fault and then the kernel handles the page fault. Is it here that the kernel provides more memory to the stack for the given process, and which data structure in linux kernel identifies the size of the stack for the process??

解决方案

There are a number of different methods used, depending on the OS (linux realtime vs. normal) and the language runtime system underneath:

1) dynamic, by page fault

typically preallocate a few real pages to higher addresses and assign the initial sp to that. The stack grows downward, the heap grows upward. If a page fault happens somewhat below the stack bottom, the missing intermediate pages are allocated and mapped. Effectively increasing the stack from the top towards the bottom automatically. There is typically a maximum up to which such automatic allocation is performed, which can or can not be specified in the environment (ulimit), exe-header, or dynamically adjusted by the program via a system call (rlimit). Especially this adjustability varies heavily between different OSes. There is also typically a limit to "how far away" from the stack bottom a page fault is considered to be ok and an automatic grow to happen. Notice that not all systems' stack grows downward: under HPUX it (used?) to grow upward so I am not sure what a linux on the PA-Risc does (can someone comment on this).

2) fixed size

other OSes (and especially in embedded and mobile environments) either have fixed sizes by definition, or specified in the exe header, or specified when a program/thread is created. Especially in embedded real time controllers, this is often a configuration parameter, and individual control tasks get fix stacks (to avoid runaway threads taking the memory of higher prio control tasks). Of course also in this case, the memory might be allocated only virtually, untill really needed.

3) pagewise, spaghetti and similar

such mechanisms tend to be forgotten, but are still in use in some run time systems (I know of Lisp/Scheme and Smalltalk systems). These allocate and increase the stack dynamically as-required. However, not as a single contigious segment, but instead as a linked chain of multi-page chunks. It requires different function entry/exit code to be generated by the compiler(s), in order to handle segment boundaries. Therefore such schemes are typically implemented by a language support system and not the OS itself (used to be earlier times - sigh). The reason is that when you have many (say 1000s of) threads in an interactive environment, preallocating say 1Mb would simply fill your virtual address space and you could not support a system where the thread needs of an individual thread is unknown before (which is typically the case in a dynamic environment, where the use might enter eval-code into a separate workspace). So dynamic allocation as in scheme 1 above is not possible, because there are would be other threads with their own stacks in the way. The stack is made up of smaller segments (say 8-64k) which are allocated and deallocated from a pool and linked into a chain of stack segments. Such a scheme may also be requried for high performance support of things like continuations, coroutines etc.

Modern unixes/linuxes and (I guess, but not 100% certain) windows use scheme 1) for the main thread of your exe, and 2) for additional (p-)threads, which need a fix stack size given by the thread creator initially. Most embedded systems and controllers use fixed (but configurable) preallocation (even physically preallocated in many cases).

edit: typo

这篇关于堆栈内存如何增加?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆