segment fault 段错误 (core dumped)的起因分析!
来源:互联网 发布:cda数据分析师试题 编辑:程序博客网 时间:2024/04/29 07:01
很久一致被这个问题,这里试着去理解理解,首先写一个程序:
int main()
{
int *p=(int *)malloc(0);
*p=10;
printf("%p/n",p);
sleep(100);
printf("%d/n",getpid());
p=(int *)0x847e008;
*p=20;
return 0;
}
[qiqi@localhost test]$ ./hello
0x9f9e008
段错误 (core dumped)
[qiqi@localhost test]$
产生段错的根本的原因,首先要理解进程的地址空间,这里找到来一篇关于进程地址空间的好文章:
Linux进程地址空间
内核使用内存描述符结构体表示进程的地址空间,该结构体包含了和进程地址空间有关的全部信息。内存描述符由mm_struct结构体表示,定义在文件<linux/sched.h>中。进程地址空间由每个进程的线性地址区(vm_area_struct)组成。通过内核,进程可以给自己的地址空间动态的添加或减少线性区域。如下图是内存描述符mm_struct和线性区域描述符vm_area_struct的关系:
mm_users域记录正在使用该地址的进程数目。比如,如果两个进程共享该地址空间,那么mm_users的值便等于2;mm_count域是mm_struct的结构体的主引用计数,只要mm_users不为0,那么mm_count值就等于1.当mm_users值减为0(两个线程都退出)时,mm_count域的值才变为0。如果mm_count的值等于0,说明已经没有任何指向该mm_struct结构体的引用了,这时该结构体会被销毁。mmap和mm_rb这两个不同的数据结构描述的对象是相同的:该地址空间中的全部内存区域。
mmap结构体最为链表,利于简单,高效地遍历所有元素;而mm_rb结构体作为红-黑树,更适合搜索指定元素。所有的mm_struct结构体通过自身的mmlist域连接在一个双向链表中,该链表的首元素是init_mm内存描述符,它代表0号进程的地址空间。在进程的进程描述符中,mm域存放着该进程使用的内存描述符。copy_process函数利用copy_mm函数复制父进程的内存描述符。像vfork和clone系统调用指定的CLONE_VM标志的,仅仅需要在调用copy_mm()函数中将mm域指向其父进程的内存描述符就可以了:
- if(clone_flags & CLONE_VM){
- atomic_inc(&oldmm->mm_users);
- mm = oldmm;
- goto good_mm;
- }
而fork系统调用产生的子进程中的mm_struct结构体实际是通过文件kernel/fork.c中的alloc_mm()宏从mm_cachep slab缓存中分配得到的。通常,每个进程都有一个唯一的mm_struct结构体,即唯一的进程地址空间。是否共享地址空间,几乎是进程和Linux中所谓线程间本质上的唯一区别。
vm_start域指向线性区域的首地址,vm_end域指向尾地址之后的第一个字节,也就是说,vm_start是线性区域的开始地址,vm_end是线性区域的结束地址。vm_mm域指向和VMA相关的mm_struct结构体,注意每个VMA对其相关的mm_struct结构体来说都是唯一的,所以即使两个独立的进程将同一个文件映射到各自的地址空间,他们分别都会有一个vm_area_struct结构体来标志自己的内存区域;但是如果两个线程共享一个地址空间,那么他们也同时共享其中的所有vm_area_struct结构体。
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/proc_fs.h>
- #include <linux/list.h>
- #include <linux/types.h>
- #include <linux/sched.h>
- int read_myproc(char *page, char **start, off_t off, int count, int *eof, void *data){
- struct task_struct *p;
- struct vm_area_struct *first;
- p = list_entry(init_task.tasks.next, struct task_struct, tasks);
- printk("%20s %20s/n", "vm_area_start", "vm_area_end");
- for(first = p->mm->mmap; first != NULL;){
- printk("%20lx %20lx/n", first->vm_start, first->vm_end);
- first = first->vm->next;
- }
- return 0;
- }
- static int __init myproc_init(void){
- struct proc_dir_entry * e = create_proc_read_entry("myproc",0,NULL,read_myproc,NULL);
- e->read_proc = read_myproc;
- return 0;
- }
- static void __exit myproc_exit(void){
- removre_proc_entry("myproc",NULL);
- }
- module_init(myproc_init);
- module_exit(myproc_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("liwanpeng");
查看init进程的地址空间效果如下:
通过上面的理解我们可以看出进程就是通过mm_struct 来描述3G的线性地址空间,mm_struct通过
划分为一个个线性区,而每个线性区用一个vm_area_struct来描述,其实我们的mm_struct是与我们的可执行文件的结构体相关联的,首先看我们的mm_struct:
从上面可以看出mm_struct 包含了进程start_code,end_code,start_data,end_data等section段描述的结构,我们来看一个可执行文件的构成来看看,就拿上面的hello
[qiqi@localhost test]$ objdump -h hello
hello: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .interp 00000013 08048134 08048134 00000134 2**0 VMA代表虚拟地址,这里表示我们的程序运行是的虚拟地址起为
0x08048134
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .note.ABI-tag 00000020 08048148 08048148 00000148 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
......
10 .init 00000030 080482f4 080482f4 000002f4 2**2 init加载头最开始执行的
CONTENTS, ALLOC, LOAD, READONLY, CODE
11 .plt 00000070 08048324 08048324 00000324 2**2 跳转我们程序执行的头
CONTENTS, ALLOC, LOAD, READONLY, CODE
12 .text 000001cc 080483a0 080483a0 000003a0 2**4 ..即我们的代码段
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .fini 0000001c 0804856c 0804856c 0000056c 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .rodata 00000018 08048588 08048588 00000588 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
....
20 .dynamic 000000c8 08049654 08049654 00000654 2**2
CONTENTS, ALLOC, LOAD, DATA
21 .got 00000004 0804971c 0804971c 0000071c 2**2
CONTENTS, ALLOC, LOAD, DATA
....
23 .data 00000004 08049744 08049744 00000744 2**2 数据段
CONTENTS, ALLOC, LOAD, DATA
24 .bss 00000008 08049748 08049748 00000748 2**2 未初始化段
ALLOC
25 .comment 0000002c 00000000 00000000 00000748 2**0 未初始化的符号表
CONTENTS, READONLY
由链接过程可以看出main并不是真正的入口,是从.plt段中跳转过来的所有的程序都是从这里进入的,有一相当于jmp *main的跳转指令,
同时可以看出我们的可执行文件被分成以一个不同的section,这就和我们mm_struct中的线性区描述符对应起来了,比如我们的.data段就被加载到start_data与end_data地址空间的地方,其它的段与此类似!!!!!!,
那么为什么我们产生segment fault 呢,???因为我们访问的地址没有映射啊,!!!就是地址没有在mm_struct中描述的空间中,因为我们的
mm_struct并没有映射整个3G的空间啊,因为我们程序的每个段都是有大小限制的,.data,.text,.bss,.common等已经指名的section以外,
还有两个非常重要的线性区start_stack,end_stack(栈)与start_brk,end_brk(堆),如果我们访问的线性地址没有在这些地址空间中,那么就会产生segment fault,
接下来我们查看到底我们访问的地址到底有没有落在进程的mm_struct所描述的地址空间中:
[qiqi@localhost test]$ ./hello 先开一个终端运行程序运行程序由于设置了100s的延时,
pid:32549
0x9848008
0041d000-0043a000 r-xp 00000000 fd:00 278736 /lib/ld-2.13.so //这里是加载器
0043a000-0043b000 r--p 0001c000 fd:00 278736 /lib/ld-2.13.so
0043b000-0043c000 rw-p 0001d000 fd:00 278736 /lib/ld-2.13.so
0043e000-005c1000 r-xp 00000000 fd:00 278737 /lib/libc-2.13.so //这里是我们包含的库函数相应的可执行文件
005c1000-005c2000 ---p 00183000 fd:00 278737 /lib/libc-2.13.so
005c2000-005c4000 r--p 00183000 fd:00 278737 /lib/libc-2.13.so
005c4000-005c5000 rw-p 00185000 fd:00 278737 /lib/libc-2.13.so
005c5000-005c8000 rw-p 00000000 00:00 0
007b0000-007b1000 r-xp 00000000 00:00 0 [vdso]
08048000-08049000 r-xp 00000000 fd:00 1188391 /home/qiqi/test/hello //这里才是我们写的代码段,r-xp代表只读的
08049000-0804a000 rw-p 00000000 fd:00 1188391 /home/qiqi/test/hello // rw-p 表示可读写,为.data段
09848000-09869000 rw-p 00000000 00:00 0 [heap] //表示我们的堆预设的空间,当不足时在系统自动分配
b7856000-b7857000 rw-p 00000000 00:00 0
b7871000-b7873000 rw-p 00000000 00:00 0
bfa45000-bfa66000 rw-p 00000000 00:00 0 [stack] //这里是栈的预设空间
[root@localhost /]#
可以看出0x847e008没有落在我们上面的任何一个线性区!!!!
待续!!!!!!
- segment fault 段错误 (core dumped)的起因分析!
- Segment Fault(core dumped)
- Segmentation fault (core dumped)(段错误)
- Segmentation fault (core dumped) --段错误
- 关于Segmentation fault (core dumped)的错误
- 再谈Segmentation fault (core dumped)问题 -查找段错误原因
- Ubuntu--(8)段错误Segmentation fault (core dumped)
- C++迭代器iterator段错误segmentation fault (core dumped)
- 调用 inet_ntoa 函数产生的段错误 “Segmentation fault (core dumped)” 的原因及解决办法
- 调用 inet_ntoa 函数产生的段错误 “Segmentation fault (core dumped)” 的原因及解决办法
- c++ 段错误 (core dumped )原因分析
- keras-Segment fault(core dumped)解决方案
- 【Segment Fault】段错误解析
- 段错误 (core dumped) 之 core文件
- 段错误 (core dumped) 之 core文件
- Segmentation fault (core dumped)的解决办法
- Segmentation fault (core dumped)
- Segmentation fault (core dumped)
- MyEclipse8.6安装SVN
- (POJ1844)Sum
- LDAP常见错误对应编码表
- wifi workflow
- 启动一个线程是用run()还是start()? 为什么?
- segment fault 段错误 (core dumped)的起因分析!
- java不使用中间变量交换两个数
- 软件的层次化结构
- [整理]WinCE下直接启动自己应用程序的方法
- new和delete malloc和free
- 十种排序算法的C++泛型实现(转)
- 串行的blast+2.2.25本地化: blastdbcmd,makeblastdb,blastn.简介
- (POJ1862)Stripies
- asp.net 中页面静态化