1. 程式人生 > >segment fault 段錯誤 (core dumped)的起因分析!

segment fault 段錯誤 (core dumped)的起因分析!

核心使用記憶體描述符結構體表示程序的地址空間,該結構體包含了和程序地址空間有關的全部資訊。記憶體描述符由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域指向其父程序的記憶體描述符就可以了:

  1. if (clone_flags & CLONE_VM){   
  2.     atomic_inc(&oldmm->mm_users);   
  3.     mm = oldmm;   
  4.     goto  good_mm;   
  5. }  

      而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結構體。

  1. #include <linux/module.h>  
  2. #include <linux/init.h>  
  3. #include <linux/proc_fs.h>  
  4. #include <linux/list.h>  
  5. #include <linux/types.h>  
  6. #include <linux/sched.h>   
  7. int  read_myproc(char  *page, char  **start, off_t off, int  count, int  *eof, void  *data){   
  8.     struct  task_struct *p;   
  9.     struct  vm_area_struct *first;   
  10.     p = list_entry(init_task.tasks.next, struct  task_struct, tasks);   
  11.     printk("%20s %20s/n" "vm_area_start" "vm_area_end" );   
  12.     for (first = p->mm->mmap; first != NULL;){   
  13.         printk("%20lx %20lx/n" , first->vm_start, first->vm_end);   
  14.         first = first->vm->next;   
  15.     }   
  16.     return  0;   
  17. }   
  18. static  int  __init myproc_init(void ){   
  19.     struct  proc_dir_entry * e = create_proc_read_entry("myproc" ,0,NULL,read_myproc,NULL);   
  20.     e->read_proc = read_myproc;   
  21.     return  0;    
  22. }   
  23. static  void  __exit myproc_exit(void ){   
  24.     removre_proc_entry("myproc" ,NULL);   
  25. }   
  26. module_init(myproc_init);   
  27. module_exit(myproc_exit);   
  28. MODULE_LICENSE("GPL" );   
  29. 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

[[email protected] 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所描述的地址空間中:

[[email protected] test]$ ./hello   先開一個終端執行程式執行程式由於設定了100s的延時,
pid:32549
0x9848008

[[email protected] /]# cat /proc/32549/maps     檢視我們相應程序的資訊
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]                                  //這裡是棧的預設空間
[[email protected] /]#

可以看出0x847e008沒有落在我們上面的任何一個線性區!!!!

待續!!!!!!

相關推薦

segment fault 錯誤 (core dumped)的起因分析

核心使用記憶體描述符結構體表示程序的地址空間,該結構體包含了和程序地址空間有關的全部資訊。記憶體描述符由mm_struct結構體表示,定義在檔案<linux/sched.h>中。進程地址空間由每個程序的線性地址區(vm_area_struct)組成。通過核心,程序可以給自己的地址空間動態的新增或

Linux下如何生成core dump 文件(解決segment fault錯誤的問題)

http alt 系統設置 images mit 只讀 功能 lin 設置 Linux下的C程序常常會因為內存訪問等原因造成segment fault(段錯誤),如果此時core dump 的功能是打開的,在運行我們的可執行程序時就會生成一個名為core的文件,然後我們就可

PHP請求https域名發生segment fault錯誤

PHP使用file_get_contents或curl請求https的域名均會發生segment fault的錯誤。 問題PHP程式碼如下,執行該PHP命令會發生segment fault: var_dump(file_get_contents("https://www.

Segmentation Fault 錯誤的原因分析

1 訪問不存在的記憶體地址        在一個三個節點的連結串列中,有pHeader->pNext->pNext我們將pHeader->pNext後面的節點全部刪除,因此只有pHeader->pNext        程式碼中,使用 pHeader-

關於Segmentation fault(錯誤)探究

在編寫演算法競賽習題2.5時,資料無法正常輸入到檔案data1.out,用gdb除錯的時候,報錯: Program received signal SIGSEGV, Segmentation fault. __chkstk_ms () at …/…/…/src

NoiLinux中編寫cpp程式出現錯誤的解決方案分析

NOIP資訊學奧賽複賽中全員需要使用noilinux來編寫程式,大佬們教我們用vim,然後vim編寫程式時不能逐步除錯,只能斷言輸出(可能是井底之蛙沒發現),而且在出現段錯誤的時候,只會華麗麗的出來如下類似語句:      /bin/bash:行 1 :286

GDB arm-linux交叉編譯移植和使用方法(特別是對於正在執行的程式或者錯誤的程式進行分析

測試程式碼中的test1是用來定位堆疊段錯誤,Delay函式是用來定位程式阻塞,都可以用gdb定位出來,如下:  (1)測試程式執行時首先會有個段錯誤:./gdbtest & [[email protected] user0]$ [65334.020000] pgd = c3e14000 [

Segmentation fault錯誤除錯總結

Segmetation fault也叫做段錯誤,引發的原因有好多,這裡我們只說一下段錯誤發生時的除錯方法。 方法1:加列印printf。這是最基本的往往也很有效的方法,在哪裡Core掉就會在哪裡停止列印--一目瞭然。同時這種方法也存在一個致命缺陷:如果恰巧Core掉的地方沒

GDB遠端除錯(二)之用gdb解決segmentation-fault錯誤,看gdb的使用(嵌入式)

最近在除錯程式碼的時候鬧人的segmentation-fault段錯誤,又來煩人了,不過程式碼都是自己寫的 ,有事也是作繭自縛,自作自受,先自責下。 來看看在串列埠段錯誤給了我們什麼資訊: 可以看出 Fault addr=0x328,出錯的地址在 0x328,這個哪 啊

造成segment fault,產生core dump的可能原因!

一 造成segment fault,產生core dump的可能原因1.記憶體訪問越界 a) 由於使用錯誤的下標,導致陣列訪問越界 b) 搜尋字串時,依靠字串結束符來判斷字串是否結束,但是字串沒有正常的使用結束符 c) 使用strcpy, strcat, sprintf, strcmp, strcasecmp

Linux Segmentation Fault 錯誤 產生原因除錯方法

轉載至:http://www.cnblogs.com/panfeng412/archive/2011/11/06/segmentation-fault-in-linux.html http://blog.sina.com.cn/s/blog_69cc2f0b0100qgl

造成segment fault,產生core dump的可能原因

1.記憶體訪問越界  a) 由於使用錯誤的下標,導致陣列訪問越界  b) 搜尋字串時,依靠字串結束符來判斷字串是否結束,但是字串沒有正常的使用結束符  c) 使用strcpy, strcat, sprintf, strcmp, strcasecmp等字串操作函

【Z】錯誤Segment Fault定位,即core dump文件與gdb定位

rect fun 發生 toolbar ulimit top wid title 沒有 使用C++開發系統有時會出現段錯誤,即Segment Fault。此類錯誤程序直接崩潰,通常沒有任何有用信息輸出,很難定位bug,因而無從解決問題。今天我們介紹core dump文件,

捕獲Linux錯誤(Segment fault)並且列印錯誤堆疊

Linux上跑伺服器如果遇到程式崩潰是一件很苦惱的事情, 再碰到重現很難的BUG, 估計只能通過傳統的排查方法進行. 在編寫本文前, 筆者使用過諸如libunwind等庫進行錯誤時堆疊列印, 但是其本身由於需要引用第三方庫, 使用還是稍微麻煩. 經過Google後, 居然找到一篇好文, 其通過捕獲SI

嵌入式 使用gdb除錯錯誤segment fault

我們打算使用gdb去解決為什麼下面的程式(檔案為segfault.c)引起了段錯誤的問題。下面的這段程式是從使用者那裡讀入一行文字字串然後顯示在螢幕上。然而,如下當前的程式並不會如期執行... [cpp] view plaincopyprint? <s

linux之Segment Fault錯誤分析[2]

        簡而言之,產生段錯誤就是訪問了錯誤的記憶體段,一般是你沒有許可權,或者根本就不存在對應的實體記憶體,尤其常見的是訪問0地址.         一 般來說,段錯誤就是指訪問的記憶體超出了系統所給這個程式的記憶體空間,通常這個值是由gdtr來儲存的,他是一個48位的暫存器,其中的32位是儲存由

使用gdb除錯錯誤segment fault

我們打算使用gdb去解決為什麼下面的程式(檔案為segfault.c)引起了段錯誤的問題。下面的這段程式是從使用者那裡讀入一行文字字串然後顯示在螢幕上。然而,如下當前的程式並不會如期執行... #include <stdio.h> #include <

linux之Segment Fault錯誤分析[1]

最近在Linux環境下做C語言專案,由於是在一個原有專案基礎之上進行二次開發,而且專案工程龐大複雜,出現了不少問題,其中遇到最多、花費時間最長的問題就是著名的“段錯誤”(Segmentation Fault)。藉此機會系統學習了一下,這裡對Linux環境下的段錯誤做個小結,方便以後同類問題的排查與解決。 1

GDB除錯php擴充套件錯誤(Segmentation fault (core dumped))

(gdb) print (char *)(executor_globals.function_state_ptr->function)->common.function_name (gdb) print (char *)executor_globals.act

錯誤(sgementation fault)和核心已轉儲(core dump)的除錯方法

本文主要介紹gdb+core的除錯方法,其他幾種方法的介紹參考:段錯誤產生原因及除錯方法彙總 一、printf方法除錯 二、gdb方法除錯 三、gdb+core檔案的方法除錯,步驟如下,具體參考:gd