Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2985134
  • 博文数量: 401
  • 博客积分: 12926
  • 博客等级: 上将
  • 技术积分: 4588
  • 用 户 组: 普通用户
  • 注册时间: 2009-02-22 14:51
文章分类

全部博文(401)

文章存档

2015年(16)

2014年(4)

2013年(12)

2012年(82)

2011年(98)

2010年(112)

2009年(77)

分类: LINUX

2011-07-01 15:39:08

  1. int main( int argc, char *argv[] )
  2. {
  3.         int i;
  4.         int *p;

  5.         p = (int*)&p - 1;
  6.         for( i = 1; 1; i++ )
  7.         {
  8.                 printf( "%d\t", i );
  9.                 fflush( stdout );
  10.                 *p-- = 1;
  11.         }

  12.         return 0;
  13. }
复制代码


很自然,这段代码运行出错:Memory fault 。 出错时 i=1489, 所以应该是p越下栈区时被内核检测到报错,不是资源耗尽错误。
请教一下,在内存报错时,是否执行到do_page_fault函数。
如果执行到这个函数,我觉的应该在下面这段代码中执行栈扩展expand_stack,直到资源耗尽错误。
请指点一下在内核检测Memory fault 过程中执行的函数序列。谢谢。

if (address + 65536 + 32 * sizeof(unsigned long) < regs->esp)
  1.         vma = find_vma(mm, address);
  2.         if (!vma)
  3.                 goto bad_area;
  4.         if (vma->vm_start <= address)
  5.                 goto good_area;
  6.         if (!(vma->vm_flags & VM_GROWSDOWN))
  7.                 goto bad_area;
  8.         if (error_code & 4) {
  9.                 /*
  10.                  * Accessing the stack below %esp is always a bug.
  11.                  * The large cushion allows instructions like enter
  12.                  * and pusha to work.  ("enter $65535,$31" pushes
  13.                  * 32 pointers and then decrements %esp by 65535.)
  14.                  */
  15.                 if (address + 65536 + 32 * sizeof(unsigned long) < regs->esp)
  16.                         goto bad_area;
  17.         }
  18.         if (expand_stack(vma, address))
  19.                 goto bad_area;
复制代码

[ 本帖最后由 jack9981 于 2007-6-19 19:21 编辑 ]

| | | 

情景分析上有详解,去看看
__________________________________
Good better best,
never let it rest,
till good is better,
and better best.

| | | 



QUOTE:
原帖由 qtdszws 于 2007-6-19 19:22 发表于 2楼  
情景分析上有详解,去看看


看过,堆栈扩展那一节? 
解不了疑惑。

| | | 



QUOTE:
原帖由 jack9981 于 2007-6-19 19:24 发表于 3楼  

看过,堆栈扩展那一节? 
解不了疑惑。


lz误解了栈增长的方式。栈的增长,是通过改变栈指针(x86中的esp)来实现的,并非是你访问了一个不在栈上的地址,栈就会增长到那个地址去(真这样就天下大乱了)。
为什么这里的判断条件是if (address + 65536 + 32 * sizeof(unsigned long) < regs->esp)呢,那是因为,在x86平台上,一次性增长栈最大的指令是enter。它的格式是:
  1. enter imm16,imm8 //imm16表示16位的立即数,imm8表示8位立即数
复制代码

其中第一个操作数imm16代表要增长的栈大小,第二个操作数imm8表示嵌套层数(0到31)。所以一次性增长栈最大的情况为
  1. enter $65535,$31
复制代码

执行了如上指令后,栈指针esp的变化为:esp=esp-65536-32*4(这里32*4的意思是,enter指令会压imm8个框架指针到栈上去,所以最多可以压31个指针到栈上去)。所以栈一次性最大可以增长65536+32*4个字节。
如果if (address + 65536 + 32 * sizeof(unsigned long) < regs->esp)成立的话,那么操作肯定是个栈越界操作,kernel就会发送一个segmention faut给用户进程。

lz在一楼发的程序,仅仅是用一个不断减小的指针去访问栈的内容,它并不会改变栈指针esp,所以栈是不会随之增长的。

lz如果想尝试不断增长栈直到耗尽用户栈,可以用一个递归的例子,例如:
  1. void overflow()
  2. {
  3.   int arr[1<<20] = {1};
  4.   overflow();
  5. }
复制代码

这里由于分配了局部变量,它是会减小栈指针从而增长栈的

[ 本帖最后由 zx_wing 于 2007-6-19 21:41 编辑 ]
__________________________________

C调 ---- C语言
都是最喜欢的

| | | 

thanks
剩下的可以自己解决了。

| | | 

LZ这段代码根本不会到do_page_fault,你还没有搞清楚这个函数是拿来做什么的,你的代码为什么死的原因,你知道吗?
这些东西都没搞清楚,我不知道你在这里想问什么。。。。
__________________________________
Linux要学习的东东太多了!
不过很幸福,我们还有东西可学!

| | | 



QUOTE:
原帖由 snow_insky 于 2007-6-20 00:40 发表于 6楼  
LZ这段代码根本不会到do_page_fault,你还没有搞清楚这个函数是拿来做什么的,你的代码为什么死的原因,你知道吗?
这些东西都没搞清楚,我不知道你在这里想问什么。。。。



不要打击我,老大,偶正努力的学习。。

| | | 



QUOTE:
原帖由 snow_insky 于 2007-6-20 00:40 发表于 6楼  
LZ这段代码根本不会到do_page_fault,你还没有搞清楚这个函数是拿来做什么的,你的代码为什么死的原因,你知道吗?
这些东西都没搞清楚,我不知道你在这里想问什么。。。。



这段代码肯定是会到do_page_fault的。
它出错的流程是:
栈越界,CPU在TLB中找不到相应的映射,产生TLB miss ----> 从cr3开始逐级walk页表,发现对应的PTE为空,产生page_fault ---->do_page_fault检查地址,发现栈越界,产生segmention fault。
__________________________________

C调 ---- C语言
都是最喜欢的

| | | 

do_page_fault是被调用的。

仅仅减少ESP也不能自动延伸stack. 减少的同时必须读写ESP处的内容。
__________________________________
Email: johnye@webizmail.com

| | | 



QUOTE:
原帖由 思一克 于 2007-6-20 13:44 发表于 9楼  
do_page_fault是被调用的。

仅仅减少ESP也不能自动延伸stack. 减少的同时必须读写ESP处的内容。


是的,使用enter指令扩展栈后第一次访问就会产生一个page fault,从而为栈分配页。
多谢补充。
__________________________________

C调 ---- C语言
都是最喜欢的








QUOTE:
原帖由 zx_wing 于 2007-6-19 21:39 发表 

lz误解了栈增长的方式。栈的增长,是通过改变栈指针(x86中的esp)来实现的,并非是你访问了一个不在栈上的地址,栈就会增长到那个地址去(真这样就天下大乱了)。
为什么这里的判断条件是if (address + 655 ...



还是没有理解,内核并没有做判断到底是谁在访问这个地址啊,用户程序完全可以利用一个指针这个地址进行访问啊,就像楼主这样。内核怎么知道是否是esp对这个地址进行了访问。查了n多网页都没解释清楚。能否提点一二?

| | | 

LZ用的内核应该是
这个版本的代码
                if (address + 32 < regs->esp)
                        goto bad_area;
而不是
                if (address + 65536 + 32 * sizeof(unsigned long) < regs->esp)
                        goto bad_area;

这就解释了为何只输出到1000多就fault,如果是下面这个版本的内核,要到一万多才fault

[ 本帖最后由 qtdszws 于 2008-12-18 11:04 编辑 ]
__________________________________
Good better best,
never let it rest,
till good is better,
and better best.

| | | 



QUOTE:
原帖由 zx_wing 于 2007-6-20 14:04 发表 

是的,使用enter指令扩展栈后第一次访问就会产生一个page fault,从而为栈分配页。
多谢补充。



呵呵,zx_wing 很酷啊。

无论是学识和态度都让我佩服。
__________________________________
我的Blog:
http://blog.csdn.net/yayong

Twitter:

| | | 
阅读(1564) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~