首页 > 系统相关 >结构体强制转换导致的内存对齐问题

结构体强制转换导致的内存对齐问题

时间:2023-05-25 10:24:30浏览次数:43  
标签:STRUCT brief UINT16 MBX 内存 对齐 强制 TYPE PACKED

在开发ethercat协议栈邮箱通讯的过程中遇到一个BUG,主站协议栈传过来的邮箱数据包是正确的,但是到FOE服务处理时,使用结构体引用的方式处理时发现数据是不对的。

如下所示

 1 UINT8 MailboxServiceInd(TMBX MBXMEM *pMbx)
 2 {
 3     UINT8 result;
 4 
 5     /*only FoE is allowed in Boot mode*/
 6     if(bBootMode == TRUE && (MBX_TYPE_FOE != ((pMbx->MbxHeader.Flags[MBX_OFFS_TYPE] & MBX_MASK_TYPE) >> MBX_SHIFT_TYPE )))
 7         return MBXERR_UNSUPPORTEDPROTOCOL;
 8     switch ( (pMbx->MbxHeader.Flags[MBX_OFFS_TYPE] & MBX_MASK_TYPE) >> MBX_SHIFT_TYPE )
 9     {
10     case MBX_TYPE_COE:
11         /* CoE datagram received */
12         result = COE_ServiceInd((TCOEMBX MBXMEM *) pMbx);
13         break;
14     case MBX_TYPE_FOE:
15         /* FoE datagram received */
16         result = FOE_ServiceInd((TFOEMBX MBXMEM *) pMbx);        
17 
18     default:
19 
20         result = MBXERR_UNSUPPORTEDPROTOCOL;
21         break;
22     }
23 
24     return result;
25 }

在第1行传入了一个结构体类型为TMBX 的*pMbx,在第16行将其强制转换成了 TFOEMBX类型的结构体。

两个结构体定义如下:

TMBX

1 typedef struct MBX_STRUCT_PACKED_START
2 {
3     TMBXHEADER                      MbxHeader; /**< \brief Mailbox header*/
4     UINT16                          Data[(MAX_MBX_DATA_SIZE >> 1)]; /**< \brief Mailbox data*/
5 }MBX_STRUCT_PACKED_END
6 TMBX;

1 typedef struct MBX_STRUCT_PACKED_START
2 {
3       TMBXHEADER        MbxHeader; /**< \brief Mailbox header*/    
4       TFOEHEADER        FoeHeader; /**< \brief FoE header*/
5             UINT16            Data[((MAX_MBX_DATA_SIZE)-(FOE_HEADER_SIZE)) >> 1]; /**< \brief FoE Data buffer*/
6 }MBX_STRUCT_PACKED_END
7 TFOEMBX;
 1 typedef struct  MBX_STRUCT_PACKED_START
 2 {
 3     UINT16        OpCode; /**< \brief OpCode
 4                              *
 5                              * 1 : RRQ<br>
 6                              * 2 : WRQ<br>
 7                              * 3 : DATA<br>
 8                              * 4 : ACK<br>
 9                              * 5 : ERR<br>
10                              * 6 : BUSY*/
11     union MBX_STRUCT_PACKED_START
12     {
13         UINT32        Password; /**< \brief Password (used in Read request and Write request). 0 if unknown*/
14         UINT32        PacketNo; /**< \brief Packet number (used in DATA and ACK datagram)*/
15         UINT32        ErrorCode; /**< \brief Error code (used in ERR datagram)*/
16         struct MBX_STRUCT_PACKED_START
17         {
18             UINT16    Done; /**< \brief Done indication (used in BUSY datagram)*/
19             UINT16    Entire;  /**< \brief Entire indication (used in BUSY datagram)*/
20         }MBX_STRUCT_PACKED_END
21         Busy; /**< \brief Busy variable*/
22     }MBX_STRUCT_PACKED_END
23     Cmd; /**< \brief Command field*/
24 }MBX_STRUCT_PACKED_END
25 TFOEHEADER;
 1 typedef struct MBX_STRUCT_PACKED_START
 2 {
 3 UINT16 Length; /**< \brief Length*/
 4 UINT16 Address; /**< \brief Address*/
 5 
 6 UINT16 Flags[1]; /**< \brief Flags*/
 7 #define MBX_OFFS_TYPE 0 /**< \brief Protocol type offset*/
 8 #define MBX_MASK_TYPE 0x0F00 /**< \brief Protocol type mask*/
 9 #define MBX_SHIFT_TYPE 8 /**< \brief Protocol type shift*/
10 #define MBX_OFFS_COUNTER 0 /**< \brief Protocol counter offset*/
11 #define MBX_MASK_COUNTER 0xF000 /**< \brief Protocol counter mask*/
12 #define MBX_SHIFT_COUNTER 12 /**< \brief Protocol counter shift*/
13 }MBX_STRUCT_PACKED_END
14 TMBXHEADER;

可以看到出问题的是在TFOEHEADER结构体中,联合体为4个字节,编译时系统会把该头文件按照4字节对齐,从而实际占用8个字节而不是6个字节,使用sizeof()验证也确实如此,当把函数传入的结构体*pMbx强制转换成TFOEMBX结构体就会出现问题。

解决方式使用#pragma pack(2) 强制编译器按照2字节对齐

将TFOEHEADER结构体强制为2字节对齐,代码如下:

 1 #pragma pack(2)    //此结构体在强制邮箱结构体类型转换时内存对齐发生错误
 2 typedef struct  MBX_STRUCT_PACKED_START
 3 {
 4     UINT16        OpCode; /**< \brief OpCode
 5                              *
 6                              * 1 : RRQ<br>
 7                              * 2 : WRQ<br>
 8                              * 3 : DATA<br>
 9                              * 4 : ACK<br>
10                              * 5 : ERR<br>
11                              * 6 : BUSY*/
12     union MBX_STRUCT_PACKED_START
13     {
14         UINT32        Password; /**< \brief Password (used in Read request and Write request). 0 if unknown*/
15         UINT32        PacketNo; /**< \brief Packet number (used in DATA and ACK datagram)*/
16         UINT32        ErrorCode; /**< \brief Error code (used in ERR datagram)*/
17         struct MBX_STRUCT_PACKED_START
18         {
19             UINT16    Done; /**< \brief Done indication (used in BUSY datagram)*/
20             UINT16    Entire;  /**< \brief Entire indication (used in BUSY datagram)*/
21         }MBX_STRUCT_PACKED_END
22         Busy; /**< \brief Busy variable*/
23     }MBX_STRUCT_PACKED_END
24     Cmd; /**< \brief Command field*/
25 }MBX_STRUCT_PACKED_END
26 TFOEHEADER;
27 #pragma pack()

 

标签:STRUCT,brief,UINT16,MBX,内存,对齐,强制,TYPE,PACKED
From: https://www.cnblogs.com/mangorange/p/17430355.html

相关文章

  • Linux大页会立即占用分配内存
     Linux大页会立即占用分配内存 系统参数vm.nr_hugepages设置生效后,会立即分配对应内存。如下:[root@dev-app80~]#sysctl-qvm.nr_hugepagesvm.nr_hugepages=0[root@dev-app80~]#free-mtotalusedfreesharedbuff/cachea......
  • Redis的内存占用情况怎么样?
    Redis的内存占用情况怎么样大家都清楚Redis内存占用情况:与存储的数据量、配置参数、服务器内存大小等因素有关。在默认情况下,Redis 会使用尽可能多的内存,直到服务器的内存资源被占满。那么大家知道,为什么在默认情况下Redis 会使用尽可能多的内存吗?因为Redis 是一个基于内存的数......
  • 动态内存分配复习
    动态内存分配复习为什么要使用动态内存分配:在声明数组时,必须用一个编译常量指定数组长度,但是,数组的长度往往只有在运行的时候才能被确定,这是因为它所需要的内存空间取决于输入数据,但是容易浪费空间,又或者容易溢出malloc和free:malloc执行动态内存分配,free执行释放内存,当使用mal......
  • 使用ffmpeg将内存中的裸流打包成可播放的MP4文件,并输出到内存中
     前两天项目上有个需求,要求大概是这样的,输入端是一帧一帧的h264裸流(本示例只支持h264裸流,h265可基于本示例自己开发,在此我就不过多阐述了)和一个时间,要求输出根据这个时间来产生一个前后各延伸一段时间的视频(伴随录像),且伴随录像是可直接播放的MP4文件。但是产生的视频文件不是直接......
  • Trace32下对ARM内存访问Access Classes总结
    原内容来源于T32帮助文档debugger_arm.pdf的ARMSpecificImplementations->AccessClasses,这里记录方便查询。首先介绍AccessClasses都有哪些选项,然后介绍常见的AccessClasses组合,最后介绍如何创建合法的AccessClasses组合。1.单个AccessClasses描述2.常见AccessCla......
  • 防止Cannot allocate memory(无法分配内存)
    防止Cannotallocatememory(无法分配内存)值为不超过总内存的1%即可,我这里设置的是512M,min_free_kbytes表示强制Linux系统最低保留的空闲内存(Kbytes),如果系统可用内存低于设定的min_free_kbytes值,则默认系统启动oom-killer或强制重启。具体行为由内核参数vm.panic_on_oo......
  • Python变量内存管理
    变量三个组成部分:1变量名:反应变量值所描述的意义,并且可以用来引用变量值。2赋值符号:赋值。3变量值:存放数据,用来记录现实世界中的某种状态。常量计算机语言便设计了常量这个概念,也就是说常量相对于变量是一个不会变化的量。在Python中,虽然也和其他很多计算机语言一样拥有常量......
  • 内存映射大文件
    对于一些小文件,用普通的文件流就可以很好的解决,可是对于超大文件,比如2G或者更多,文件流就不行了,所以要使用API的内存映射的相关方法,即使是内存映射,也不能一次映射全部文件的大小,所以必须采取分块映射,每次处理一小部分。先来看几个函数CreateFile:打开文件GetFileSize:获取文件......
  • [css]总结-如何实现水平垂直都居中对齐?
    最后一种方法最简单普通盒子-居中对齐:方式一思路:外面的容器盒子outer让他只有一行.里面元素改为非块元素.因为vertical-align对块元素无效.然后用vertical-align:middle;垂直居中代码实现<!doctypehtml><htmllang="en"><head><metacharset="UTF-8">......
  • 一篇文章告诉你什么是Java内存模型
    在上篇并发编程Bug起源:可见性、有序性和原子性问题,介绍了操作系统为了提示运行速度,做了各种优化,同时也带来数据的并发问题,定义在单线程系统中,代码按照顺序从上往下顺序执行,执行不会出现问题。比如一下代码:inta=1;intb=2;intc=a+b;程序从上往下执行,最终c的结果......