內存對齊理論
a.數據的對齊(alignment)
指數據的地址和由硬件條件決定的內存塊大小之間的關係。一個變量的地址是它大小的倍數的時候,這就叫作天然對齊(naturally aligned)。
例如,對於一個32bit的變量,若是它的地址是4的倍數(地址的低兩位是0--備註1),那麼這就是天然對齊.
對齊的規則是由硬件引發的。一些體系的計算機在數據對齊這方面有着很嚴格的要求。在一些系統上,一個不對齊的數據的載入可能會引發進程的陷入。
在另一些系統,對不對齊的數據的訪問是安全的,但卻會引發性能的降低。在編寫可移植的代碼的時候,對齊的問題是必須避免的,全部的類型都該天然對齊。
b.預對齊內存的分配
在大多數狀況下,編譯器和C庫透明地幫你處理對齊問題。POSIX標明瞭經過malloc(),calloc(),和realloc()返回的地址對於任何的C類型來講都是對齊的。
在Linux中,這些函數返回的地址在32位系統是以8字節爲邊界對齊,在64位系統是以16字節爲邊界對齊的。有時候,對於更大的邊界,程序員須要動態的對齊。
雖然動機是多種多樣的,但最多見的是直接塊I/O的緩存的對齊或者其它的軟件對硬件的交互,所以,POSIX 1003.1d提供一個叫作posix_memalign( )的函數
c.數據對齊的性能提高
對於現代計算機硬件來講,內存只能經過特定的對齊地址(好比按照機器字)進行訪問。舉個例子來講,
好比在64位的機器上,無論咱們是要讀取第0個字節仍是要讀取第1個字節,在硬件上傳輸的信號都是同樣的。
由於它都會把地址0到地址7,這8個字節所有讀到CPU,只是當咱們是須要讀取第0個字節時,丟掉後面7個字節,
當咱們是須要讀取第1個字節,丟掉第1個和後面6個字節。
假設咱們要讀取2個字節,這兩個字節恰好落在兩個機器字內時,就出現兩次訪問內存的狀況,同時經過一些邏輯計算才能獲得最終的結果。
所以,爲了更好的提高性能,咱們須儘可能將結構體作到機器字(或倍數)對齊,而結構體中一些頻繁訪問的字段也儘可能安排在機器字對齊的位置。
備註1:
二進制現象解釋
對於二進制數 *****000 不管高5位怎麼變化,該數必定8的倍數(對於二進制數 ******00 不管高6位怎麼變化,該數必定4的倍數)
由於是二進制, *****000 除以 2 ,結果和 0*****00 一致,至關於二進制數的每一位都降1階,
那麼 *****000 一共能夠除以3個2,便可以除以8,所以 *****000 必定是8的倍數
/* 內存對齊 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#ifndef NGX_ALIGNMENT
#define NGX_ALIGNMENT sizeof(unsigned long) /* platform word */
#endif
/*
設計說明
sizeof(unsigned long)
在32位操做平臺上,unsigned long 的大小是4個字節,恰巧32位平臺的機器字也是4個字節
在64位操做平臺上,unsigned long 的大小是8個字節,恰巧64位平臺的機器字也是8個字節
*/
#define ngx_align_ptr(p, a) \
(unsigned char *) (((unsigned int) (p) + ((unsigned int) a - 1)) & ~((unsigned int) a - 1))
/*
設計說明:
ngx_align_ptr宏定義設計詳解
(unsigned int) (p) 把地址當作整數進行操做,爲了計算 整數p 加多少纔是 a 的倍數
(unsigned int) (p) + ((unsigned int) a - 1 將 整數p 向上擴充,由於是內存對齊,地址只能向後跑。向前跑就可能內存越界
假設a是8,(((unsigned int) (p) + ((unsigned int) a - 1)) & ~((unsigned int) a - 1)) 只會影響 低3位,若是 整數p 在低位上有值,
那麼 整數p 就會比原來小,而 整數p + a - 1 整數p的低3位上所有加1,若是 整數p 低3位上有值,確定會產生進位,
這樣能夠確保操做後的 整數p 絕對比 原來的整數p 大
(((unsigned int) (p) + ((unsigned int) a - 1)) & ~((unsigned int) a - 1)) 假設a是8,該操做就會將 整數p 後3位變成0
*/
int main()
{
//示例用法
void * p = (void *)0x2379b1;
//進行內存對齊操做
p = ngx_align_ptr(p, NGX_ALIGNMENT);
return 0;
}
posix_memalign
函數原型
int posix_memalign(void **memptr, size_t alignment, size_t size);
函數說明
調用posix_memalign( )成功時會返回size字節的動態內存,而且這塊內存的地址是alignment的倍數。參數alignment必須是2的冪,
仍是void指針的大小的倍數。返回的內存塊的地址放在了memptr裏面,函數返回值是0.
返回值
調用失敗時,沒有內存會被分配,memptr的值沒有被定義,返回以下錯誤碼之一:
EINVAL
參數不是2的冪,或者不是void指針的倍數。
ENOMEM
沒有足夠的內存去知足函數的請求。
注意
posix_memalign函數,errno不會被設置,只能經過返回值獲得。
由posix_memalign( )得到的內存經過free( )釋放