当前位置: 首页 > news >正文

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;
}

流程要点:

  1. 读取命令行参数:确定配置文件 config_file
  2. Skynet全局初始化skynet_globalinit / skynet_env_init 设置一些全局环境,注册信号处理等。
  3. 解析配置:通过 Lua 脚本来读取 config_file 并存到 struct skynet_config
  4. 调用 skynet_start:是 Skynet 的核心启动函数,后面会详细讲。
  5. 清理:退出时 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 模块初始化

  1. skynet_harbor_init:与分布式/harbor机制有关(分布式集群的一部分)。
  2. skynet_handle_init:管理 “Handle -> Service” 映射。
  3. skynet_mq_init:初始化全局消息队列结构(global_queue)。
  4. skynet_module_init:C服务的模块管理,如加载 cpath 下的 .so
  5. skynet_timer_init:定时器初始化,创建 TI = timer_create_timer() 并记录当前系统时间。
  6. skynet_socket_init:socket层初始化,创建 socket_server
  7. 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_exitskynet_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. 小结

  1. 主进程先解析配置,初始化一些全局,随后调用 skynet_start
  2. skynet_start 依次初始化 harbor、handle、mq、module、timer、socket…
  3. 启动logservice(记录日志)和bootstrap(初始Lua服务),最后启动多线程(monitor/timer/socket/worker)。
  4. 整个 Skynet 运行后,Timer 线程负责定时器 & 唤醒 Worker,Socket 线程负责网络事件,Worker 处理消息循环,Monitor检查 Worker 是否卡死。

通过这样一个启动流程,Skynet 建立起一个消息驱动的并发系统框架:

  • Worker 负责业务逻辑
  • Socket 处理IO
  • Timer 提供定时&超时功能
  • Monitor 监控

后续若要深入,可阅读每个线程对应函数(如 thread_timer, thread_worker)里更细节的流程,比如如何 dispatch 消息,如何在定时器中移位管理远期任务等。本篇作为阅读 Skynet 源码的起点,建立对整个启动过程多线程分工有一个全局认识。

相关文章:

skynet 源码阅读 -- 启动主流程

Skynet 启动主流程分析 Skynet 是一个轻量级、高并发的服务器框架。它在启动时会进行一系列初始化操作&#xff0c;并启动多个不同功能的线程&#xff08;Monitor、Timer、Worker、Socket&#xff09;&#xff0c;从而实现消息分发、定时器、网络I/O等核心功能。本文主要从 ma…...

OpenCV:高通滤波之索贝尔、沙尔和拉普拉斯

目录 简述 什么是高通滤波&#xff1f; 高通滤波的概念 应用场景 索贝尔算子 算子公式 实现代码 特点 沙尔算子 算子公式 实现代码 特点 拉普拉斯算子 算子公式 实现代码 特点 高通滤波器的对比与应用场景 相关阅读 OpenCV&#xff1a;图像滤波、卷积与卷积核…...

UDP 广播组播点播的区别及联系

1、网络IP地址的分类 组播地址是分类编址的IPv4地址中的D类地址&#xff0c;又叫多播地址&#xff0c;他的前四位必须是1110&#xff0c;所以网络地址的二进制取值范围是11100000~11101111对应的十进制为 224~~239。所以以224~239开头的网络地址都是组播地址。 组播地址的功能…...

STM32补充——IAP

0 前置知识&#xff1a; FLASH相关内容&#xff1a;前往STM32补充——FLASH STM32三种烧录方式&#xff08;看看就行&#xff09;&#xff1a; 1.ISP&#xff1a;In System Programming&#xff08;在系统编程&#xff09; 执行芯片厂商的 Bootloader 程序进入 ISP 模式&…...

Jetson Xavier NX (ARM) 使用 PyTorch 安装 Open3D-ML 指南

由于 Jetson 为 ARM64 (aarch64) 的系统架构&#xff0c;所以不能用 pip install 直接安装&#xff0c;需要通过源码编译。 升级系统 JetPack 由于 Open3D-ML 目前只支持 CUDA 10.0 以及 CUDA 11.*&#xff0c;并且 JetPack 的 CUDA 开发环境只有10.2、11.4以及12.2&#xff0…...

【C++高并发服务器WebServer】-1:Linux中父子进程fork创建及关系、GDB多进程调试

本文目录 一、进程创建二、GDB多进程调试 一、进程创建 在Linux中输入man 2 fork可以查看man文档中的fork的相关函数信息。 fork的作用就是创建一个子进程。 通过fork我们可以知道&#xff0c;创建子进程的时候&#xff0c;复制父进程的信息。 我们看看翻译的man文档信息&am…...

C语言数组详解:从基础到进阶的全面解析

在C语言中&#xff0c;数组是一种基本的数据结构&#xff0c;用于存储多个相同类型的数据。数组的引入使得C语言能够高效地存储和操作大量数据。在任何一个C语言程序中&#xff0c;数组都发挥着极其重要的作用。无论是在算法实现、数据存储、还是在复杂程序的设计中&#xff0c…...

docker的前世今生

docker来自哪里&#xff1f; 从我们运维部署的历史来看&#xff0c;宿主机从最初的物理机到虚拟机&#xff0c;再到docker&#xff0c;一步步演进到现在。技术演进其实是为了解决当前技术的痛点&#xff0c;那我们来看看有哪些痛点以及如何克服痛点的。 物理机 一般来说&…...

python实现施瓦茨-克里斯托费尔【全网首个】根据用户输入推测函数

上代码&#xff1a; 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语言中的数组(上)

数组的概念 数组是⼀组相同类型元素的集合&#xff1b; 数组中存放的是1个或者多个数据&#xff0c;但是数组元素个数不能为0。 数组中存放的多个数据&#xff0c;类型是相同的。 数组分为⼀维数组和多维数组&#xff0c;多维数组⼀般⽐较多⻅的是⼆维数组。 数组创建 在C语言…...

Unity3D仿星露谷物语开发25之创建时钟界面

1、目标 在时钟界面显示当前时钟信息&#xff0c;同时设置特殊按钮可以快速推进时间用于测试。 2、创建GameClock.cs脚本 在Assets -> Scripts -> TimeSystem目录下创建GameClock.cs脚本。 代码如下&#xff1a; using System.Collections; using System.Collections…...

数据结构测试题1

一、选择题: 1&#xff0e;若长度为n的钱性表采用顺序存储结构&#xff0c;删除它的第i数据元素之前&#xff0c;需要先依次向前移动( )个数据元素。( C ) A .n-i B.ni C.n-i-1 D.n-i1 2.在单链表中&#xff0c;已知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 通过模拟人类视觉认知过程&#xff0c;显著提升了区域级多模态识别能力。通过引入人眼的动态分辨率机制&#xff0c; 能够以同时完成区域识别、区域属性检测和区域字幕生成任务。 文章链…...

Midjourney基础-常用修饰词+权重的用法大全

用好修饰词很关键 Midjourney要用除了掌握好提示词的写法&#xff0c;按照上一篇《做Midjourney最好图文教程-提示词公式以及高级参数讲解》画面主体 场景氛围 主体行为 构图方式 艺术风格 图像质量。 要画出有质感的内容我们必须要掌握好“修饰词”&#xff0c;这些修饰…...

没有屋檐的房子-023粪堆旁边的舞蹈

爱美是天性&#xff0c;贫苦的农村人也一样&#xff0c;贫苦的时代也一样。 本世纪&#xff0c;广场舞在华夏大地遍地开花&#xff0c;甚至都传到了外面。但是广场舞这种舞蹈形式并不是互联网时代的特产&#xff0c;也不是电声设备日益高级和普及时代的特产&#xff0c;更不是大…...

基于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…...

【博客之星】年度总结:在云影与墨香中探寻成长的足迹

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、年度回顾 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导入必要的库&#xff0c;加载给定的数据&#xff0c;划分测试集和训练集之后训练预测和评估即可 具体代码如下&#xff1a; import numpy as np from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split f…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

【Go语言基础【12】】指针:声明、取地址、解引用

文章目录 零、概述&#xff1a;指针 vs. 引用&#xff08;类比其他语言&#xff09;一、指针基础概念二、指针声明与初始化三、指针操作符1. &&#xff1a;取地址&#xff08;拿到内存地址&#xff09;2. *&#xff1a;解引用&#xff08;拿到值&#xff09; 四、空指针&am…...

如何更改默认 Crontab 编辑器 ?

在 Linux 领域中&#xff0c;crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用&#xff0c;用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益&#xff0c;允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

适应性Java用于现代 API:REST、GraphQL 和事件驱动

在快速发展的软件开发领域&#xff0c;REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名&#xff0c;不断适应这些现代范式的需求。随着不断发展的生态系统&#xff0c;Java 在现代 API 方…...

MySQL 主从同步异常处理

阅读原文&#xff1a;https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主&#xff0c;遇到的这个错误&#xff1a; Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一&#xff0c;通常表示&#xff…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态

前言 在人工智能技术飞速发展的今天&#xff0c;深度学习与大模型技术已成为推动行业变革的核心驱动力&#xff0c;而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心&#xff0c;系统性地呈现了两部深度技术著作的精华&#xff1a;…...

小木的算法日记-多叉树的递归/层序遍历

&#x1f332; 从二叉树到森林&#xff1a;一文彻底搞懂多叉树遍历的艺术 &#x1f680; 引言 你好&#xff0c;未来的算法大神&#xff01; 在数据结构的世界里&#xff0c;“树”无疑是最核心、最迷人的概念之一。我们中的大多数人都是从 二叉树 开始入门的&#xff0c;它…...