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在這個表中的行數。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章