目录
概述
主线程 是 Skynet 框架的启动入口和核心控制线程,负责整个框架的初始化、服务创建、线程启动和退出管理。主线程是程序启动时自动创建的,不需要显式创建。
特点:
- 程序启动时自动创建
- 执行初始化和配置加载
- 创建 Logger 服务
- 创建其他系统线程
- 等待所有线程退出后清理资源
文件位置 : skynet/skynet-src/skynet_main.c
主要职责
1. 配置文件解析
功能: 读取并解析 Lua 配置文件
步骤:
- 解析命令行参数,获取配置文件路径
- 创建 Lua 虚拟机
- 加载并执行配置脚本
- 提取配置项到
skynet_config结构 - 销毁 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. 服务创建
创建的服务:
- Logger 服务: 第一个服务,负责日志输出
- 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. 线程启动
启动的线程:
- Monitor 线程 (1个)
- Timer 线程 (1个)
- Socket 线程 (1个)
- 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 | 守护进程模式 |
总结
主线程的核心作用:
- 初始化: 加载配置、初始化子系统
- 服务创建: 创建 Logger 和 Bootstrap 服务
- 线程管理: 创建并管理所有系统线程
- 等待退出: 等待所有线程完成工作
- 资源清理: 清理全局资源
关键特点:
- 单例线程:整个程序只有一个主线程
- 阻塞等待:在
start()中阻塞等待其他线程 - 最后退出:在其他线程退出后才退出
与其他线程的关系:
- 主线程创建所有其他线程
- 主线程等待其他线程退出
- 主线程在退出前清理资源