美文网首页
skynet源代码阅读(3)

skynet源代码阅读(3)

作者: 嘴里叼着棒棒糖 | 来源:发表于2017-11-20 15:23 被阅读0次

    snlua实例初始化
    看看snlua的snlua_init函数

    int
    snlua_init(struct snlua *l, struct skynet_context *ctx, const char * args) {
        int sz = strlen(args);
        char * tmp = skynet_malloc(sz);
        memcpy(tmp, args, sz);
        skynet_callback(ctx, l , launch_cb);
        const char * self = skynet_command(ctx, "REG", NULL);
        uint32_t handle_id = strtoul(self+1, NULL, 16);
    
        printf("snlua_init %d\n", handle_id);
        // it must be first message
        //PTYPE_TAG_DONTCOPY
        skynet_send(ctx, 0, handle_id, PTYPE_TAG_DONTCOPY,0, tmp, sz);
        return 0;
    }
    

    函数skynet_callback
    Skynet 的核心功能就是发送消息和处理消息。它体现在 skynet_send 和 skynet_callback 两个 api 上

    void 
    skynet_callback(struct skynet_context * context, void *ud, skynet_cb cb) {
        context->cb = cb;
        context->cb_ud = ud;
    }
    

    函数skynet_command
    它接收一个字符串参数,返回一个字符串结果。你可以看成是一种文本协议。但 skynet_command 保证在调用过程中,不会切出当前的服务线程,导致状态改变的不可预知性。其每个功能的实现,其实也是内嵌在 skynet 的源代码中,相同上层服务,还是比较高效的。

    void 
    skynet_callback(struct skynet_context * context, void *ud, skynet_cb cb) {
        context->cb = cb;
        context->cb_ud = ud;
    }
    

    strtoul() 函数源自于“string to unsigned long”,用来将字符串转换成无符号长整型数(unsigned long),其原型为:
    unsigned long strtoul (const char* str, char** endptr, int base);
    str 为要转换的字符串,endstr 为第一个不能转换的字符的指针,base 为字符串 str 所采用的进制。

    skynet_send(ctx, 0, handle_id, PTYPE_TAG_DONTCOPY,0, tmp, sz);
    

    就是在snlua实例初始化的时候发送一个不需要复制的msg/sz的数据包给自己

    int skynet_send(
      struct skynet_context * context, 
      uint32_t source, 
      uint32_t destination,
      int type,
      int session,
      void * msg, 
      size_t sz
    );
    
    typedef int (*skynet_cb)(
      struct skynet_context * context,
      void *ud, 
      int type, 
      int session, 
      uint32_t source ,
      const void * msg,
      size_t sz
    );
    

    source 和 destination 都是 32 位整数,表示地址。原则上不需要填写 source 地址,因为默认就是它自己。0 是系统保留的 handle ,可以指代自己。这里允许填写 source 值,是因为在某些特殊场合,需要伪造一个由别人发出的包。姑且可以理解 source 为 reply address 。

    发送一个数据包,就是发送 msg/sz 对。我们可以在 type 里打上 dontcopy 的 tag (PTYPE_TAG_DONTCOPY) ,让框架不要复制 msg/sz 指代的数据包。否则 skynet 会用 malloc 分配一块内存,把数据复制进去。callback 函数在处理完这块数据后,会调用 free 释放内存。你可以通过让 callback 返回 1 ,阻止框架释放内存。这通常和在 send 时标记 dontcopy 标记配对使用.

    在skynet_send函数中将消息加入destination对应的消息队列,skynet_harbor_message_isremote是重点

        if (skynet_harbor_message_isremote(destination)) {
            struct remote_message * rmsg = skynet_malloc(sizeof(*rmsg));
            rmsg->destination.handle = destination;
            rmsg->message = data;
            rmsg->sz = sz;
            skynet_harbor_send(rmsg, source, session);
        } else {
            struct skynet_message smsg;
            smsg.source = source;
            smsg.session = session;
            smsg.data = data;
            smsg.sz = sz;
    
            if (skynet_context_push(destination, &smsg)) {
                skynet_free(data);
                return -1;
            }
        }
    

    在工作线程中thread_worker -> skynet_context_message_dispatch->dispatch_message->ctx的cb函数

    static void
    dispatch_message(struct skynet_context *ctx, struct skynet_message *msg) {
        assert(ctx->init);
        CHECKCALLING_BEGIN(ctx)
        pthread_setspecific(G_NODE.handle_key, (void *)(uintptr_t)(ctx->handle));
        int type = msg->sz >> MESSAGE_TYPE_SHIFT;
        size_t sz = msg->sz & MESSAGE_TYPE_MASK;
        if (ctx->logfile) {
            skynet_log_output(ctx->logfile, msg->source, type, msg->session, msg->data, sz);
        }
        ++ctx->message_count;
        int reserve_msg;
        if (ctx->profile) {
            ctx->cpu_start = skynet_thread_time();
            reserve_msg = ctx->cb(ctx, ctx->cb_ud, type, msg->session, msg->source, msg->data, sz);
            uint64_t cost_time = skynet_thread_time() - ctx->cpu_start;
            ctx->cpu_cost += cost_time;
        } else {
            reserve_msg = ctx->cb(ctx, ctx->cb_ud, type, msg->session, msg->source, msg->data, sz);
        }
        if (!reserve_msg) {
            skynet_free(msg->data);
        }
        CHECKCALLING_END(ctx)
    }
    

    type 表示的是当前消息包的协议组别,而不是传统意义上的消息类别编号。协议组别类型并不会很多,所以,我限制了 type 的范围是 0 到 255 ,由一个字节标识。在实现时,skynet把 type 编码到了 size 参数的高 8 位。因为单个消息包限制长度在 16 M (24 bit)内,是个合理的限制。这样,为每个消息增加了 type 字段,并没有额外增加内存上的开销。

    相关文章

      网友评论

          本文标题:skynet源代码阅读(3)

          本文链接:https://www.haomeiwen.com/subject/cdyjvxtx.html