skynet 主线程 (Main Thread) 详解

目录

  1. 概述
  2. 主要职责
  3. 启动流程
  4. 代码实现
  5. 关键代码分析
  6. 生命周期

概述

主线程 是 Skynet 框架的启动入口和核心控制线程,负责整个框架的初始化、服务创建、线程启动和退出管理。主线程是程序启动时自动创建的,不需要显式创建。

特点:

  • 程序启动时自动创建
  • 执行初始化和配置加载
  • 创建 Logger 服务
  • 创建其他系统线程
  • 等待所有线程退出后清理资源

文件位置 : skynet/skynet-src/skynet_main.c


主要职责

1. 配置文件解析

功能: 读取并解析 Lua 配置文件

步骤:

  1. 解析命令行参数,获取配置文件路径
  2. 创建 Lua 虚拟机
  3. 加载并执行配置脚本
  4. 提取配置项到 skynet_config 结构
  5. 销毁 Lua 虚拟机

配置示例:

lua 复制代码
-- skynet.conf
thread = 8                  -- Worker 线程数量
logger = "./skynet.log"     -- 日志文件
logservice = "logger"       -- Logger 服务模块名
bootstrap = "snlua bootstrap"  -- Bootstrap 服务配置
start = "main"              -- 启动的用户服务
harbor = 0                  -- Harbor ID

2. 全局环境初始化

功能: 初始化 Skynet 框架的全局状态

初始化的子系统:

  • 环境变量管理 (skynet_env_init)
  • 全局消息队列 (skynet_mq_init)
  • 线程局部存储 (skynet_globalinit)
  • 服务句柄管理 (skynet_handle_init)
  • 模块管理器 (skynet_module_init)
  • 定时器系统 (skynet_timer_init)
  • 监控系统 (skynet_monitor_init)
  • 网络系统 (skynet_socket_init)
  • 跨节点通信 (skynet_harbor_init)

3. 服务创建

创建的服务:

  1. Logger 服务: 第一个服务,负责日志输出
  2. Bootstrap 服务: 启动引导服务

创建流程:

c 复制代码
// 创建 Logger 服务
struct skynet_context *ctx = skynet_context_new(config->logservice, config->logger);
skynet_handle_namehandle(skynet_context_handle(ctx), "logger");

// 启动 Bootstrap 服务
bootstrap(ctx, config->bootstrap);

4. 线程启动

启动的线程:

  1. Monitor 线程 (1个)
  2. Timer 线程 (1个)
  3. Socket 线程 (1个)
  4. Worker 线程 (N个,默认8个)

启动函数 : start(config->thread)

5. 等待退出

退出条件:

  • 所有服务都已退出 (skynet_context_total() == 0)
  • 或收到退出信号

清理工作:

  • 通知所有线程退出
  • 等待所有线程结束
  • 清理跨节点通信资源
  • 清理网络资源

启动流程

复制代码
┌─────────────────────────────────────────────────────────────┐
│                    主线程启动流程                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. main() 入口                                             │
│     │                                                       │
│     ├─ 解析命令行参数                                        │
│     ├─ 获取配置文件路径                                      │
│     │                                                       │
│     ▼                                                       │
│  2. 初始化环境变量                                           │
│     │                                                       │
│     ├─ skynet_globalinit()                                  │
│     ├─ skynet_env_init()                                    │
│     └─ skynet_thread_init()                                 │
│     │                                                       │
│     ▼                                                       │
│  3. 读取配置文件                                             │
│     │                                                       │
│     ├─ 创建 Lua 虚拟机                                       │
│     ├─ 执行配置脚本                                          │
│     ├─ 提取配置项                                            │
│     └─ 销毁 Lua 虚拟机                                       │
│     │                                                       │
│     ▼                                                       │
│  4. 忽略 SIGPIPE 信号                                       │
│     │                                                       │
│     └─ signal(SIGPIPE, SIG_IGN);                            │
│     │                                                       │
│     ▼                                                       │
│  5. 调用 skynet_start()                                     │
│     │                                                       │
│     ├─ 初始化各子系统                                        │
│     │   ├─ skynet_mq_init()                                 │
│     │   ├─ skynet_handle_init()                             │
│     │   ├─ skynet_module_init()                             │
│     │   ├─ skynet_timer_init()                              │
│     │   ├─ skynet_socket_init()                             │
│     │   └─ skynet_harbor_init()                             │
│     │                                                       │
│     ├─ 创建 Logger 服务                                      │
│     │   └─ skynet_context_new("logger", "./skynet.log")     │
│     │                                                       │
│     ├─ 启动 Bootstrap 服务                                   │
│     │   └─ bootstrap(ctx, config->bootstrap)                │
│     │                                                       │
│     ├─ 创建并启动所有线程                                     │
│     │   └─ start(config->thread)                            │
│     │                                                       │
│     │   主线程进入等待状态                                   │
│     │   │                                                   │
│     │   ▼                                                   │
│     │  其他线程运行:                                         │
│     │  ├─ Monitor 线程: 监控死循环                           │
│     │  ├─ Timer 线程: 定时器管理                             │
│     │  ├─ Socket 线程: 网络处理                              │
│     │  └─ Worker 线程: 消息处理                              │
│     │                                                       │
│     ▼                                                       │
│  6. 等待所有线程退出                                         │
│     │                                                       │
│     ├─ 等待 start() 函数返回                                 │
│     ├─ 所有线程都已退出                                      │
│     │                                                       │
│     ▼                                                       │
│  7. 清理资源                                                 │
│     │                                                       │
│     ├─ skynet_harbor_exit()                                 │
│     ├─ skynet_socket_free()                                 │
│     ├─ skynet_globalexit()                                  │
│     └─ 如果是守护进程,清理 PID 文件                          │
│     │                                                       │
│     ▼                                                       │
│  8. 程序退出                                                 │
│                                                             │
└─────────────────────────────────────────────────────────────┘

代码实现

主函数入口

文件 : skynet/skynet-src/skynet_main.c

位置 : skynet_main.c:244-378

c 复制代码
int main(int argc, char *argv[]) {
    // 1. 保存配置文件路径
    const char * config_file = NULL;
    
    if (argc > 1) {
        config_file = argv[1];
    } else {
        fprintf(stderr, "Need a config file. Please read skynet wiki : https://github.com/cloudwu/skynet/wiki/Config\n");
        return 1;
    }
    
    // 2. 初始化全局环境
    skynet_globalinit();
    skynet_env_init();
    
    // 3. 忽略 SIGPIPE 信号
    signal(SIGPIPE, SIG_IGN);
    
    // 4. 加载配置文件
    load_config(config_file);
    
    // 5. 启动 Skynet
    skynet_start();
    
    // 6. 清理全局环境
    skynet_globalexit();
    
    return 0;
}

加载配置文件

位置 : skynet_main.c:138-241

c 复制代码
static void
load_config(const char * filename) {
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);
    
    // 加载并执行配置文件
    if (luaL_dofile(L, filename)) {
        fprintf(stderr, "%s\n", lua_tostring(L, -1));
        exit(1);
    }
    
    // 创建 skynet_config 结构
    struct skynet_config config;
    config.thread = optint("thread", 8);
    config.logger = optstring("logger", NULL);
    config.logservice = optstring("logservice", "logger");
    config.bootstrap = optstring("bootstrap", "snlua bootstrap");
    config.harbor = optint("harbor", 0);
    config.master = optstring("master", NULL);
    config.start = optstring("start", "main");
    config.daemon = optstring("daemon", NULL);
    
    lua_close(L);
    
    // 调用 skynet_start(),传递配置
    skynet_start(&config);
}

关键代码分析

1. 环境变量管理

文件 : skynet/skynet-src/skynet_env.c

功能: 存储全局配置信息

数据结构:

c 复制代码
struct env {
    struct spinlock lock;
    int lock_entity;          // 是否被锁住
    char * value;             // 存储的值
    struct env * next;        // 下一个环境变量
};

static struct env *E = NULL;  // 全局环境变量链表

操作函数:

  • skynet_setenv(key, value): 设置环境变量
  • skynet_getenv(key): 获取环境变量

2. 线程局部存储

文件 : skynet/skynet-src/skynet_server.c

功能: 每个线程存储自己的类型信息

实现:

c 复制代码
void skynet_initthread(int m) {
    uintptr_t v = (uint32_t)(-m);
    pthread_setspecific(G_NODE.handle_key, (void *)v);
}

// 获取当前线程类型
int skynet_thread_type() {
    void *v = pthread_getspecific(G_NODE.handle_key);
    return (int)(-(uintptr_t)v);
}

// 线程类型枚举
enum {
    THREAD_MAIN = 1,
    THREAD_WORKER = 2,
    THREAD_SOCKET = 3,
    THREAD_TIMER = 4,
    THREAD_MONITOR = 5,
};

3. 服务创建

文件 : skynet/skynet-src/skynet_start.c

位置 : skynet_start.c:544-557

c 复制代码
// 创建 Logger 服务
struct skynet_context *ctx = skynet_context_new(config->logservice, config->logger);
if (ctx == NULL) {
    fprintf(stderr, "Can't launch %s service\n", config->logservice);
    exit(1);
}

// 为 logger 服务命名
skynet_handle_namehandle(skynet_context_handle(ctx), "logger");

// 启动 bootstrap 服务
bootstrap(ctx, config->bootstrap);

4. 线程启动

文件 : skynet/skynet-src/skynet_start.c

位置 : skynet_start.c:392-449

c 复制代码
static void
start(int thread) {
    pthread_t pid[thread+3];  // 线程 ID 数组
    
    // 初始化监控器
    struct monitor *m = skynet_malloc(sizeof(*m));
    m->count = thread;
    m->sleep = 0;
    m->m = skynet_malloc(thread * sizeof(struct skynet_monitor *));
    
    for (i=0;i<thread;i++) {
        m->m[i] = skynet_monitor_new();
    }
    
    // 初始化互斥锁和条件变量
    pthread_mutex_init(&m->mutex, NULL);
    pthread_cond_init(&m->cond, NULL);
    
    // 创建特殊线程
    create_thread(&pid[0], thread_monitor, m);  // Monitor 线程
    create_thread(&pid[1], thread_timer, m);    // Timer 线程
    create_thread(&pid[2], thread_socket, m);   // Socket 线程
    
    // 创建 worker 线程
    for (i=0;i<thread;i++) {
        wp[i].m = m;
        wp[i].id = i;
        wp[i].weight = weight[i];
        create_thread(&pid[i+3], thread_worker, &wp[i]);
    }
    
    // 等待所有线程退出
    for (i=0;i<thread+3;i++) {
        pthread_join(pid[i], NULL);
    }
    
    free_monitor(m);
}

生命周期

复制代码
┌─────────────────────────────────────────────────────────────┐
│                    主线程生命周期                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  程序启动                                                    │
│     │                                                       │
│     ▼                                                       │
│  main() 入口                                                │
│     │  ← 程序开始执行                                        │
│     │                                                       │
│     ├─ 解析命令行参数                                        │
│     ├─ 初始化全局环境                                        │
│     ├─ 加载配置文件                                          │
│     │                                                       │
│     ▼                                                       │
│  skynet_start()                                             │
│     │  ← 启动 Skynet 框架                                    │
│     │                                                       │
│     ├─ 初始化子系统                                          │
│     ├─ 创建 Logger 服务                                      │
│     ├─ 启动 Bootstrap 服务                                   │
│     ├─ 创建并启动所有线程                                     │
│     │                                                       │
│     │  ┌─────────────────────────────────┐                  │
│     │  │  等待其他线程运行                 │                  │
│     │  │  主线程阻塞在 pthread_join()     │                  │
│     │  │  ┌─────────────────────────┐    │                  │
│     │  │  │  Monitor 线程运行        │    │                  │
│     │  │  │  Timer 线程运行          │    │                  │
│     │  │  │  Socket 线程运行         │    │                  │
│     │  │  │  Worker 线程运行         │    │                  │
│     │  │  └─────────────────────────┘    │                  │
│     │  │                                 │                  │
│     │  └─────────────────────────────────┘                  │
│     │                                                       │
│     │  所有线程退出后,返回主线程                              │
│     │                                                       │
│     ├─ 清理资源                                              │
│     │   ├─ skynet_harbor_exit()                             │
│     │   └─ skynet_socket_free()                             │
│     │                                                       │
│     ▼                                                       │
│  skynet_globalexit()                                        │
│     │  ← 清理全局环境                                        │
│     │                                                       │
│     ▼                                                       │
│  return 0                                                   │
│     │                                                       │
│     ▼                                                       │
│  程序退出                                                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

配置项

配置项 类型 默认值 说明
thread int 8 Worker 线程数量
logger string NULL 日志文件路径
logservice string "logger" Logger 服务模块名
bootstrap string "snlua bootstrap" Bootstrap 服务配置
harbor int 0 Harbor ID
master string NULL 主节点地址
start string "main" 启动的用户服务
daemon string NULL 守护进程模式

总结

主线程的核心作用:

  1. 初始化: 加载配置、初始化子系统
  2. 服务创建: 创建 Logger 和 Bootstrap 服务
  3. 线程管理: 创建并管理所有系统线程
  4. 等待退出: 等待所有线程完成工作
  5. 资源清理: 清理全局资源

关键特点:

  • 单例线程:整个程序只有一个主线程
  • 阻塞等待:在 start() 中阻塞等待其他线程
  • 最后退出:在其他线程退出后才退出

与其他线程的关系:

  • 主线程创建所有其他线程
  • 主线程等待其他线程退出
  • 主线程在退出前清理资源
相关推荐
lpl3129055095 小时前
skynet Socket 线程详解
lua
林鸿群1 天前
Cocos2d-x Lua 游戏前端工程架构深度解析
游戏·mvc·lua·游戏开发·cocos2d·游戏架构
林鸿群1 天前
Lua 5.4 语法与核心知识学习总结
lua
007张三丰2 天前
软件测试专栏(7/20):接口测试全攻略:Postman+Newman实现API自动化
自动化·lua·接口测试·postman·api测试·newman
于眠牧北2 天前
重写RedisTemplate后在lua脚本中传递参数不需要二次转换
java·junit·lua
csdn_aspnet2 天前
技术难题:高并发场景下的“超卖”现象(库存一致性)
redis·lua·秒杀
shuair2 天前
redis执行lua脚本
数据库·redis·lua
小白-Tester3 天前
2026最新Postman安装教程[简单易懂]附安装包
开发语言·lua
qq_246839755 天前
Redis lua 执行性能优化
redis·性能优化·lua