请使劲回答一个关于UNIX/Linux自动扩展堆栈的问

  

<强>有本事就出来,没本事就当鳖!

如果让我回答关于进程栈,线程栈的问题,只要问题不笼统,只要问题明确,我会一五一十地回答,正确率上九成,然而,可悲的是,问题往往他妈的都不是   很明确,因此,游戏到此结束! !艹。但是如果给我一个问的机会,我会问下面一个问题,记住,使出你拉屎的劲来回答(该问题足够糙!不必太当回事,重要的东   西在下面):

,,,,,,   凡是回答操作系统这么规定的之类的答案,一律零分!况且,你能证明我说的一定对吗?万一我是逗你玩呢?操作系统能规定一个错的东西吗?或者我可以继续问为   什么这么规定,直到像我初中的历史老师被我问到朝我眼角猛打一拳那样,如果我能找回点贱人所谓的快感,那么来打吧!问题就是这样,不管这个问题是一个伪命   题还是你有自己的想法,能说5分钟的,我觉得也够可以了。该题目的答题要求如下:
<强>时间限制:5分钟。
作答方式:全口述,不能画的图,不能打手势……语言含糊不清的,表达能力不好的,算错误。
答题建议:如果你对操作系统虚拟内存管理以及Linux的VMA实现细节没有相当深入的理解,请不要猜测答案。请直接回答“不知道”,然后看完此文。
.................................
5分钟过去。我要公布一点我的想法了。
,,,,,,首先,这个问题在本身看来,有问题。因为虽然Linux理应这么做,但它:
<强>第一,它不一定能做的到,
第二,它根本没有必要做。
那么论据是什么?凭什么这样说?

积极论点

没必要这样做。执行流还会调用别的函数或者再次调用,频繁回收栈损耗性能,

消极论点

很难或者不能做到.stack操作是处理器控制的,和操作系统内核地址空间管理机制之间没有同步机制,一个函数调用结束后,CPU自动处理堆栈寄存器的收缩,弹出栈帧,然而它无法通知OS内存管理系统去更新进程地址空间的映射关系。

如何处理栈所在地址空间区域的争议

堆栈   会一直扩展到碰到异常的地址B, B可能是一个只读的的地址或者是一个保护空洞,在向下扩展堆栈情况下,如果地址B偏上,会导致堆栈   空间变小,如果偏下,一旦函数局部变量几乎占满了栈底到B的空间,mmap虽然也能映射掉这段区域然后再交换,然而这会使数据混乱,造   成严重问题。

拍脑袋的结论

mmap或者brk期间,比较栈顶部与esp寄存器,若小于则回收(等于是正常的,大于是不可能的)。

Linux真实的做法

Linux   没有判断什么esp寄存器,Linux的原则很简单,只要一个地址处在一个vma范围内或者处在栈可扩展的范围内,且拥有权限的,它就是可以访   问,内核是不管这个VMA是属于栈还是堆或者别的什么,具体由应用程序自己控制,也就是说,你完全可以写一段代码,把地址空间中所有可以写   的区域全部清零,这完全有可能,缓冲区溢出可能是一种蓄意的破坏,然而程序员偶然的错误也会造成破坏,虽然他们大多数都不知道错误是如何发生的。我不想用   文字长篇大论Linux是如何管理VMA的,你知道这个应该是一个前提,你必须知道这个。我用一段代码以及两个图示来展示Linux系统内核是如何管理   堆栈附近的地址空间映射的,并且在第二张图中给出,如果你非要蓄意破坏,会造成什么问题。也就是说,一旦发生莫名奇妙的错误,你必须能从细节上理解   这个错误是如何发生的。

演示代码

# include  & lt; stdio.h>   # include  & lt; stdlib.h>   # include  & lt; sys/types.h>   # include  & lt; unistd.h>   # include  & lt; sys/mman.h>      # define  LARGE ,,,,,, 70000000   # define  PAGESIZE ,, 4096//,该函数什么都不做,仅仅为了把堆栈向下扩展//,请注意用ulimit将堆栈大小限制去掉,这样会更容易说明问题   void 叫()   {   ,,,int 我;   ,,,char (大);      ,,,//,请相信,一定是在中间的赋值中触发段错误,因为两边的元素要么处在堆栈/fixmap 影响规律,   ,,,//,要么处在游离的,及其孤独的,被fixmap给截断了的vma中。因此下面的赋值不会引发段错误:   ,,,//,一个[0],=,1;   ,,,//,一个[LARGE-1],=, 1;   ,,,for (小姐:=,0;,小姐:& lt;,大;,我+ +),{   ,,,,,,,一个[我],=,1;   ,,,},,   }         命令行参数个数,int 主要(int  char  * * argv)   {   ,,,int 我;   ,,,char  * p_map,, * p_base,, * p_base2;      ,,,printf (" % d \ ninit 国家\ n”,, getpid ());      ,,,//,获取栈的大致地址,并且页大小对齐。   ,,,p_base =, (char  *),我;   ,,,p_base2 =, (char  *) (((unsigned 长)p_base),,, ~ 4095);      ,,,//,获取页大小对齐的用来fixmap的地址,该地址起点在当前堆栈的下面。   ,,,p_base2 =, (char  *) ((unsigned 长)p_base2 作用;(unsigned 长)36 *页大小);   ,,,获取字符();      ,,,//,调用fixmap,显然,如果你仔细在获取字符期间分析了/proc/xx/地图文件并且   ,,,//,得到了上述的那些magic 数字,下面的mmap无论如何是会成功的!   ,,,p_map =, (char  *) mmap (p_base2 (void *),页大小* 3,PROT_READ  |, PROT_WRITE分别,,   ,,,,,,,,,,,,,,,,,,,,,,,MAP_FIXED  | MAP_PRIVATE  |, MAP_ANONYMOUS,, 1,, 0);   ,,,if  (p_map ==, MAP_FAILED), {   ,,,,,,,printf (" failed  1 \ n ");   ,,,},{else    ,,,,,,,printf (" before  unmap  fixmap  around 堆栈\ n”);   ,,,,,,,获取字符();   ,,,,,,,//,成功了就释放掉它,此时的地址空间恢复成mmap之前的状况   ,,,,,,,munmap (p_map,页大小* 3);   ,,,},,   ,,   ,,,printf (" after  unmap  fixmap  around 堆栈\ n”);   ,,,获取字符();      ,,,叫();      ,,,printf (" after  extend 堆栈(第一次)\ n”);   ,,,获取字符();      ,,,//,依然调用之前的那个一模一样的mmap进行fixmap,由于调用了电话,堆栈   ,,,//,空间已经扩展到了这个fixmap的fixaddress,很遗憾,成功了,然而它将stack 影响   ,,,//,一刀切成了两段,不管怎样,访问还是可以进行的。   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null

请使劲回答一个关于UNIX/Linux自动扩展堆栈的问