skynet 源码阅读 -- 启动主流程
Skynet 启动主流程分析
Skynet 是一个轻量级、高并发的服务器框架。它在启动时会进行一系列初始化操作,并启动多个不同功能的线程(Monitor、Timer、Worker、Socket),从而实现消息分发、定时器、网络I/O等核心功能。本文主要从 main() 函数开始一步步trace,循序渐进地看 Skynet 的启动过程以及各条线程的分工,为后续深入阅读 Skynet 源码做铺垫。
1. 启动入口 main 函数 -- skynet_main.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. usage: skynet configfilename\n");return 1;}// 2) Skynet全局初始化skynet_globalinit();skynet_env_init();sigign(); // 忽略部分信号struct skynet_config config;// 3) 解析配置文件struct lua_State *L = luaL_newstate();luaL_openlibs(L);// load_config 脚本读取 config_file 并将其返回到 Lua// ..._init_env(L); // 将Lua中的配置项保存到C层lua_close(L);// 4) 将 Lua 解析到的配置写入 config 结构体config.thread = optint("thread",8);config.module_path = optstring("cpath","./cservice/?.so");config.harbor = optint("harbor", 1);config.bootstrap = optstring("bootstrap","snlua bootstrap");config.daemon = optstring("daemon", NULL);config.logger = optstring("logger", NULL);config.logservice = optstring("logservice", "logger");config.profile = optboolean("profile", 1);// 5) 启动Skynetskynet_start(&config);// 6) 全局退出清理skynet_globalexit();return 0;
}
流程要点:
- 读取命令行参数:确定配置文件
config_file。 - Skynet全局初始化:
skynet_globalinit/skynet_env_init设置一些全局环境,注册信号处理等。 - 解析配置:通过 Lua 脚本来读取
config_file并存到struct skynet_config。 - 调用
skynet_start:是 Skynet 的核心启动函数,后面会详细讲。 - 清理:退出时
skynet_globalexit做一些释放资源操作(比如内存管理模块等)。
从这里可以看到:Lua 脚本用于配置 Skynet,通过 Lua 作为配置及引擎脚本。
2. skynet_start:初始化模块 & 启动多线程
void
skynet_start(struct skynet_config * config) {// 1) 注册SIGHUP用来重开日志文件struct sigaction sa;sa.sa_handler = &handle_hup;sa.sa_flags = SA_RESTART;sigfillset(&sa.sa_mask);sigaction(SIGHUP, &sa, NULL);// 2) 若配置了daemon模式 -> 后台运行if (config->daemon) {if (daemon_init(config->daemon)) {exit(1);}}// 3) 各模块初始化skynet_harbor_init(config->harbor);skynet_handle_init(config->harbor);skynet_mq_init();skynet_module_init(config->module_path);skynet_timer_init();skynet_socket_init();skynet_profile_enable(config->profile);// 4) 启动 logservice 服务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);}skynet_handle_namehandle(skynet_context_handle(ctx), "logger");// 5) 启动 bootstrap 服务 bootstrap(ctx, config->bootstrap);// 6) 启动多线程start(config->thread);// 7) 退出处理skynet_harbor_exit();skynet_socket_free();if (config->daemon) {daemon_exit(config->daemon);}
}
2.1 模块初始化
skynet_harbor_init:与分布式/harbor机制有关(分布式集群的一部分)。skynet_handle_init:管理 “Handle -> Service” 映射。skynet_mq_init:初始化全局消息队列结构(global_queue)。skynet_module_init:C服务的模块管理,如加载cpath下的.so。skynet_timer_init:定时器初始化,创建TI = timer_create_timer()并记录当前系统时间。skynet_socket_init:socket层初始化,创建socket_server。skynet_profile_enable:若config->profile为真,打开性能分析。
2.2 启动初始服务
- logservice:日志服务,用于记录日志(
logger服务)。 - bootstrap: 会启动launcher skynet.launch("snlua","launcher") 服务,后续用作lua 服务的启动器。
2.3 启动线程
start(config->thread);
- Skynet 同时需要多个线程来协同工作:Monitor 线程、Timer 线程、Socket 线程、以及 N 个 Worker 线程。
- 这样就形成了一个 Skynet 进程 有多条并行线程,分别干不同的事(定时器、网络I/O、处理消息等)。
- 具体见
static void start(int thread)。
2.4 退出
- 当线程全部 join() 完后,会执行
skynet_harbor_exit、skynet_socket_free等操作收尾。
3. start函数:创建并管理多条线程
static void
start(int thread) {pthread_t pid[thread+3];// 1) 创建 monitor 结构struct monitor *m = skynet_malloc(sizeof(*m));memset(m, 0, sizeof(*m));m->count = thread;m->sleep = 0;m->m = skynet_malloc(thread * sizeof(struct skynet_monitor *));// 初始化互斥量, 条件变量// 2) 创建3条特殊线程create_thread(&pid[0], thread_monitor, m);create_thread(&pid[1], thread_timer, m);create_thread(&pid[2], thread_socket, m);// 3) 创建 worker 线程struct worker_parm wp[thread];for (i=0;i<thread;i++) {wp[i].m = m;wp[i].id = i;...create_thread(&pid[i+3], thread_worker, &wp[i]);}// 4) join 所有线程for (i=0;i<thread+3;i++) {pthread_join(pid[i], NULL);}free_monitor(m);
}
由此可见:
- Monitor 线程:
thread_monitor - Timer 线程:
thread_timer - Socket 线程:
thread_socket - Worker 线程:
thread_worker(数量 =thread)
总线程数 = thread + 3。
3.1 Monitor 线程
static void *
thread_monitor(void *p) {struct monitor * m = p;...for (;;) {// 定期检查 worker 是否卡死for (i=0;i<n;i++) {skynet_monitor_check(m->m[i]);}sleep(1);}return NULL;
}
- 用于监控 Worker 线程是否卡死,通过
skynet_monitor_check观测 worker 执行时间。 - 如果检测到超长执行,会打印报警或采取措施(避免Worker永久阻塞)。
3.2 Timer 线程
static void *
thread_timer(void *p) {struct monitor * m = p;for (;;) {skynet_updatetime(); // 更新定时器skynet_socket_updatetime();// socket层时间更新wakeup(m,m->count-1); // 唤醒所有线程( 让 worker 从 cond.wait 中唤醒 )usleep(2500); // 2.5 ms 间隔...}...return NULL;
}
- skynet_updatetime:增加 “逻辑时间” 并执行到期任务
- skynet_socket_updatetime:驱动 socket server 中一些超时逻辑
- wakeup:唤醒 Worker,防止他们陷入空闲
usleep(2500)=> 每 2.5ms tick 一次
3.3 Socket 线程
static void *
thread_socket(void *p) {for (;;) {int r = skynet_socket_poll();if (r==0)break;if (r<0) {continue;}wakeup(m,0);}return NULL;
}
- 这里循环调用
skynet_socket_poll(),监听网络事件(读写就绪、连接、断开等)。 r==0表示 socket server 退出 => break- 每次有网络事件 =>
wakeup=> 唤醒 Worker 线程去处理消息
3.4 Worker 线程
static void *
thread_worker(void *p) {struct worker_parm *wp = p;while (!m->quit) {// 1) 分发一条消息q = skynet_context_message_dispatch(sm, q, weight);if (q == NULL) {// 如果没有消息可处理 => sleeppthread_cond_wait(&m->cond, &m->mutex);}}return NULL;
}
- Worker 线程主要做消息分发处理(从全局队列/本地队列取出消息 => 交给相应的服务context去执行。)
- 如果没消息 =>
pthread_cond_wait=> 休眠 => 等 Timer 或 Socket 线程唤醒。 weight让某些线程能多处理几条消息(可实现不均衡分配, 保证核心Worker多干活)。
4. 结构化启动流程图
下图(示意)可帮助理解:
(1) main()|vskynet_globalinit() + skynet_env_init()|+--> parse config via Lua|vskynet_start(&config)|--- register SIGHUP|--- if daemon => daemon_init|--- skynet_*_init:| - harbor_init| - handle_init| - mq_init| - module_init| - timer_init| - socket_init||--- create logservice => "logger"|--- bootstrap => e.g. "snlua bootstrap"||--- start(#thread)|+-> create monitor thread+-> create timer thread+-> create socket thread+-> create N worker threads|+-> all threads join => end|vskynet_globalexit()|vreturn
- main:解析 config, 调用
skynet_start。 - skynet_start:初始化模块/服务 => 启动多线程 => run => join => exit
- 各个线程并行运作:
- Monitor => 检测卡死
- Timer => 定时器滴答 & 唤醒 worker
- Socket => 处理网络事件
- Worker => 真正执行 Lua 服务消息
5. 小结
- 主进程先解析配置,初始化一些全局,随后调用
skynet_start。 - skynet_start 依次初始化 harbor、handle、mq、module、timer、socket…
- 启动logservice(记录日志)和bootstrap(初始Lua服务),最后启动多线程(monitor/timer/socket/worker)。
- 整个 Skynet 运行后,Timer 线程负责定时器 & 唤醒 Worker,Socket 线程负责网络事件,Worker 处理消息循环,Monitor检查 Worker 是否卡死。
通过这样一个启动流程,Skynet 建立起一个消息驱动的并发系统框架:
- Worker 负责业务逻辑
- Socket 处理IO
- Timer 提供定时&超时功能
- Monitor 监控
后续若要深入,可阅读每个线程对应函数(如 thread_timer, thread_worker)里更细节的流程,比如如何 dispatch 消息,如何在定时器中移位管理远期任务等。本篇作为阅读 Skynet 源码的起点,建立对整个启动过程和多线程分工有一个全局认识。
相关文章:
skynet 源码阅读 -- 启动主流程
Skynet 启动主流程分析 Skynet 是一个轻量级、高并发的服务器框架。它在启动时会进行一系列初始化操作,并启动多个不同功能的线程(Monitor、Timer、Worker、Socket),从而实现消息分发、定时器、网络I/O等核心功能。本文主要从 ma…...
OpenCV:高通滤波之索贝尔、沙尔和拉普拉斯
目录 简述 什么是高通滤波? 高通滤波的概念 应用场景 索贝尔算子 算子公式 实现代码 特点 沙尔算子 算子公式 实现代码 特点 拉普拉斯算子 算子公式 实现代码 特点 高通滤波器的对比与应用场景 相关阅读 OpenCV:图像滤波、卷积与卷积核…...
UDP 广播组播点播的区别及联系
1、网络IP地址的分类 组播地址是分类编址的IPv4地址中的D类地址,又叫多播地址,他的前四位必须是1110,所以网络地址的二进制取值范围是11100000~11101111对应的十进制为 224~~239。所以以224~239开头的网络地址都是组播地址。 组播地址的功能…...
STM32补充——IAP
0 前置知识: FLASH相关内容:前往STM32补充——FLASH STM32三种烧录方式(看看就行): 1.ISP:In System Programming(在系统编程) 执行芯片厂商的 Bootloader 程序进入 ISP 模式&…...
Jetson Xavier NX (ARM) 使用 PyTorch 安装 Open3D-ML 指南
由于 Jetson 为 ARM64 (aarch64) 的系统架构,所以不能用 pip install 直接安装,需要通过源码编译。 升级系统 JetPack 由于 Open3D-ML 目前只支持 CUDA 10.0 以及 CUDA 11.*,并且 JetPack 的 CUDA 开发环境只有10.2、11.4以及12.2࿰…...
【C++高并发服务器WebServer】-1:Linux中父子进程fork创建及关系、GDB多进程调试
本文目录 一、进程创建二、GDB多进程调试 一、进程创建 在Linux中输入man 2 fork可以查看man文档中的fork的相关函数信息。 fork的作用就是创建一个子进程。 通过fork我们可以知道,创建子进程的时候,复制父进程的信息。 我们看看翻译的man文档信息&am…...
C语言数组详解:从基础到进阶的全面解析
在C语言中,数组是一种基本的数据结构,用于存储多个相同类型的数据。数组的引入使得C语言能够高效地存储和操作大量数据。在任何一个C语言程序中,数组都发挥着极其重要的作用。无论是在算法实现、数据存储、还是在复杂程序的设计中,…...
docker的前世今生
docker来自哪里? 从我们运维部署的历史来看,宿主机从最初的物理机到虚拟机,再到docker,一步步演进到现在。技术演进其实是为了解决当前技术的痛点,那我们来看看有哪些痛点以及如何克服痛点的。 物理机 一般来说&…...
python实现施瓦茨-克里斯托费尔【全网首个】根据用户输入推测函数
上代码: from sympy import symbols, integrate, simplify from sympy.plotting import plotn int(input("n:")) if n < 2:print("Error: Must n > 2") i 0 a [] aef [] A [] x, y symbols(x y) z, w symbols(z w)while i < n…...
c语言中的数组(上)
数组的概念 数组是⼀组相同类型元素的集合; 数组中存放的是1个或者多个数据,但是数组元素个数不能为0。 数组中存放的多个数据,类型是相同的。 数组分为⼀维数组和多维数组,多维数组⼀般⽐较多⻅的是⼆维数组。 数组创建 在C语言…...
Unity3D仿星露谷物语开发25之创建时钟界面
1、目标 在时钟界面显示当前时钟信息,同时设置特殊按钮可以快速推进时间用于测试。 2、创建GameClock.cs脚本 在Assets -> Scripts -> TimeSystem目录下创建GameClock.cs脚本。 代码如下: using System.Collections; using System.Collections…...
数据结构测试题1
一、选择题: 1.若长度为n的钱性表采用顺序存储结构,删除它的第i数据元素之前,需要先依次向前移动( )个数据元素。( C ) A .n-i B.ni C.n-i-1 D.n-i1 2.在单链表中,已知q指的结点是p指的结点的直接前驱结点&am…...
android wifi AsyncChannel(WifiManager和WifiP2pManager)
AynscChannel的讲解 [Android]AsyncChannel介绍-CSDN博客 WifiP2pManager里的channel的使用理解 WifiP2pManager.java public void createGroup(Channel c, ActionListener listener) {checkChannel(c);c.mAsyncChannel.sendMessage(CREATE_GROUP, WifiP2pGroup.NETWORK_ID_PE…...
【Image Captioning】DynRefer
DynRefer是由中国科学院大学于2024年提出的用于1种用于区域级多模态任务的模型。DynRefer 通过模拟人类视觉认知过程,显著提升了区域级多模态识别能力。通过引入人眼的动态分辨率机制, 能够以同时完成区域识别、区域属性检测和区域字幕生成任务。 文章链…...
Midjourney基础-常用修饰词+权重的用法大全
用好修饰词很关键 Midjourney要用除了掌握好提示词的写法,按照上一篇《做Midjourney最好图文教程-提示词公式以及高级参数讲解》画面主体 场景氛围 主体行为 构图方式 艺术风格 图像质量。 要画出有质感的内容我们必须要掌握好“修饰词”,这些修饰…...
没有屋檐的房子-023粪堆旁边的舞蹈
爱美是天性,贫苦的农村人也一样,贫苦的时代也一样。 本世纪,广场舞在华夏大地遍地开花,甚至都传到了外面。但是广场舞这种舞蹈形式并不是互联网时代的特产,也不是电声设备日益高级和普及时代的特产,更不是大…...
基于Docker的Kafka分布式集群
目录 1. 说明 2. 服务器规划 3. docker-compose文件 kafka{i}.yaml kafka-ui.yaml 4. kafka-ui配置集群监控 5. 参数表 6. 测试脚本 生产者-异步生产: AsyncKafkaProducer1.py 消费者-异步消费: AsyncKafkaConsumer1.py 7. 参考 1. 说明 创建一个本地开发环境所需的k…...
【博客之星】年度总结:在云影与墨香中探寻成长的足迹
🐇明明跟你说过:个人主页 🔖行路有良友,便是天堂🔖 目录 一、年度回顾 1、创作历程 2、个人成长 3、个人生活与博客事业 二、技术总结 1、赛道选择 2、技术工具 3、实战项目 三、前景与展望 1、云原生未来…...
SpringBoot的Swagger配置
一、Swagger配置 1.添加依赖 <dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>3.0.2</version> </dependency> 2.修改WebMvcConfig Slf4j Configurat…...
machine learning knn算法之使用KNN对鸢尾花数据集进行分类
通过导入必要的scikit-learn导入必要的库,加载给定的数据,划分测试集和训练集之后训练预测和评估即可 具体代码如下: import numpy as np from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split f…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
