skynet源码分析之skynet_handle

skynet_handle是所有服务(ctx)的仓库(handle_storage),存储所有ctx。

struct handle_name { //ctx的handle与name对应关系
        char * name;
        uint32_t handle;
};

struct handle_storage { //管理所有ctx结构
        struct rwlock lock; //读写锁

        uint32_t harbor; //每个skynet节点可配置一个唯一harbor,用于与其他节点组网
        uint32_t handle_index; //下次从handle_index位置开始查找空的位置
        int slot_size; //已分配的ctx的容量
        struct skynet_context ** slot; //slot_size容量的数组,每一项指向一个ctx
        
        int name_cap; //已分配ctx名字的容量
        int name_count; //已用的名字
        struct handle_name *name; //name_cap容量的数组,每一项是一个handle_name
};

static struct handle_storage *H = NULL;

skynet启动时初始化handle_storage(S)

void skynet_handle_init(int harbor) { //初始化,harbor在配置文件中可配置
        assert(H==NULL);
        struct handle_storage * s = skynet_malloc(sizeof(*H));
        s->slot_size = DEFAULT_SLOT_SIZE;
    //给slot分配slot_size个ctx内存,
        s->slot = skynet_malloc(s->slot_size * sizeof(struct skynet_context *));
        memset(s->slot, 0, s->slot_size * sizeof(struct skynet_context *));

        rwlock_init(&s->lock); //读写锁
        // reserve 0 for system
    // harbor取高8位,所以最多可组网2^8=255 skynet节点
        s->harbor = (uint32_t) (harbor & 0xff) << HANDLE_REMOTE_SHIFT;
        s->handle_index = 1;
        s->name_cap = 2;
        s->name_count = 0;
    //给name分配name_cap个handle_name内存

        s->name = skynet_malloc(s->name_cap * sizeof(struct handle_name));

        H = s;

        // Don't need to free H
}

为了效率,S->lock采用读写锁,因为获取一个ctx(读)的频率要远远大于创建/杀掉(写)一个ctx的频率,所以工作线程可以并发获取ctx。

S->harbor在skynet启动文件中配置,每个skynet节点可配置唯一的harbor,用于与其他skynet节点组网,harbor占8位,所以最高可组网2^8个。

S->slot存放所有ctx指针,是一个数组,容量为S->slot_size。当new一个ctx时,会给ctx注册一个对应的唯一的handle(skynet_handle_register)。handle从S->handle_index开始循环,通过handle&(S->slot_size-1)映射成一个hash值,如果在slot中未使用,则找到ctx对应的handle,在slot存放ctx指针,并设置ctx->handle=hande。当找不到空位置(数组满)时,重新申请之前2倍容量大小的内存空间,然后把数据迁移到新的空间里。这不就是c++里vector的实现方式嘛,skynet里有很多类似的实现机制,用数组+容量实现类似vector的功能。当kill一个ctx时会回收handle(skynet_handle_retire),置空ctx在slot里位置,供下一个ctx使用。skynet里每个ctx都有一个唯一的handle对应,向某个ctx发送消息时,都是通过handle找到对应的ctx,然后向ctx的消息队列里push消息。注:handle是uint32_t,其中高8位为harbor id,低24位为handle id,所以总共可创建2^24个ctx。

在上层逻辑很难记住每个handle具体代表哪个服务,通常会为handle注册name(不限一个),通过name找到对应的handle,通过S->name实现。S->name是一个数组,类似S->slot,动态分配内存,S->name_cap表示数组容量。S->name是按handle_name->name升序排序的,通过二分查找快速地查找name对应的handle(skynet_handle_findname)。给handle注册name时,也需保证注册完S->name有序(skynet_handle_namehandle)

原文地址:https://www.cnblogs.com/RainRill/p/8260466.html