Bus error

在x86+Linux上寫的程序,在PC機上運行得很好。可是使用ARM的gcc進行交叉編譯,再送到DaVinci目標板上運行的時候,出現了Bus error。
出現的位置如下(其中Debug的內容是我在程序中添加的調試信息):
[email protected]:~# arm_v5t_le-gcc -g shit.c
[email protected]:~# ./a.out
Debug: malloc space for the actual data: temp_buf = 0x13118
Debug: in my_recvn()
Debug: nleft = 52
Bus error
打開調試器進行調試:
[email protected]:~# gdb a.out
GNU gdb 6.3 (MontaVista 6.3-20.0.22.0501131 2005-07-22)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type “show copying” to see the conditions.
There is absolutely no warranty for GDB. Type “show warranty” for details.
This GDB was configured as “armv5tl-montavista-linuxeabi”…Using host libthread_db library “/lib/tls/libthread_db.so.1”.

(gdb) run // 運行程序
Starting program: /home/zpf/a.out
Debug: in get_program_info()
Debug: in conn_server(char *err_buf_ptr)
Debug: gonna create a socket
Debug: gonna fill in the serv_addr structure
Debug: gonna connect to a server
Debug: gonna send LIN_RST
Debug: in my_sendn()
Debug: send 4 bytes to server:
Debug: gonna receive LIN_RSP
Debug: in my_recvn()
Debug: nleft = 3
Debug: received first 3 bytes from server: 7
Debug: gonna check if 3rd byte is the package type
Debug: received package length = 55
Debug: malloc space for the actual data: temp_buf = 0x13118
Debug: in my_recvn()
Debug: nleft = 52

Program received signal SIGBUS, Bus error. // 在這裏出現了錯誤
0x00009624 in alloc_prog_mem (detail_buf=0x13118 “\001\002”,
err_buf_ptr=0xbefffc40 “”) at shit.c:631
631 g_data_ptr->progtype_num = (short )ptr ;
(gdb) print ptr // 查看一下ptr的值
$1 = 0x13119 “\002” // 地址起始是奇數!!!
(gdb) set ptr=0x1311a // 想改一下
(gdb) continue
Continuing.

Program terminated with signal SIGBUS, Bus error.
The program no longer exists. // 可惜程序已經退出
(gdb) quit

其中,g_data_ptr->progtype_num是一個short類型的值。
把強制類型轉換改爲用memcpy()寫值之後,再調試
[email protected]:~# gdb test
GNU gdb 6.3 (MontaVista 6.3-20.0.22.0501131 2005-07-22)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type “show copying” to see the conditions.
There is absolutely no warranty for GDB. Type “show warranty” for details.
This GDB was configured as “armv5tl-montavista-linuxeabi”…Using host libthread_db library “/lib/tls/libthread_db.so.1”.

(gdb) break 626 // 把剛剛的那句強制類型轉換變成內存拷貝
Breakpoint 1 at 0x9630: file test.c, line 626.
(gdb) run
Starting program: /home/zpf/test
Debug: in get_program_info()
Debug: in conn_server(char *err_buf_ptr)
Debug: gonna create a socket
Debug: gonna fill in the serv_addr structure
Debug: gonna connect to a server
Debug: gonna send LIN_RST
Debug: in my_sendn()
Debug: send 4 bytes to server:
Debug: gonna receive LIN_RSP
Debug: in my_recvn()
Debug: nleft = 3
Debug: received first 3 bytes from server: 7
Debug: gonna check if 3rd byte is the package type
Debug: received package length = 55
Debug: malloc space for the actual data: temp_buf = 0x13118
Debug: in my_recvn()
Debug: nleft = 52

Breakpoint 1, alloc_prog_mem (detail_buf=0x13118 “\001\002”,
err_buf_ptr=0xbefffc40 “”) at test.c:626
warning: Source file is more recent than executable.

626 memcpy(&(g_data_ptr->prog_num), ptr, 2) ; // 在這一句中斷
(gdb) print ptr // 再看看ptr
1=0x1311b\003//(gdb)continue//Continuing.Debug:sumprogtype=2,sumprog=3Debug:gonnamallocspaceforprogtypeptrDebug:gonnamallocspaceforprogptrDebug:inmvpkg2proglist()Debug:gonnasetProgramTypeprogramtypenameDebug:ProgramTypeprogramtypenamesetOKDebug:in == *ptr, j = 0
Debug: g_data_ptr->progtype_ptr[j].prog_ptr = temp_prog_ptr
Debug: in @ == *ptr, ptr = 0x13126
Debug: temp_prog_ptr->format = *ptr
Debug: temp_prog_ptr->program_id = (int )ptr
Debug: gonna set Program program_name
Debug: Program program_name set OK
Debug: finished one loop of while
Debug: in @ == *ptr, ptr = 0x1312f
Debug: temp_prog_ptr->format = *ptr
Debug: temp_prog_ptr->program_id = (int )ptr
Debug: gonna set Program program_name
Debug: Program program_name set OK
Debug: finished one loop of while
Debug: gonna set ProgramType program_type_name
Debug: ProgramType program_type_name set OK
Debug: in $ == *ptr, j = 1
Debug: g_data_ptr->progtype_ptr[j].prog_ptr = temp_prog_ptr
Debug: in @ == *ptr, ptr = 0x13142
Debug: temp_prog_ptr->format = *ptr
Debug: temp_prog_ptr->program_id = (int )ptr
Debug: gonna set Program program_name
Debug: Program program_name set OK
Debug: finished one loop of while
program type[0]
program_type_id = 1
program_type_name = love
program_num = 2
prog_ptr = 0x131d8
program[0]
program_id = 1001
program_name = you
format = 1
program[1]
program_id = 1002
program_name = me
format = 2
program type[1]
program_type_id = 2
program_type_name = hatred
program_num = 1
prog_ptr = 0x13248
program[0]
program_id = 2005
program_name = kill
format = 5
Debug: gonna return an OK
Debug: Entering send_exit_requstion()
Debug: in conn_server(char *err_buf_ptr)
Debug: gonna create a socket
Debug: gonna fill in the serv_addr structure
Debug: gonna connect to a server
Debug: gonna send EXIT_RST
Debug: in my_sendn()
Debug: send 4 bytes to server:
Debug: in my_recvn()
Debug: nleft = 4
Debug: gonna return an OK

Program exited normally. // 執行通過了!!!!
(gdb)

總結:
問題總算找到了,就是我企圖在一個奇數地址起始的地方強制類型轉換得到一個short值。
在Intel系列處理器上,可以在任一奇數內存地址儲存任何變量或數組,不會導致任何致命的錯誤影響,只是效率可能會降低。但在DaVinci上,這一點不行。所以必須對大於一個字節的數據類型小心謹慎,比較安全的方法是使用內存拷貝函數memcpy(),或者使用下面的代替方法:
// 先定義一個聯合體
union {
short short_val ;
char short_byte[2] ;
} myshort ;
// 然後,把程序中本來應該是
// g_data_ptr->progtype_num = (short )ptr ;
// ptr += 2 ;
// 這兩句的地方換成下面五句:
myshort.short_byte[0] = *ptr ;
ptr++ ;
myshort.short_byte[1] = *ptr ;
ptr++ ;
g_data_ptr->progtype_num = myshort.short_val ;
// 當然,最簡單的方法是換成下面兩句:
// memcpy(&(g_data_ptr->progtype_num), ptr, 2) ;
// ptr += 2 ;

對於這個問題的進一步探討:
在DaVinci上應該注意內存編址模式的問題。
struct {
char struc_char ;
int struc_int ;
short struc_short ;
long struct_long ;
} struc_val ;
在寬鬆模式下,儘管struc_char只有1個字節,struc_short佔2個字節,但編譯器可能給這兩個變量分別分配了4個字節,結果整個結構的大小變成了16個字節,而在編譯器設爲緊湊模式時,則正好是11個字節。根據計算機數據總線的位數,不同的編址模式存取數據的速度不一樣。我認爲在符合總線字長的情況下,效率是最高的,因爲只需進行一次總線操作。
內存編址模式會影響字節對齊方式,字節對齊操作可以解決以下兩個主要的問題:
1.訪存效率問題;一般的編譯器要對內存進行對齊,在處理變量時,編譯器會根據一定的設置將長短不同的變量的數據長度進行對齊以加快內存處理速度。
2.強制類型轉換問題:在x86上,字節不對齊的操作只會影響效率,但是在DaVinci上,可能就是一個Bus error, 因爲它要求必須字節對齊。
字節對齊的準則
1.數據類型自身的對齊值:對於char型數據,其自身對齊值爲1,對於short型爲2,對於int,long,float,double類型,其自身對齊值爲4字節。
2.結構體的自身對齊值:其成員中自身對齊值最大的那個值。
3.指定對齊值:#pragma pack (value)時的指定對齊值value。
4.數據成員、結構體和類的有效對齊值:自身對齊值和指定對齊值中小的那個值。
對於平時定義變量,儘可能先定義長度爲4的倍數的變量,然後是長度是2的變量,最後是長度爲1的變量。

通過測試,GCC編譯器是按照4字節對齊存放於內存的。而我還沒有發現更改編址模式的參數。程序如下:

include

int main()
{
struct {
char struc_char ;
int struc_int ;
short struc_short ;
long struct_long ;
} struc_val ;
char c_char ;
int i_int ;
short s_short ;
long l_long ;

printf(“sizeof(struc_val) = %d\n”, sizeof(struc_val));
printf(“sizeof(c_char) = %d\n”, sizeof(c_char));
printf(“sizeof(i_int) = %d\n”, sizeof(i_int));
printf(“sizeof(s_short) = %d\n”, sizeof(s_short));
printf(“sizeof(l_long) = %d\n”, sizeof(l_long));

printf(“address of struc_val = %p\n”, &struc_val);
printf(“address of struc_char = %p\n”, &(struc_val.struc_char));
printf(“address of struc_int = %p\n”, &(struc_val.struc_int));
printf(“address of struc_short = %p\n”, &(struc_val.struc_short));
printf(“address of struct_long = %p\n”, &(struc_val.struct_long));
printf(“address of c_char = %p\n”, &c_char);
printf(“address of i_int = %p\n”, &i_int);
printf(“address of s_short = %p\n”, &s_short);
printf(“address of l_long = %p\n”, &l_long);

return 0 ;
}
測試結果:
sizeof(struc_val) = 16
sizeof(c_char) = 1
sizeof(i_int) = 4
sizeof(s_short) = 2
sizeof(l_long) = 4
address of struc_val = 0xbf885278
address of struc_char = 0xbf885278
address of struc_int = 0xbf88527c
address of struc_short = 0xbf885280
address of struct_long = 0xbf885284
address of c_char = 0xbf885277
address of i_int = 0xbf885270
address of s_short = 0xbf88526e
address of l_long = 0xbf885268

所以對於一個32位的數據來講,如果其沒有在4字節整除的內存地址處存放,那麼處理器就需要2個總線週期對其進行訪問。
0x08 | byte8 | byte9 | byteA | byteB |
0x04 | byte4 | byte5 | byte6 | byte7 |
0x00 | byte0 | byte1 | byte2 | byte3 |
對於我剛剛的那個出現Bus error的程序,假設指針ptr剛好是指向了byte3(地址是0x0),然後想進行short強制類型轉換,使用byte3,byte4來構成一個short類型的值,由於第一次總線的數據只有byte0,byte1,byte2,byte3,取不到byte4,這在DaVinci板子上,就是一個Bus error了,因爲沒有達到邊界對齊。如果ptr指的是byte2(地址0x02),就沒有問題了。因爲0x02地址值是sizeof(short)的整數倍。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章