> For the complete documentation index, see [llms.txt](https://xd96dx.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://xd96dx.gitbook.io/docs/you-xi-kai-fa/skynet-cong-demo-dao-yuan-ma/skynet-yuan-ma-fen-xi-qi-dong-liu-cheng-zhi-chu-shi-hua.md).

# skynet 源码分析-启动流程之初始化

skynet 启动, 相当于在 c 代码里注册一个lua 虚拟机, 在其中解释lua 代码,  并通过c 内存在lua 各个线程之间共享使用, 接下来开始看skynet 的启动流程

### main函数

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2FIHipIKdwV9CVAnXrrcOd%2Fimage.png?alt=media&amp;token=28cf087e-b555-49b5-b163-3d66609723ee" alt=""><figcaption></figcaption></figure>

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2FfgPcdO1dKaEBGag78QZI%2Fimage.png?alt=media&amp;token=5c385c06-40f0-40d8-978f-037e059feb1c" alt=""><figcaption></figcaption></figure>

整个过程中, 主要是初始化一些全局数据, 加载lua lib, 启动service, 并监听, 接下来会逐一解释这些过程

***

### skynet\_globalinit:

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2F2P8Gf7SWB8kQbJneTWqP%2Fimage.png?alt=media&amp;token=32d4f8f6-cc47-4af1-9ae5-acf154871400" alt=""><figcaption></figcaption></figure>

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2FuoeaZQCKYZhA4fCQ69cK%2Fimage.png?alt=media&amp;token=3fda25e5-f32b-4e7e-99af-b275e6c73209" alt=""><figcaption></figcaption></figure>

G\_NODE 是一个全局变量, 数据结构就是 skynet\_node, total 是用来记录 skynet\_node 个数的, 每当一个新的 skynet\_context 被创建时, 他的计数就会+1, monitor\_exit 用来表示 monitor 模块是否已经推出, init 表示是否已经初始化, handle\_key 用来存储线程的局部存储, 在 skynet\_initthread 会将这个handle 指向 0xFFFFFFFF 这个地址

***

### skynet\_env\_init:&#x20;

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2Fk0RI2BGHDZ0yAotYDOBZ%2Fimage.png?alt=media&amp;token=3d8209d9-07b5-46f6-8479-b56bb0983d61" alt=""><figcaption></figcaption></figure>

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2FCs8pDgvqlOC7VexwhwyG%2Fimage.png?alt=media&amp;token=1c004305-5205-43b7-92d4-1d556e880599" alt=""><figcaption></figcaption></figure>

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2FIVUH2Pt6HCjLE7gSq2Sd%2Fimage.png?alt=media&amp;token=68877188-adad-4681-a6c7-af2bee96c118" alt=""><figcaption></figcaption></figure>

函数中的E, 是一个全局的 skynet\_env, 其中这个lua\_State 是很重要的数据结构, 可以说 <mark style="color:red;">**每个lua线程, 在c 中就是一个lua\_State,**</mark> 通过这个lua\_state, 在 c 和 lua 之间传递数据

***

### 加载配置文件

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2FmA2KHdPK05DrPGTUHOGb%2Fimage.png?alt=media&amp;token=5836fa9e-8998-42d8-acdf-c10ed2362ccb" alt=""><figcaption></figcaption></figure>

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2FIhnv5xbdAYe48tdrXyqg%2Fimage.png?alt=media&amp;token=68567d08-30f8-49d0-b22b-d2724358dbd4" alt=""><figcaption><p>luaL_openlibs</p></figcaption></figure>

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2F8zqZywtvt5y62HZlHy7i%2Fimage.png?alt=media&amp;token=1bab342d-b279-4405-88a5-e7f17fdb53f0" alt=""><figcaption><p>load_config</p></figcaption></figure>

这两步创建一个新的lua\_State, 在 openlibs 时, 绑定了lua 的一些基础操作, **loadedlibs** 包含了lua包, 协程, table, io...这些基础操作, 在 **luaL\_loadbufferx** 时, 这个 **load\_config** 实际上是一段lua 的代码, 用来解析我们传入的配置文件, 解析出来的配置, 通过 **init\_env** 放在上面初始化的全局 skynet\_env 中, 然后释放这个临时申请的lua虚拟机; 最后加载整个配置, 代码中是默认值, 如果config 里写了则加载config 的值

***

### 启动 !!! skynet\_start 之初始化

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2FTYozJ3XhuV7SC6zngqNp%2Fimage.png?alt=media&amp;token=636f1074-c4e4-4f46-9f73-1add1286a9ea" alt=""><figcaption></figcaption></figure>

#### **1. skynet\_handle\_init:**&#x20;

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2FKpGweGKoOw5au4sIOeVc%2Fimage.png?alt=media&amp;token=3f2d316b-e238-4864-a4bc-b486ac629d4f" alt=""><figcaption></figcaption></figure>

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2FQm3ShwCzOh2D0lWuCK1e%2Fimage.png?alt=media&amp;token=c406b59a-4dbe-4540-8a21-3c836c135584" alt=""><figcaption></figcaption></figure>

这部分比较重要的是解读这个全局的 handle\_storage 作用, 方法本身对这些变量做了初始化,各个字段的含义如下:&#x20;

* **harbor** 这个字段, **0是单机模式, 大于0则是集群模式**
* **slot\_size** 记录当前slot 数组大小
* **slot** 中, **存放所有已注册的 skynet 服务**
* **name\_cap, name\_count** 表示**name数组的最大可存储量**和**当前name数组个数**
* **name**则表示**可以通过名称查找handle**

所有的skynet 服务句柄都会记录在这个全局的 H 中

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2Fkqk9dMRPMopxoEPkh5Ij%2Fimage.png?alt=media&amp;token=122b0f04-c102-430c-89e2-cb2748bbd4de" alt=""><figcaption></figcaption></figure>

在这个操作中, 将harbor 左移24位, 如果是 0 就会得到0x00000000, 在skynet 中, <mark style="color:red;">高八位(0x00)表示非集群模式</mark>, 如果结果就是 0x01000001, 这个代表<mark style="color:red;">远程第1个节点(0x01) 的第1个服务(000001)</mark>

#### 2. skynet\_mq\_init&#x20;

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2FPUePbCCNj3XSIoeI7OkS%2Fimage.png?alt=media&amp;token=43e79c3d-66ea-495d-b37b-5d86e4f4119c" alt=""><figcaption></figcaption></figure>

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2FuZKspwNWCxvF7xQ8pZXB%2Fimage.png?alt=media&amp;token=c019a534-545f-4453-b361-18ecb3910a9b" alt=""><figcaption></figcaption></figure>

skynet\_mq\_init初始化了全局的global\_queue, 是个存储message\_queue的链表, 每个 skynet 服务都会有一个message\_queue, 而skynet 是用线程池来调度任务的, global\_queue就充当了任务池的角色, worker 就可以公平的从中获取任务, 让每个worker 都可以跑起来

#### 3.skynet\_module\_init&#x20;

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2F5LrICk5LREVGWzFK7upV%2Fimage.png?alt=media&amp;token=8e88430b-71a6-4005-a086-c1fd8abc541d" alt=""><figcaption><p>modules</p></figcaption></figure>

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2Ftyro5zjuVTvZd4PPEepu%2Fimage.png?alt=media&amp;token=194a2d11-0d13-4b98-9ca6-f3b4cf3288b9" alt=""><figcaption><p>module init</p></figcaption></figure>

* count: 加载模块数
* path: 指向了配置文件里的 cpath
* m: skynet\_modules 数组, 存储了不同的skynet 模块

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2FntwQFZqTr0h4viAVkHjL%2Fimage.png?alt=media&amp;token=c1fd45e9-b96e-4d8f-a699-f38d61985285" alt=""><figcaption><p>module</p></figcaption></figure>

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2FWCHSqHTnt7dtQJDM2R2v%2Fimage.png?alt=media&amp;token=5883da14-80d3-4b87-bac3-0697721f780c" alt=""><figcaption></figcaption></figure>

module 其实就是这个目录下的文件编译出的动态链接, 这一步出初始化了全局的 module, 但是还没有实际加载这些模块, 这里提前看一下这些模块, 后面启动的时候会用到

#### 4.skynet\_timer\_init&#x20;

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2FcT7x6gK49JRyGgnpzvYS%2Fimage.png?alt=media&amp;token=a00fe7cc-ad4e-4338-9d13-8744ffdf7d9a" alt=""><figcaption><p>timer</p></figcaption></figure>

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2FjDVD64ZhwKkcri4zEB3f%2Fimage.png?alt=media&amp;token=95278a43-1635-45b1-bd4c-75c23e6df48d" alt=""><figcaption><p>global_timer</p></figcaption></figure>

* near: 存储近期定时任务的链表
* t: 存储不同时间级别的定时任务链表
* time: 当前时间
* starttime: 定时器的起始时间
* current: 当前的系统时间（毫秒）
* current\_point: 当前的时间点，用于精确的时间管理

这一步初始化了skynet 的全局定时器, 所有的定时任务, 都需要在这里存储调度, 他的使用逻辑是这样的, near 里存储了即将要执行的任务, t 里存储了各个时间级别的任务, 如果t 中的任务即将执行, 会被放在near链表里, 然后去执行

#### 5. skynet\_socket\_init:

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2FcStfoeDIbQ2x4V2Bg3wU%2Fimage.png?alt=media&amp;token=83777a83-b83c-4b7e-945f-a303b4a76983" alt=""><figcaption><p>socket_server</p></figcaption></figure>

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2FxeFDA89BSl6EDytTxuxr%2Fimage.png?alt=media&amp;token=07c2e934-9371-4dde-b0af-b527b310cc46" alt=""><figcaption><p>socket init</p></figcaption></figure>

<figure><img src="https://1038370733-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsZbVV9N3S0zSZ23753et%2Fuploads%2FT5IM7pyLOXd8JJyzFV9O%2Fimage.png?alt=media&amp;token=bfe4c75a-f0fa-4b19-ab99-5c78846f8302" alt=""><figcaption></figcaption></figure>

这一步做了两个比较重要的事情,  sp\_create() 创建了一个epoll 描述符(linux下), 并把 pipe 的读端 fd\[0] 添加到epoll的事件池中监听 pipe,  pipe在这里主要用作进程间通( IPC ), 然后赋给了 SOCKET\_SERVER, 这部分会在 skynet 网络部分详细讲解, 这里只要明白做了什么事情就可以

到这一步, 所有的全局变量已经初始化完成, 接下来要实际创建skynet 的service了, 并且给全局变量赋值
