• skynet源码分析之service_logger,skynet_error


    service_srv目录是依附skynet核心模块的c服务模板,如用于日志输出的logger服务,用于运行lua脚本的snlua服务等,编译成so库供skynet框架使用。logger服务(service_logger.c)功能简单,通过了解其工作方式来熟悉skynet的工作流程。在skynet启动时,会启动一个"logger"服务,默认是logger类型服务,当然也可以配置成snlua类型。

    //skynet_start.c
    struct skynet_context *ctx = skynet_context_new(config->logservice, config->logger) 
    
    //skynet_main.c
    config.logger = optstring("logger", NULL);
    config.logservice = optstring("logservice", "logger");

    启动一个新logger服务,会调用到logger_create这个api,申请一个struct logger结构,最后赋值给ctx->instance。当服务退出后,会调用到logger_release进行清理回收。

    struct logger {
            FILE * handle; //文件句柄
            char * filename;
            int close; //0代表文件句柄是标准输出,1代表输出到文件
    };
    
    struct logger *
    logger_create(void) {
            struct logger * inst = skynet_malloc(sizeof(*inst));
            inst->handle = NULL;
            inst->close = 0;
            inst->filename = NULL;
    
            return inst;
    }

    之后会调用logger_init这个api,参数param是配置里config->logger,若指定文件路径,则打开该文件,inst->handle = fopen(parm,"w");否则,设置文件句柄为标准输出inst->handle = stdout。最后,设置ctx的消息回调函数为logger_cb(skynet_callback),并给ctx注册一个名称skynet_command(ctx, "REG", ".logger")。稍后再看logger_cb这个消息回调函数。

    int logger_init(struct logger * inst, struct skynet_context *ctx, const char * parm) {
            if (parm) {
                    inst->handle = fopen(parm,"w");
                    if (inst->handle == NULL) {
                            return 1;
                    }
                    inst->filename = skynet_malloc(strlen(parm)+1);
                    strcpy(inst->filename, parm);
                    inst->close = 1;
            } else {
                    inst->handle = stdout;
            }
            if (inst->handle) {
                    skynet_callback(ctx, inst, logger_cb);
                    skynet_command(ctx, "REG", ".logger");
                    return 0;
            }
            return 1;
    }

     skynet输出日志通常是调用skynet_error这个api(lua层用skynet.error最后也是调用skynet_error)。查找名称为“logger”对应的ctx的handle id,然后向该id发送消息包skynet_context_push,消息包的类型为PTYPE_TEXT,没有设置PTYPE_ALLOCSESSION标记表示不需要接收方返回。

    //skynet_error.c
    void
    skynet_error(struct skynet_context * context, const char *msg, ...) { static uint32_t logger = 0; if (logger == 0) { logger = skynet_handle_findname("logger"); } ... struct skynet_message smsg; if (context == NULL) { smsg.source = 0; } else { smsg.source = skynet_context_handle(context); } smsg.session = 0; smsg.data = data; smsg.sz = len | ((size_t)PTYPE_TEXT << MESSAGE_TYPE_SHIFT); skynet_context_push(logger, &smsg); }

     当工作线程分发这条消息包时,最终会调用logger服务的消息回调函数logger_cb。不同服务类型的消息回调函数接口参数是一样的。

    static int logger_cb(struct skynet_context * context, void *ud, int type, int session, uint32_t source, const void * msg, size_t sz) {
            struct logger * inst = ud;
            switch (type) {
            case PTYPE_SYSTEM:
                    if (inst->filename) {
                            inst->handle = freopen(inst->filename, "a", inst->handle);
                    }
                    break;
            case PTYPE_TEXT:
                    fprintf(inst->handle, "[:%08x] ",source);
                    fwrite(msg, sz , 1, inst->handle);
                    fprintf(inst->handle, "
    ");
                    fflush(inst->handle);
                    break;
            }
    
            return 0;
    }

    由于skynet_error发送的消息包的type是PTYPE_TEXT,会把消息包源地址以及消息包数据一起写到文件句柄里。在skynet_context_new成功启动一个服务后,会调用skynet_error(ctx, "LAUNCH %s %s", name, param ? param : ""),于是就有了经典的skynet启动画面

  • 相关阅读:
    从Linux内核中获取真随机数【转】
    linux下pthread_cancel无法取消线程的原因【转】
    LINUX-内核-中断分析-中断向量表(3)-arm【转】
    ARM中断向量表与响应流程【转】
    小任务与工作队列的区别【转】
    GNU Readline 库及编程简介【转】
    Linux 内核同步之自旋锁与信号量的异同【转】
    Professional Linux Kernel Architecture 笔记 —— 中断处理(Part 2)【转】
    Linux中断(interrupt)子系统之二:arch相关的硬件封装层【转】
    浅谈C语言中的强符号、弱符号、强引用和弱引用【转】
  • 原文地址:https://www.cnblogs.com/RainRill/p/8319930.html
Copyright © 2020-2023  润新知