swoole源碼學習——協程編號的管理和分配
swoole中定義了cidmap結構體用於管理和分配協程(coroutine)的編號,其定義位於swoole/swoole_coroutine.c 573行起
typedef struct cidmap
{
uint32_t nr_free; //nr_free表示剩餘可用cid的數目,
char page[4096]; //共4096*8=32768個bit
} cidmap_t;
這裏的nr_free表示剩餘可用cid的數目。
而page[4096]則是因爲一般在c語言中,一個char字符的大小爲1字節也就是8個bit。
那麼4096*8=32768 個bit,每個bit就代表其對應的編號有沒有分配出去,0未分配,1已分配。
cidmap變量聲明位於swoole/swoole_coroutine.c 579行起
從這裏原有的註釋中我們就可以得知,最多可分配的協程編號是32768個
/* 1 <= cid <= 32768 */
static cidmap_t cidmap = { 0x8000, {0} }; //0x8000代表16進制的8000,相當於10進制的32768
與cidmap相關的兩個操作函數alloc_cidmap(分配編號) 和 free_cidmap(釋放編號) 聲明位於swoole/swoole_coroutine.c 29行起
static int alloc_cidmap(); //分配
static void free_cidmap(int cid); //釋放
主要看一下alloc_cidmap函數,其實現位於swoole/swoole_coroutine.c 629行起
static int alloc_cidmap()
{
int cid;
if (cidmap.nr_free == 0) //檢查是否還有未分配的協程編號
{
return -1;
}
cid = find_next_zero_bit(&cidmap.page, last_cid); //獲取下一個值爲0的bit(協程編號)
if (test_and_set_bit(cid, &cidmap.page)) //將標誌位從0改成1
{
--cidmap.nr_free; //將未分配的協程編號數減一
last_cid = cid; //更換最後一個分配出去的cid爲當前這個
return cid + 1;
}
return -1;
}
這裏last_cid的初始值爲-1。
函數的大致邏輯是首先檢查是否還有可分配的協程編號,如有則找到最近的一個未分配的協程編號,然後將標識位置1,表示這個協程編號已被分配。
隨後將cidmap結構體裏的nr_free變量減一,將新獲得的cid標記爲最後分配出去的,最後返回cid加一的值
最關鍵的來了:find_next_zero_bit函數,此函數的作用是找到在page[4096]中下一個值爲0的bit所對應的cid編號。
說實話,由於此函數使用了很多位操作,初看時讓人一頭霧水有點摸不着頭腦,即使知道了每一行是在做什麼位運算,也很難理解它這樣做的原因:
/* find next free cid */
static int find_next_zero_bit(void *addr, int cid) //假設addr爲0x28ef8,cid爲-1
{
uint32_t *p;
uint32_t mask;
int mark = cid;//把傳進來的cid先記下,mark等於-1
cid++;//cid自增1變成0
//0x7fff相當於二進制中的0111 1111 1111 1111,這裏貌似是用來取絕對值(O__O)
//進行與操作後cid還是0
cid &= 0x7fff;
while (cid != mark)//cid(0)和mark(-1)相比較,兩者不同時進入循環
{
//0x1f相當於10進制的31(1*16+15),2進制的0001 1111
//將cid保留後5位,然後將1U左移,mask爲1
mask = 1U << (cid & 0x1f);
//將addr強制轉成uint32_t型指針再加上cid右移5位後的值
//傳入的addr爲0x28ef8,則p爲2682632
p = ((uint32_t*)addr) + (cid >> 5);
//將指針p指向的內容取反後和mask進行與操作
//-1&1 得1
if ((~(*p) & mask))
{
break; //跳出循環
}
++cid;
cid &= 0x7fff;
}
return cid; //返回cid其值爲0
}
要理解這個函數,需要假象一個1024行,32列的內存表,如下圖
函數中的(cid & 0x1f) 是取cid後五位的值(0~31),這個值代表這個cid在這個表中的列數
而(cid >> 5 )這一運算則是取cid前27位的值(cid由32位2進製表示),這個值代表這個cid在這個表中的行數。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.