• 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)

  • 相关阅读:
    [Noip模拟题]教主的魔法
    [Usaco2005 Jan]Muddy Fields泥泞的牧场
    机器学习-数据可视化神器matplotlib学习之路(三)
    机器学习-数据可视化神器matplotlib学习之路(二)
    机器学习-数据可视化神器matplotlib学习之路(一)
    机器学习-ID3决策树算法(附matlab/octave代码)
    Hive安装-windows(转载)
    windows下hadoop安装配置(转载)
    C#发送邮件异常:根据验证过程,远程证书无效
    C#多线程--线程池(ThreadPool)
  • 原文地址:https://www.cnblogs.com/RainRill/p/8260466.html
Copyright © 2020-2023  润新知