为什么访问越界了没有收到SIGSEGV信号?

behire123 2012-09-30 09:28:28
const int K = 1024;
int i = 0;
char *p = new char;
while(i++ < 20)
{
p = p + i * K;
*p = 'A';
printf("p = %c(%0x), i = %d.\n", *p, p, i);
sleep(1);
}

delete p;
p = NULL;


要循环15次才收到SIGSEGV信号。也就是说越界了15K才会收到段错误的信号。一页4K大小,好像跟书本上说的内存管理有冲突啊?谁能解析一下?
...全文
370 29 打赏 收藏 转发到动态 举报
写回复
用AI写文章
29 条回复
切换为时间正序
请发表友善的回复…
发表回复
behire123 2012-10-10
  • 打赏
  • 举报
回复
[Quote=引用 21 楼 的回复:]

楼主竟然能够忘了内存的实模式和保护模式,也算是很厉害啊。
[/Quote]


我主要不明白的是:
比如相对于在栈上分配的内存 int arr[200];这个数组的起始地址是addr = 0xbf97a1dc.说明分配的内存为:页表项0xbf97a,偏移为0x1dc==476,系统是按照4096大小来分配内存的,那么4096 - 476 = 3620, 3620 / 4 = 905,从0x1dc这个位置开始到这一页的结束还可以放905个Int,所以就算我们访问了arr[900]也不一定会收到段错误,但是如果访问了arr[906],这样就是跨页访问了,应该就会收到段错误(起码我测试是可以精确收到段错误的)。
----------------------------------------------
但是对于在堆上分配的内存:
比如我给出的那个列子,如果是按照 p = p + i * k;这个方式来写,可以循环15次(而且每次都是15次,再2台不同版本的linux系统下测试结果都一样),也就是说进程可以连续访问到15K的空间?15 / 4 差不多是4页的大小了,为什么进程能跨这么多页访问,而不引发缺页中断从而调用on_page_fault之类的函数进行合法性检查,难道是进程一起来的时候已经把这4页的内容load进进程空间了?(但是按照请页机制不应该啊)

还有为什么我只改了一个写法: char *q = p + i * k;这个可以循环131次(固定这么多次),130 / 4 = 32个页面,这个写法跟原来只是换了一个方式怎么有这么大的差别?

这些几个问题是我不能解析清楚的。谢谢
behire123 2012-10-10
  • 打赏
  • 举报
回复
终于找到答案了,可以写个小程序遍历一下进程的地址空间,或者直接 cat /proc/pid/maps,比如我的这个测试程序:
[root@bogon _sock]# cat /proc/29261/maps
00110000-00299000 r-xp 00000000 08:01 1970045 /lib/libc-2.12.so
00299000-0029a000 ---p 00189000 08:01 1970045 /lib/libc-2.12.so
0029a000-0029c000 r--p 00189000 08:01 1970045 /lib/libc-2.12.so
0029c000-0029d000 rw-p 0018b000 08:01 1970045 /lib/libc-2.12.so
0029d000-002a0000 rw-p 00000000 00:00 0
007e4000-00802000 r-xp 00000000 08:01 1970032 /lib/ld-2.12.so
00802000-00803000 r--p 0001d000 08:01 1970032 /lib/ld-2.12.so
00803000-00804000 rw-p 0001e000 08:01 1970032 /lib/ld-2.12.so
0094b000-0094c000 r-xp 00000000 00:00 0 [vdso]
0099c000-009c4000 r-xp 00000000 08:01 1970071 /lib/libm-2.12.so
009c4000-009c5000 r--p 00027000 08:01 1970071 /lib/libm-2.12.so
009c5000-009c6000 rw-p 00028000 08:01 1970071 /lib/libm-2.12.so
04eb9000-04ed6000 r-xp 00000000 08:01 1970077 /lib/libgcc_s-4.4.6-20110824.so.1
04ed6000-04ed7000 rw-p 0001d000 08:01 1970077 /lib/libgcc_s-4.4.6-20110824.so.1
0520b000-052ec000 r-xp 00000000 08:01 153272 /usr/lib/libstdc++.so.6.0.13
052ec000-052f0000 r--p 000e0000 08:01 153272 /usr/lib/libstdc++.so.6.0.13
052f0000-052f2000 rw-p 000e4000 08:01 153272 /usr/lib/libstdc++.so.6.0.13
052f2000-052f8000 rw-p 00000000 00:00 0
08048000-08049000 r-xp 00000000 00:1f 3684 /mnt/hgfs/Linux_bak/_sock/f
08049000-0804a000 rw-p 00000000 00:1f 3684 /mnt/hgfs/Linux_bak/_sock/f
09205000-09226000 rw-p 00000000 00:00 0 [heap]
b7706000-b7709000 rw-p 00000000 00:00 0
b7720000-b7722000 rw-p 00000000 00:00 0
bfbb1000-bfbc6000 rw-p 00000000 00:00 0 [stack]

这个应该就是进程的虚拟地址空间
09205000-09226000 rw-p 00000000 00:00 0 [heap]

(0x09226000 - 0x09205000) / 1024 = 132 就是我说的循环131次的原因。谢谢大家的指导!
behire123 2012-10-10
  • 打赏
  • 举报
回复
[Quote=引用 27 楼 的回复:]

我突然发现我已经忘了这2个概念的定义了。麻烦楼主勤快点,解释一下吧。

引用 22 楼 的回复:
引用 21 楼 的回复:

楼主竟然能够忘了内存的实模式和保护模式,也算是很厉害啊。


兄弟能不能直接指导一下啊。你说的这两个模式我在看《linux源码解析第三版》(差不多叫这个名字)有说,但是好像不能解析这个问题啊。你直接给我解析一下不就成了!
[/Quote]

baidu嘛.....话说你们谁给指导一下啊。《操作系统设计与实现》这本书相应章节已经看完了,跟大学时候学的差不多一样的...还是找不到解析。
taodm 2012-10-10
  • 打赏
  • 举报
回复
我突然发现我已经忘了这2个概念的定义了。麻烦楼主勤快点,解释一下吧。

[Quote=引用 22 楼 的回复:]
引用 21 楼 的回复:

楼主竟然能够忘了内存的实模式和保护模式,也算是很厉害啊。


兄弟能不能直接指导一下啊。你说的这两个模式我在看《linux源码解析第三版》(差不多叫这个名字)有说,但是好像不能解析这个问题啊。你直接给我解析一下不就成了!
[/Quote]
behire123 2012-10-10
  • 打赏
  • 举报
回复
终于等到有人回复了,上面连续回复了3次不能回复了。。。之前有一出一直说错了。循环15次应该是 16 * (0 +15) / 2 = 120K 那样子。。。to楼上,我现在就是越写越糊涂了。这么简单的问题都没弄明白。
赵4老师 2012-10-10
  • 打赏
  • 举报
回复
对学习编程者的忠告:
眼过千遍不如手过一遍!
书看千行不如手敲一行!
手敲千行不如单步一行!
单步源代码千行不如单步对应汇编一行!

WinDbg
《深入解析Windows操作系统-Windows Internals》
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 的回复:]

taodm 大大呢??
[/Quote]
听taodm的没错
behire123 2012-10-09
  • 打赏
  • 举报
回复
taodm 大大呢??
behire123 2012-10-09
  • 打赏
  • 举报
回复
[Quote=引用 21 楼 的回复:]

楼主竟然能够忘了内存的实模式和保护模式,也算是很厉害啊。
[/Quote]

为了解析这个问题,把放着吃灰10年的数据结构和操作系统都翻出来找了,都没找到满意的答案,麻烦指点一下呗。
behire123 2012-10-09
  • 打赏
  • 举报
回复
[Quote=引用 21 楼 的回复:]

楼主竟然能够忘了内存的实模式和保护模式,也算是很厉害啊。
[/Quote]

兄弟能不能直接指导一下啊。你说的这两个模式我在看《linux源码解析第三版》(差不多叫这个名字)有说,但是好像不能解析这个问题啊。你直接给我解析一下不就成了!
taodm 2012-10-09
  • 打赏
  • 举报
回复
楼主竟然能够忘了内存的实模式和保护模式,也算是很厉害啊。
behire123 2012-10-09
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 的回复:]

可以参考linux源代码中的内存管理相关源代码。
也可以下载这个研究一下:
MSDN98中的例子walker又名pwalk。完整列出指定进程的内存使用情况,显示进程地址空间内容,装载哪些DLL,代码、数据、堆栈段分配在何处,可以用来检测内存泄漏,监测内存使用。
http://download.csdn.net/detail/zhao4zhong1/3667896
[/Quote]

就是看了linux关于内存管理方面的几本书,结合这个例子才有上述的疑问,跟自己理解的不一样。所以才向大家请教。
赵4老师 2012-10-09
  • 打赏
  • 举报
回复
可以参考linux源代码中的内存管理相关源代码。
也可以下载这个研究一下:
MSDN98中的例子walker又名pwalk。完整列出指定进程的内存使用情况,显示进程地址空间内容,装载哪些DLL,代码、数据、堆栈段分配在何处,可以用来检测内存泄漏,监测内存使用。
http://download.csdn.net/detail/zhao4zhong1/3667896
behire123 2012-10-09
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 的回复:]

内存管理不是以字节为单位而是以块或页为单位的。
[/Quote]

我知道,在我测试的系统中是以页为单位,一页是4K字节大小。在我的测试程序中,可以循环15次(固定)才收到段错误的信号,也就是说,这个进程可以连续访问到15K那么长的内存地址(而这些是我没有申请的),按照内存管理的请页机制,就是进程在运行的时候不会吧所有需要的物理内存页面load进来,等到需要的时候才load进来,那么我在测试程序里面连续访问了15K的内容。怎么中间没有触发缺页中断从而调用do_page_fault,然后检查查我访问了不属于进程虚拟空间的地址,从而产生SIGSEGV?难道进程一开始就把15K的地址空间load进去了?

而且,我把这两行改成这样:
p = p + i * K;
*p = 'A';
改成:
char * q = p + i * K;
*q = 'A';

可以循环到131次。也就是说只是改了一下写法,怎么又能多访问130K的地址?这个真不是很明白了。
赵4老师 2012-10-09
  • 打赏
  • 举报
回复
内存管理不是以字节为单位而是以块或页为单位的。
behire123 2012-10-09
  • 打赏
  • 举报
回复
大家帮忙解析一下阿。我找了很多资料都没能找到能说服自己的答案。
赵4老师 2012-10-09
  • 打赏
  • 举报
回复
为什么楼主犯罪了没有被警察抓?
behire123 2012-10-08
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 的回复:]

楼主还是找本坦尼博姆的《操作系统设计与实现》认真啃过了再看所谓的linux内核的书吧。
你基本概念上就错的。
[/Quote]

这本不是大学时候学的操作系统嘛,当年考试还考了95分的.....麻烦指导一下。
behire123 2012-10-08
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 的回复:]

楼主还是找本坦尼博姆的《操作系统设计与实现》认真啃过了再看所谓的linux内核的书吧。
你基本概念上就错的。
[/Quote]


那麻烦您,帮忙解析一下嘛?我这就去买。
大熊猫侯佩 2012-10-01
  • 打赏
  • 举报
回复
堆中的越界不能即时触发,如果你在NT下将调试堆打开,可以看到立即效果

linux下应该也有类似调试选项。
加载更多回复(9)

69,382

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧