筆者小白一枚,在開發LogViewer工具時,遇到了記憶體對齊的問題。網上有很多關於記憶體對齊問題的部落格,本文僅分享一下實際案例,希望能幫到讀者去理解記憶體對齊。
LogViewer是一款自研的協定棧信令解析工具,協定棧通過TCP將信令上傳給LogViewer進行解析(信令本質上就是C語言中的結構體)。筆者所接觸的4G協定棧是基於x86系統開發的,而5G協定棧是基於x64系統開發的。因此,5G協定棧結構體的儲存預設是8位元組對齊的,而LogViewer在沒有更改設定的情況下依舊按照4位元組對齊去解析,於是導致數據異常。
爲了方便闡述記憶體對齊的問題,筆者將具體信令抽象成如下MEM_ALIGN_DEMO_T結構體,讀者可忽略信令及其內容的實際意義。
typedef struct mem_align_demo_t
{
unsigned short A; // 2bytes
unsigned short B; // 2bytes
unsigned long C; // 8bytes
unsigned long D; // 8bytes
unsigned char E; // 1bytes
}MEM_ALIGN_DEMO_T;
閒言少敘,直接上圖。建議讀者遇到記憶體對齊問題時,可以利用Excel將數據的儲存和解析過程形象化。下面 下麪對圖示幾個部分做具體的說明:
筆者做了一個簡單的c程式mem_align.c,該程式實現MEM_ALIGN_DEMO_T結構體成員的value、addr、size的列印,並且以16進位制輸出實際儲存在記憶體中的結構體數據,讀者可以在此基礎上進行驗證和學習。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct mem_align_demo_t
{
unsigned short A; // 2bytes
unsigned short B; // 2bytes
unsigned long C; // 8bytes
unsigned long D; // 8bytes
unsigned char E; // 1bytes
}MEM_ALIGN_DEMO_T;
int main()
{
unsigned char *buffer = NULL;
unsigned int i = 0;
/** Define and print struct */
MEM_ALIGN_DEMO_T t;
memset(&t, 0, sizeof(t));
t.A = 0x0208;
t.B = 0x001c;
t.C = 0x00062f8100002a00;
t.D = 0x00062f81000029e3;
t.E = 0x02;
printf("=================================================================\n");
printf("\tvalue\t\t\taddr\t\t\tsize\n");
printf("t.A\t%04x\t\t\t%p\t\t%ld\n", t.A, &(t.A), sizeof(t.A));
printf("t.B\t%04x\t\t\t%p\t\t%ld\n", t.B, &(t.B), sizeof(t.B));
printf("t.C\t%016lx\t%p\t\t%ld\n", t.C, &(t.C), sizeof(t.C));
printf("t.D\t%016lx\t%p\t\t%ld\n", t.D, &(t.D), sizeof(t.D));
printf("t.E\t%02x\t\t\t%p\t\t%ld\n", t.E, &(t.E), sizeof(t.E));
printf("t\t--\t\t\t%p\t\t%ld\n", &t, sizeof(t));
printf("=================================================================\n");
/** Output actual stored struct content in hex */
buffer = (unsigned char *)malloc(sizeof(t));
memcpy(buffer, (unsigned char *)(&t), sizeof(t));
printf("Actual Struct Content:\n");
for (i=0; i<sizeof(t); i++)
{
printf("%02x", *(buffer + i));
}
printf("\n");
printf("=================================================================\n");
free(buffer);
return 1;
}
建立mem_align.c檔案,將程式碼複製下來儲存(筆者系統ubuntu18.04.3-x64,gcc版本7.5.0)。
編譯:gcc -Wall -g mem_align.c -o mem_align
執行:./mem_align
輸出結果如下,注意觀察各成員的記憶體地址addr的變化,請讀者自行和上述Excel表格對應分析,不做贅述。另外,整個結構體的大小爲32位元組,而不是2+2+8+8+1=21個位元組,自然是因爲8位元組對齊時補齊佔位造成的。
root@ubuntu:/home/share/study# gcc -Wall -g mem_align.c -o mem_align
root@ubuntu:/home/share/study# ./mem_align
=================================================================
value addr size
t.A 0208 0x7ffde716fac0 2
t.B 001c 0x7ffde716fac2 2
t.C 00062f8100002a00 0x7ffde716fac8 8
t.D 00062f81000029e3 0x7ffde716fad0 8
t.E 02 0x7ffde716fad8 1
t -- 0x7ffde716fac0 32
=================================================================
Actual Struct Content:
08021c0000000000002a0000812f0600e3290000812f06000200000000000000
=================================================================
root@ubuntu:/home/share/study#