swoole源码学习——协程编号的管理和分配

来源:互联网 发布:手机软件广告拦截软件 编辑:程序博客网 时间:2024/06/07 11:51

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在这个表中的行数。


阅读全文
0 0
原创粉丝点击