天天看點

03Nginx源碼分析之分析記憶體對齊宏(ngx_align,ngx_align_ptr)

03Nginx源碼分析之分析記憶體對齊宏ngx_align,ngx_align_ptr

因為ngx_align與ngx_align_ptr實際上是一樣的,隻不過int與char*位址的差別。是以下面隻分析ngx_align_ptr即可。

1 ngx_align_ptr

// p 是記憶體指針,a 是對齊位元組數(必須是2的幂次方結果)
#define ngx_align_ptr(p, a)                                                   \
    (u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))

           

以下假設為64位機器:

先分析最右邊的~((uintptr_t) a - 1):

當a為2的0次方,即2^0 - 1 = 0時,運算取反後64位全為1;

當a為2的1次方,即2^1 - 1 = 1時,運算取反後64位的隻有低一位為0;

當a為2的2次方,即2^2 - 1 = 3時,運算取反後64位的隻有低兩位為0;

可以得出下面結果:

幂次方為幾低位就有幾個零。

對齊位元組數 換成2的幂次方 64位二進制
1 2^0 1111111111111111111111111111111111111111111111111111111111111111
2 2^1 1111111111111111111111111111111111111111111111111111111111111110
4 2^2 1111111111111111111111111111111111111111111111111111111111111100
8 2^3 1111111111111111111111111111111111111111111111111111111111111000
16 2^4 1111111111111111111111111111111111111111111111111111111111110000
32 2^5 1111111111111111111111111111111111111111111111111111111111100000
64 2^6 1111111111111111111111111111111111111111111111111111111111000000

然後左邊的結果不需要分析,直接看例子。

2 例子

下面的代碼是從ngx的哈希表初始化函數中抽出的。并且本例子按照ngx_cacheline_size = 64位對齊測試。

#include <iostream>
#include <string>
#include <windows.h>
#include <tchar.h>
#include <deque>
#include <assert.h>
using namespace std;

#define ngx_align(d, a)     (((d) + (a - 1)) & ~(a - 1))
#define ngx_cacheline_size 64	//本例子按64位對齊

int main(int argc, char* argv[])
{

	int i = 0;
	short int test[10];
	int size = 10;
	int len = 0;

	//測試對齊位數,4位元組代表32位,8位元組代表64,不過一旦上面設定64,與機器是x86還是x64無關,最終開辟的記憶體仍然一樣
	for (i = 0; i < size; i++) {
		test[i] = sizeof(void*);
	}

	//列印
	for (i = 0; i < size; i++) {
		cout << test[i] << " ";
	}
	cout << endl;

	//将數組test中的數值設為一百多,然後使用對齊宏進行對齊,驗證對齊後的數值
	//數組中的數值代表我們要開辟的記憶體,這一步的意思是:當實際要開辟的記憶體大小對齊後,實際将占用多少記憶體(這個例子是參考ngx哈希表的初始化函數)
	for (i = 0; i < size; i++) {
		test[i] = i + 100;
	}

	for (i = 0; i < size; i++) {
		if (test[i] == sizeof(void *)) {
			continue;
		}

		//cout << &test[i] << endl;
		test[i] = static_cast<short>(ngx_align(test[i], ngx_cacheline_size));
		//cout << &test[i] << endl;
		//len += test[i];
	}

	//列印對齊後的記憶體開辟大小
	for (i = 0; i < size; i++) {
		cout << test[i] << " ";
	}
	cout << endl;

	cout << sizeof(ngx_hash_elt_t *) << endl;

	system("pause");

	return 0;
}
           

結果:

03Nginx源碼分析之分析記憶體對齊宏(ngx_align,ngx_align_ptr)

可以看到,将8+100後,按照64位對齊,每個數組元素的值變成128的大小,即實際占用128的記憶體。這就是記憶體對齊宏的作用。隻有了解了本例子,你才能去了解ngx的哈希表結構源碼。

3 總結

對比上一篇的ngx_memalign(size_t alignment, size_t size, ngx_log_t *log)記憶體對齊函數,兩者的差別是(實際上一篇在開頭的注意講過,即下圖):

03Nginx源碼分析之分析記憶體對齊宏(ngx_align,ngx_align_ptr)

這裡再次總結一下兩者的一些使用場合和功能上的差別。

  • 1)ngx_memalign記憶體對齊主要是針對開辟記憶體後傳回的記憶體首位址,若想實作記憶體對齊,下一次開辟記憶體必須傳相應的倍數(ngx_memalign的參1),該函數的作用直覺的可以看出,一般是為了開辟大記憶體時進行大記憶體的記憶體對齊,因為參1可以傳256,512這些大倍數。
  • 2)而對齊宏ngx_align,是直接将已經開辟好的記憶體首位址傳進(參數d),按照一定倍數直接對齊(即參數a),對齊後占用的記憶體大小大于實際開辟的大小,例如上面的例子。一般該對齊倍數(參數a)一般是32位,64位,128位這些正常的對齊倍數。
  • 3)再次說明記憶體對齊宏的參數。
/*
* d為指針的首位址,a為想要對齊的位元組數。
* 其中:一般想要對齊的位元組數會通過sizeof(void*)去自動比對電腦
* 的32位或者64位,例如參考nginx的哈希表的NGX_HASH_ELT_SIZE宏定義。
* 
* 記憶體對齊後,CPU就能一大塊一大塊的讀取記憶體。
*/
#define ngx_align(d, a)     (((d) + (a - 1)) & ~(a - 1))
           

至此,對于記憶體對齊宏的介紹到此完畢。

繼續閱讀