導航:首頁 > 操作系統 > linuxbuserror

linuxbuserror

發布時間:2023-03-09 09:18:15

linux 出現 segment fault怎麼解決

1. 段錯誤是什麼
一句話來說,段錯誤是指訪問的內存超出了系統給這個程序所設定的內存空間,例如訪問了不存在的內存地址、訪問了系統保護的內存地址、訪問了只讀的內存地址等等情況。這里貼一個對於「段錯誤」的准確定義(參考Answers.com):

A segmentation fault (often shortened to segfault) is a particular error condition that can occur ring the operation of computer software. In short, a segmentation fault occurs when a program attempts to access a memory location that it is not allowed to access, or attempts to access a memory location in a way that is not allowed (e.g., attempts to write to a read-only location, or to overwrite part of the operating system). Systems based on processors like the Motorola 68000 tend to refer to these events as Address or Bus errors.

Segmentation is one approach to memory management and protection in the operating system. It has been superseded by paging for most purposes, but much of the terminology of segmentation is still used, "segmentation fault" being an example. Some operating systems still have segmentation at some logical level although paging is used as the main memory management policy.

On Unix-like operating systems, a process that accesses invalid memory receives the SIGSEGV signal. On Microsoft Windows, a process that accesses invalid memory receives the STATUS_ACCESS_VIOLATION exception.

2. 段錯誤產生的原因
2.1 訪問不存在的內存地址

#include<stdio.h>
#include<stdlib.h>
void main()
{
int *ptr = NULL;
*ptr = 0;
}

2.2 訪問系統保護的內存地址

#include<stdio.h>
#include<stdlib.h>
void main()
{
int *ptr = (int *)0;
*ptr = 100;
}

2.3 訪問只讀的內存地址

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void main()
{
char *ptr = "test";
strcpy(ptr, "TEST");
}

2.4 棧溢出

#include<stdio.h>
#include<stdlib.h>
void main()
{
main();
}

等等其他原因。
3. 段錯誤信息的獲取
程序發生段錯誤時,提示信息很少,下面有幾種查看段錯誤的發生信息的途徑。
3.1 dmesg
dmesg可以在應用程序crash掉時,顯示內核中保存的相關信息。如下所示,通過dmesg命令可以查看發生段錯誤的程序名稱、引起段錯誤發生的內存地址、指令指針地址、堆棧指針地址、錯誤代碼、錯誤原因等。以程序2.3為例:
panfeng@ubuntu:~/segfault$ dmesg
[ 2329.479037] segfault3[2700]: segfault at 80484e0 ip 00d2906a sp bfbbec3c error 7 in libc-2.10.1.so[cb4000+13e000]

3.2 -g
使用gcc編譯程序的源碼時,加上-g參數,這樣可以使得生成的二進制文件中加入可以用於gdb調試的有用信息。以程序2.3為例:
panfeng@ubuntu:~/segfault$ gcc -g -o segfault3 segfault3.c

3.3 nm
使用nm命令列出二進制文件中的符號表,包括符號地址、符號類型、符號名等,這樣可以幫助定位在哪裡發生了段錯誤。以程序2.3為例:

panfeng@ubuntu:~/segfault$ nm segfault3
08049f20 d _DYNAMIC
08049ff4 d _GLOBAL_OFFSET_TABLE_
080484dc R _IO_stdin_used
w _Jv_RegisterClasses
08049f10 d __CTOR_END__
08049f0c d __CTOR_LIST__
08049f18 D __DTOR_END__
08049f14 d __DTOR_LIST__
080484ec r __FRAME_END__
08049f1c d __JCR_END__
08049f1c d __JCR_LIST__
0804a014 A __bss_start
0804a00c D __data_start
08048490 t __do_global_ctors_aux
08048360 t __do_global_dtors_aux
0804a010 D __dso_handle
w __gmon_start__
0804848a T __i686.get_pc_thunk.bx
08049f0c d __init_array_end
08049f0c d __init_array_start
08048420 T __libc_csu_fini
08048430 T __libc_csu_init
U __libc_start_main@@GLIBC_2.0
0804a014 A _edata
0804a01c A _end
080484bc T _fini
080484d8 R _fp_hw
080482bc T _init
08048330 T _start
0804a014 b completed.6990
0804a00c W data_start
0804a018 b dtor_idx.6992
080483c0 t frame_mmy
080483e4 T main
U memcpy@@GLIBC_2.0

3.4 ldd
使用ldd命令查看二進製程序的共享鏈接庫依賴,包括庫的名稱、起始地址,這樣可以確定段錯誤到底是發生在了自己的程序中還是依賴的共享庫中。以程序2.3為例:
panfeng@ubuntu:~/segfault$ ldd ./segfault3
linux-gate.so.1 => (0x00e08000)
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00675000)
/lib/ld-linux.so.2 (0x00482000)

4. 段錯誤的調試方法
4.1 使用printf輸出信息
這個是看似最簡單但往往很多情況下十分有效的調試方式,也許可以說是程序員用的最多的調試方式。簡單來說,就是在程序的重要代碼附近加上像printf這類輸出信息,這樣可以跟蹤並列印出段錯誤在代碼中可能出現的位置。
為了方便使用這種方法,可以使用條件編譯指令#ifdef DEBUG和#endif把printf函數包起來。這樣在程序編譯時,如果加上-DDEBUG參數就能查看調試信息;否則不加該參數就不會顯示調試信息。
4.2 使用gcc和gdb
4.2.1 調試步驟
1、為了能夠使用gdb調試程序,在編譯階段加上-g參數,以程序2.3為例:
panfeng@ubuntu:~/segfault$ gcc -g -o segfault3 segfault3.c

2、使用gdb命令調試程序:

panfeng@ubuntu:~/segfault$ gdb ./segfault3
GNU gdb (GDB) 7.0-ubuntu
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show ing"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/panfeng/segfault/segfault3...done.
(gdb)

3、進入gdb後,運行程序:

(gdb) run
Starting program: /home/panfeng/segfault/segfault3

Program received signal SIGSEGV, Segmentation fault.
0x001a306a in memcpy () from /lib/tls/i686/cmov/libc.so.6
(gdb)

從輸出看出,程序2.3收到SIGSEGV信號,觸發段錯誤,並提示地址0x001a306a、調用memcpy報的錯,位於/lib/tls/i686/cmov/libc.so.6庫中。
4、完成調試後,輸入quit命令退出gdb:

(gdb) quit
A debugging session is active.

Inferior 1 [process 3207] will be killed.

Quit anyway? (y or n) y

4.2.2 適用場景
1、僅當能確定程序一定會發生段錯誤的情況下使用。
2、當程序的源碼可以獲得的情況下,使用-g參數編譯程序。
3、一般用於測試階段,生產環境下gdb會有副作用:使程序運行減慢,運行不夠穩定,等等。
4、即使在測試階段,如果程序過於復雜,gdb也不能處理。
4.3 使用core文件和gdb
在4.2節中提到段錯誤會觸發SIGSEGV信號,通過man 7 signal,可以看到SIGSEGV默認的handler會列印段錯誤出錯信息,並產生core文件,由此我們可以藉助於程序異常退出時生成的core文件中的調試信息,使用gdb工具來調試程序中的段錯誤。
4.3.1 調試步驟
1、在一些Linux版本下,默認是不產生core文件的,首先可以查看一下系統core文件的大小限制:
panfeng@ubuntu:~/segfault$ ulimit -c
0

2、可以看到默認設置情況下,本機Linux環境下發生段錯誤時不會自動生成core文件,下面設置下core文件的大小限制(單位為KB):
panfeng@ubuntu:~/segfault$ ulimit -c 1024
panfeng@ubuntu:~/segfault$ ulimit -c
1024

3、運行程序2.3,發生段錯誤生成core文件:
panfeng@ubuntu:~/segfault$ ./segfault3
段錯誤 (core mped)

4、載入core文件,使用gdb工具進行調試:

panfeng@ubuntu:~/segfault$ gdb ./segfault3 ./core
GNU gdb (GDB) 7.0-ubuntu
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show ing"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/panfeng/segfault/segfault3...done.

warning: Can't read pathname for load map: 輸入/輸出錯誤.
Reading symbols from /lib/tls/i686/cmov/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib/tls/i686/cmov/libc.so.6
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2
Core was generated by `./segfault3'.
Program terminated with signal 11, Segmentation fault.
#0 0x0018506a in memcpy () from /lib/tls/i686/cmov/libc.6

從輸出看出,同4.2.1中一樣的段錯誤信息。
5、完成調試後,輸入quit命令退出gdb:
(gdb) quit

4.3.2 適用場景
1、適合於在實際生成環境下調試程序的段錯誤(即在不用重新發生段錯誤的情況下重現段錯誤)。
2、當程序很復雜,core文件相當大時,該方法不可用。
4.4 使用objmp
4.4.1 調試步驟
1、使用dmesg命令,找到最近發生的段錯誤輸出信息:
panfeng@ubuntu:~/segfault$ dmesg
... ...
[17257.502808] segfault3[3320]: segfault at 80484e0 ip 0018506a sp bfc1cd6c error 7 in libc-2.10.1.so[110000+13e000]

其中,對我們接下來的調試過程有用的是發生段錯誤的地址:80484e0和指令指針地址:0018506a。
2、使用objmp生成二進制的相關信息,重定向到文件中:
panfeng@ubuntu:~/segfault$ objmp -d ./segfault3 > segfault3Dump

其中,生成的segfault3Dump文件中包含了二進制文件的segfault3的匯編代碼。
3、在segfault3Dump文件中查找發生段錯誤的地址:

panfeng@ubuntu:~/segfault$ grep -n -A 10 -B 10 "80484e0" ./segfault3Dump
121- 80483df: ff d0 call *%eax
122- 80483e1: c9 leave
123- 80483e2: c3 ret
124- 80483e3: 90 nop
125-
126-080483e4 <main>:
127- 80483e4: 55 push %ebp
128- 80483e5: 89 e5 mov %esp,%ebp
129- 80483e7: 83 e4 f0 and $0xfffffff0,%esp
130- 80483ea: 83 ec 20 sub $0x20,%esp
131: 80483ed: c7 44 24 1c e0 84 04 movl $0x80484e0,0x1c(%esp)
132- 80483f4: 08
133- 80483f5: b8 e5 84 04 08 mov $0x80484e5,%eax
134- 80483fa: c7 44 24 08 05 00 00 movl $0x5,0x8(%esp)
135- 8048401: 00
136- 8048402: 89 44 24 04 mov %eax,0x4(%esp)
137- 8048406: 8b 44 24 1c mov 0x1c(%esp),%eax
138- 804840a: 89 04 24 mov %eax,(%esp)
139- 804840d: e8 0a ff ff ff call 804831c <memcpy@plt>
140- 8048412: c9 leave
141- 8048413: c3 ret

通過對以上匯編代碼分析,得知段錯誤發生main函數,對應的匯編指令是movl $0x80484e0,0x1c(%esp),接下來打開程序的源碼,找到匯編指令對應的源碼,也就定位到段錯誤了。
4.4.2 適用場景
1、不需要-g參數編譯,不需要藉助於core文件,但需要有一定的匯編語言基礎。
2、如果使用了gcc編譯優化參數(-O1,-O2,-O3)的話,生成的匯編指令將會被優化,使得調試過程有些難度。
4.5 使用catchsegv
catchsegv命令專門用來撲獲段錯誤,它通過動態載入器(ld-linux.so)的預載入機制(PRELOAD)把一個事先寫好的庫(/lib/libSegFault.so)載入上,用於捕捉斷錯誤的出錯信息。

panfeng@ubuntu:~/segfault$ catchsegv ./segfault3
Segmentation fault (core mped)
*** Segmentation fault
Register mp:

EAX: 00000000 EBX: 00fb3ff4 ECX: 00000002 EDX: 00000000
ESI: 080484e5 EDI: 080484e0 EBP: bfb7ad38 ESP: bfb7ad0c

EIP: 00ee806a EFLAGS: 00010203

CS: 0073 DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 007b

Trap: 0000000e Error: 00000007 OldMask: 00000000
ESP/signal: bfb7ad0c CR2: 080484e0

Backtrace:
/lib/libSegFault.so[0x3b606f]
??:0(??)[0xc76400]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0xe89b56]
/build/buildd/eglibc-2.10.1/csu/../sysdeps/i386/elf/start.S:122(_start)[0x8048351]

Memory map:

② linux c 段錯誤如何定位

1. 段錯誤是什麼
一句話來說,段錯誤是指訪問的內存超出了系統給這個程序所設定的內存空間,例如訪問了不存在的內存地址、訪問了系統保護的內存地址、訪問了只讀的內存地址等等情況。這里貼一個對於「段錯誤」的准確定義(參考Answers.com):
A segmentation fault (often shortened to segfault) is a particular error condition that can occur ring the operation of computer software. In short, a segmentation fault occurs when a program attempts to access a memory location that it is not allowed to access, or attempts to access a memory location in a way that is not allowed (e.g., attempts to write to a read-only location, or to overwrite part of the operating system). Systems based on processors like the Motorola 68000 tend to refer to these events as Address or Bus errors.
Segmentation is one approach to memory management and protection in the operating system. It has been superseded by paging for most purposes, but much of the terminology of segmentation is still used, "segmentation fault" being an example. Some operating systems still have segmentation at some logical level although paging is used as the main memory management policy.
On Unix-like operating systems, a process that accesses invalid memory receives the SIGSEGV signal. On Microsoft Windows, a process that accesses invalid memory receives the STATUS_ACCESS_VIOLATION exception.
2. 段錯誤產生的原因
2.1 訪問不存在的內存地址
#include<stdio.h>
#include<stdlib.h>
void main()
{
int *ptr = NULL;
*ptr = 0;
}
2.2 訪問系統保護的內存地址
#include<stdio.h>
#include<stdlib.h>
void main()
{
int *ptr = (int *)0;
*ptr = 100;
}
2.3 訪問只讀的內存地址
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void main()
{
char *ptr = "test";
strcpy(ptr, "TEST");
}
2.4 棧溢出
#include<stdio.h>
#include<stdlib.h>
void main()
{
main();
}
等等其他原因。
3. 段錯誤信息的獲取
程序發生段錯誤時,提示信息很少,下面有幾種查看段錯誤的發生信息的途徑。
3.1 dmesg
dmesg可以在應用程序crash掉時,顯示內核中保存的相關信息。如下所示,通過dmesg命令可以查看發生段錯誤的程序名稱、引起段錯誤發生的內存地址、指令指針地址、堆棧指針地址、錯誤代碼、錯誤原因等。以程序2.3為例:
panfeng@ubuntu:~/segfault$ dmesg
[ 2329.479037] segfault3[2700]: segfault at 80484e0 ip 00d2906a sp bfbbec3c error 7 in libc-2.10.1.so[cb4000+13e000]
3.2 -g
使用gcc編譯程序的源碼時,加上-g參數,這樣可以使得生成的二進制文件中加入可以用於gdb調試的有用信息。以程序2.3為例:
panfeng@ubuntu:~/segfault$ gcc -g -o segfault3 segfault3.c
3.3 nm
使用nm命令列出二進制文件中的符號表,包括符號地址、符號類型、符號名等,這樣可以幫助定位在哪裡發生了段錯誤。以程序2.3為例:
panfeng@ubuntu:~/segfault$ nm segfault3
08049f20 d _DYNAMIC
08049ff4 d _GLOBAL_OFFSET_TABLE_
080484dc R _IO_stdin_used
w _Jv_RegisterClasses
08049f10 d __CTOR_END__
08049f0c d __CTOR_LIST__
08049f18 D __DTOR_END__
08049f14 d __DTOR_LIST__
080484ec r __FRAME_END__
08049f1c d __JCR_END__
08049f1c d __JCR_LIST__
0804a014 A __bss_start
0804a00c D __data_start
08048490 t __do_global_ctors_aux
08048360 t __do_global_dtors_aux
0804a010 D __dso_handle
w __gmon_start__
0804848a T __i686.get_pc_thunk.bx
08049f0c d __init_array_end
08049f0c d __init_array_start
08048420 T __libc_csu_fini
08048430 T __libc_csu_init
U __libc_start_main@@GLIBC_2.0
0804a014 A _edata
0804a01c A _end
080484bc T _fini
080484d8 R _fp_hw
080482bc T _init
08048330 T _start
0804a014 b completed.6990
0804a00c W data_start
0804a018 b dtor_idx.6992
080483c0 t frame_mmy
080483e4 T main
U memcpy@@GLIBC_2.0
3.4 ldd
使用ldd命令查看二進製程序的共享鏈接庫依賴,包括庫的名稱、起始地址,這樣可以確定段錯誤到底是發生在了自己的程序中還是依賴的共享庫中。以程序2.3為例:
panfeng@ubuntu:~/segfault$ ldd ./segfault3
linux-gate.so.1 => (0x00e08000)
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00675000)
/lib/ld-linux.so.2 (0x00482000)

③ 如何解決bus error

一,Bus Error究竟是指什麼

Bus Error,即匯流排錯誤。

引發原因:

CPU處於性能方面的考慮,要求對數據進行訪問時都必須是地址對齊的。如果發現進行的不是地址對齊的訪問,就會發送SIGBUS信號給進程,使進程產生 core mp。RISC包括SPARC(一種微處理器架構)都是這種類型的晶元。x86系列CPU都支持不對齊訪問,也提供了開關禁用這個機制。x86架構不要求對齊訪問的時候,必定會有性能代價。例如,對int的訪問應該是4位元組對齊的,即地址應該是4的倍數,對short則是2位元組對齊的,地址應該是2的倍數。

Bus Error也有可能是因為機器物理問題或者訪問無效物理地址,但這種情況非常少見。

Linux平台上執行malloc(),如果沒有足夠的RAM,Linux不是讓malloc()失敗返回,而是向當前進程分發SIGBUS信號。
注: 對該點執懷疑態度,有機會可自行測試確認當前系統反應。

SIGBUS與SIGSEGV信號的一般區別如下:

1) SIGBUS(Bus error)意味著指針所對應的地址是有效地址,但匯流排不能正常使用該指針。通常是未對齊的數據訪問所致。

2) SIGSEGV(Segment fault)意味著指針所對應的地址是無效地址,沒有物理內存對應該地址。

二,例子程序:

1 int main(){
2
3
4
5
6 #if defined(__GNUC__)
7 # if defined(__i386__)
8
9 __asm__("pushf/norl $0x40000,(%esp)/npopf");
10 # elif defined(__x86_64__)
11
12 __asm__("pushf/norl $0x40000,(%rsp)/npopf");
13 # endif
14 #endif
15
16
17
18
19
20
21
22
23
24 short array[16];
25
26 int * p = (int *) &array[1];
27 *p = 1;
28
29 return 1;
30 }

short類型大小為2個位元組,其地址必是2的倍數。而對於int指針來說,能夠使用以訪問數據的地址應該是4的倍數,轉化arrary[1]的地址為int *並訪問,系統會發出SIGBUS信號,導致程序崩潰。

wiki上的例子:

http://en.wikipedia.org/wiki/Bus_error#Bus_error_example
#include <stdlib.h>

int main( int argc, char ** argv) {
int * iptr;
char * cptr;

#if defined(__GNUC__)
# if defined(__i386__)

__asm__( "pushf/n orl $0x40000,(%esp)/n popf" ) ;
# elif defined(__x86_64__)

__asm__( "pushf/n orl $0x40000,(%rsp)/n popf" ) ;
# endif
#endif

cptr = malloc( sizeof ( int ) + 1) ;

iptr = ( int * ) ++ cptr;

* iptr = 42 ;

return 0 ;
}
$ gcc -ansi sigbus.c -o sigbus
$ ./sigbus
Bus error
$ gdb ./sigbus
(gdb) r
Program received signal SIGBUS , Bus error.
0x080483ba in main ()
(gdb) x/i $pc
0x80483ba <main+54>: mov DWORD PTR [eax],0x2a
(gdb) p/x $eax
$1 = 0x804a009
(gdb) p/t $eax & (sizeof(int) - 1)
$2 = 1

三,編譯器和硬體平台相關性

上述已經描述,對於x86平台,默認允許非對齊訪問,只不過會有性能代價。開啟檢測可以使用上述代碼中的宏。

這段程序如果用Sun Studio編譯器的話,運行就沒有問題。這是因為Sun Studio默認對32位編譯使用的參數是-xmemalign=8i,其中i選項設置明確指明不產生SIGBUS信號。
不過如果編譯成64位程序,Sun Studio使用的-xmemalign=8s,其中s選項設置意味對這種非對齊訪問產生SIGBUS信號,則仍舊會遇到這個錯誤。

如果堅持在SPARC上使用GCC去編譯這種代碼,可以如下進行:
GCC有一個Type Attributes特性,例如在需人工對齊的變數後加上:__attribute__ ((aligned (4))); 其意義就是指定偏移量為4的倍數。比如:
short array[10] __attribute__ ((aligned (4)));

不過這個屬性只對Linker連接器可見的變數有效,也就是說對local variable無效。而且這種特性作用粒度比較大,比如這里只對第一個元素有作用,並不為數組的每個成員設置偏移量。如果一定要針對local variable或者數組的每個成員進行偏移量設置,可以使用union類型:
union {
short s;
int i;
}

閱讀全文

與linuxbuserror相關的資料

熱點內容
760貼片機編程視頻 瀏覽:333
歐姆龍plc編程第36講 瀏覽:915
我的世界如何將一個伺服器弄崩 瀏覽:8
php網站訪問量代碼 瀏覽:431
怠速壓縮機咔咔響 瀏覽:176
怎麼才能修改APP中的數據 瀏覽:688
哪裡有搶單的app 瀏覽:462
演算法概率題 瀏覽:465
長方形拉伸的命令 瀏覽:279
python代碼函數編程技術 瀏覽:194
java正則式 瀏覽:429
外包程序員好進嗎 瀏覽:384
雲伺服器服務模型架構 瀏覽:901
刪文件夾什麼指令 瀏覽:509
極速抖音已加密怎麼辦 瀏覽:603
matlab拉格朗日演算法框圖 瀏覽:430
華為公司計算機視覺演算法顧問 瀏覽:254
夏老師講的單片機 瀏覽:298
在編程中如何將圖片放大 瀏覽:163
appstore怎麼看是否付費 瀏覽:603