转自:http://bavon.bokee.com/5429805.html

在x86+Linux上写的程序,在PC机上运行得很好。可是使用ARM的gcc进行交叉编译,再送到DaVinci目标板上运行的时候,出现了Bus error。
出现的位置如下(其中Debug的内容是我在程序中添加的调试信息):
root@211.69.193.189:~# arm_v5t_le-gcc -g shit.c
root@211.69.193.189:~# ./a.out
Debug: malloc space for the actual data: temp_buf = 0x13118
Debug: in my_recvn()
Debug: nleft = 52
Bus error
打开调试器进行调试:
root@211.69.193.189:~# 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()写值之后,再调试
root@211.69.193.189:~# 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: sum_progtype = 2 , sum_prog = 3
Debug: gonna malloc space for progtype_ptr
Debug: gonna malloc space for prog_ptr
Debug: in mv_pkg2prog_list()
Debug: gonna set ProgramType program_type_name
Debug: ProgramType program_type_name set OK
Debug: 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)的整数倍。

关于arm处理器 内存编址模式 与 字节对齐方式 (转)的更多相关文章

  1. ARM处理器的运行模式

    ARM处理器的7种运行模式 用户模式( usr ):ARM处理器正常的程序执行状态: 快速中断模式( fiq ):用于高速数据传输或通道处理: 外部中断模式( irq):用于通常的中断处理: 管理模式 ...

  2. c语言结构体在内存中存储,字节对齐

    注意: 出于效率的考虑,C语言引入了字节对齐机制,一般来说,不同的编译器字节对齐机制有所不同,但还是有以下3条通用准则: (1)结构体变量的大小能够被其最宽基本类型成员的大小所整除: (2)结构体每个 ...

  3. C++类成员在内存中的存储及对齐方式

    前言:数据对齐的基本理论参见文章:http://www.cnblogs.com/MyBlog-Richard/articles/5993448.html 一.空类的大小 C++中空类的大小是1,这是因 ...

  4. ARM处理器的寄存器,ARM与Thumb状态,7中运行模式 【转】

    转自:http://blog.chinaunix.net/uid-28458801-id-3494646.html ARM处理器工作模式一共有 7 种 : USR  模式    正常用户模式,程序正常 ...

  5. ARM处理器的寄存器,ARM与Thumb状态,7中运行模式

     ** ARM处理器的寄存器,ARM与Thumb状态,7中运行模式  分类: 嵌入式 ARM处理器工作模式一共有 7 种 : USR  模式    正常用户模式,程序正常执行模式 FIQ模式(Fast ...

  6. arm的字节对齐问题总结(转)

    问题由来:pc的lsb总是0,因为代码至少要字对齐.cm3的指令至少是半字对齐的(16) 一.啥是字对齐?为啥要字对齐? 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访 ...

  7. 【C语言】字节对齐(内存对齐)

    数据对齐 1.  对齐原则: [原则1]数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma p ...

  8. c++内存中字节对齐问题详解

    一.什么是字节对齐,为什么要对齐?    现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址 ...

  9. struct/class等内存字节对齐问题详解

    问题引入 定义一个结构体的一般形式为: struct 结构体名 { //类型说明符 成员名; }; 例如有如下结构体: struct Stu { int id; char sex; float hig ...

随机推荐

  1. 一步步学习NHibernate(1)——NHibernate介绍

    请注明转载地址:http://www.cnblogs.com/arhat 第十五章 从本章开始,老魏将给大家一起学习NHibernate这个流行的ORM框架,本来老魏想要和大家一起探讨微软的EF框架的 ...

  2. 认识HTML

    基本框架: 1 <html> 2 <head> 3 <title>This Is Title</title> 4 </head> 5 6 & ...

  3. 关于 Java 性能监控您不知道的 5 件事,第 1 部分

    责怪糟糕的代码(或不良代码对象)并不能帮助您发现瓶颈,提高 Java? 应用程序速度,猜测也不能帮您解决.Ted Neward 引导您关注 Java 性能监控工具,从5 个技巧开始,使用Java 5 ...

  4. centos 卸载vsftpd方法

    centos 卸载vsftpd方法 在服务器上安装了vsftpd,配置出错需要卸载vsftpd.卸载vsftpd的命令如下: 1 [root@localhost ~]# rpm -aq vsftpd2 ...

  5. 在MAC中安装Compass的方法 (转)

    在MAC中通过gem命令安装compass时会出异常,原因是compass版本更新了,一些运行时所用到的依赖软件的版本没能得到更新,故而出现错误.例如,用以下命令安装compass: $ gem in ...

  6. Python中zip()函数用法

    定义:zip([iterable, …])zip()是Python的一个内建函数,它接受一系列可迭代的对象作为参数,将对象中对应的元素打包成一个个tuple(元组),然后返回由这些tuples组成的l ...

  7. [转载]C# Random 生成不重复随机数

    Random 类 命名空间:System 表示伪随机数生成器,一种能够产生满足某些随机性统计要求的数字序列的设备. 伪随机数是以相同的概率从一组有限的数字中选取的.所选数字并不具有完全的随机性,因为它 ...

  8. 【深度学习系列3】 Mariana CNN并行框架与图像识别

    [深度学习系列3] Mariana CNN并行框架与图像识别 本文是腾讯深度学习系列文章的第三篇,聚焦于腾讯深度学习平台Mariana中深度卷积神经网络Deep CNNs的多GPU模型并行和数据并行框 ...

  9. 关于hibernate的实体类中有集合类型转化成JSON的工具类 - 怀念今天的专栏 - 博客频道

    Json 来源:http://blog.csdn.net/zczzsq/article/details/18697045#1536434-hi-1-4387-42d97150898b1af15ddaa ...

  10. 10个基于 Ruby on Rails 构建的顶级站点

    本文系国内 ITOM 行业领军企业 OneAPM 工程师翻译整理自 Raviraj Hegde 的文章 Top Sites Built with Ruby on Rails. 就其本身而言,Ruby ...