做为核心功能,Skynet 仅解决一个问题:
把一个符合规范的 C 模块,从动态库(so 文件)中启动起来,绑定一个永不重复(即使模块退出)的数字 id 做为其 handle 。模块被称为服务(Service),服务间可以自由发送消息。每个模块可以向 Skynet 框架注册一个 callback 函数,用来接收发给它的消息。每个服务都是被一个个消息包驱动,当没有包到来的时候,它们就会处于挂起状态,对 CPU 资源零消耗。
云风 <<skynet设计综述>>
除去网络,定时器这些基础模块外,skynet做的事情其实很简单:
- 解析so文件,加载为一个个c服务。
- c服务间通过消息队列来互相驱动。
拆分为模块就是:
看代码:
- skynet_mq.h
- skynet_mq.c
#ifndef SKYNET_MESSAGE_QUEUE_H
#define SKYNET_MESSAGE_QUEUE_H
#include <stdlib.h>
#include <stdint.h>
struct skynet_message {
uint32_t source; /* toby@2022-03-02): 发送方服务id */
int session; /* toby@2022-03-02): 标识消息,类似消息序号,逻辑层用到 */
void * data;
size_t sz; /* toby@2022-03-02): 64位操作系统下8个字节 */
};
// type is encoding in skynet_message.sz high 8bit
#define MESSAGE_TYPE_MASK (SIZE_MAX >> 8)
#define MESSAGE_TYPE_SHIFT ((sizeof(size_t)-1) * 8)
struct message_queue;
void skynet_globalmq_push(struct message_queue * queue);
struct message_queue * skynet_globalmq_pop(void);
struct message_queue * skynet_mq_create(uint32_t handle);
/* toby@2022-03-02): 销毁队列时,剩余消息的处理函数 */
typedef void (*message_drop)(struct skynet_message *, void *);
/* toby@2022-03-02): 标记为待释放,插入全局消息队列 */
void skynet_mq_mark_release(struct message_queue *q);
/* toby@2022-03-02): 如果标记为待释放则销毁消息队列,否则插入全局消息队列 */
void skynet_mq_release(struct message_queue *q, message_drop drop_func, void *ud);
uint32_t skynet_mq_handle(struct message_queue *);
// 0 for success
int skynet_mq_pop(struct message_queue *q, struct skynet_message *message);
void skynet_mq_push(struct message_queue *q, struct skynet_message *message);
// return the length of message queue, for debug
int skynet_mq_length(struct message_queue *q);
int skynet_mq_overload(struct message_queue *q);
/* toby@2022-03-02): 初始化全局消息队列 */
void skynet_mq_init();
#endif
#include "skynet.h"
#include "skynet_mq.h"
#include "skynet_handle.h"
#include "spinlock.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdbool.h>
#define DEFAULT_QUEUE_SIZE 64
#define MAX_GLOBAL_MQ 0x10000
// 0 means mq is not in global mq.
// 1 means mq is in global mq , or the message is dispatching.
#define MQ_IN_GLOBAL 1
#define MQ_OVERLOAD 1024
struct message_queue {
struct spinlock lock; /* toby@2022-03-02): TODO 锁住的时候进行 从全局队列中插拔 */
uint32_t handle; /* toby@2022-03-02): 服务id 每个服务一个队列 */
int cap; /* toby@2022-03-02): TODO 消息数量限制 */
int head; /* toby@2022-03-02): TODO 当前该读取的消息索引 */
int tail; /* toby@2022-03-02): TODO 当前该插入的消息索引 */
int release; /* toby@2022-03-02): 待释放标志 */
int in_global; /* toby@2022-03-02): MQ_IN_GLOBAL 是否在全局队列中 */
int overload;
int overload_threshold; /* toby@2022-03-02): 阈值 = 2^n > overload > 2^(n - 1) */
struct skynet_message *queue; /* toby@2022-03-02): 消息数组,动态扩容 */
struct message_queue *next; /* toby@2022-03-02): 在全局消息队列中时,指向下一个队列 */
};
struct global_queue {
struct message_queue *head;
struct message_queue *tail;
struct spinlock lock;
};
static struct global_queue *Q = NULL;
void
skynet_globalmq_push(struct message_queue * queue) {
struct global_queue *q= Q;
SPIN_LOCK(q)
assert(queue->next == NULL);
if(q->tail) {
q->tail->next = queue;
q->tail = queue;
} else {
q->head = q->tail = queue;
}
SPIN_UNLOCK(q)
}
struct message_queue *
skynet_globalmq_pop() {
struct global_queue *q = Q;
SPIN_LOCK(q)
struct message_queue *mq = q->head;
if(mq) {
q->head = mq->next;
if(q->head == NULL) {
assert(mq == q->tail);
q->tail = NULL;
}
mq->next = NULL;
}
SPIN_UNLOCK(q)
return mq;
}
struct message_queue *
skynet_mq_create(uint32_t handle) {
struct message_queue *q = skynet_malloc(sizeof(*q));
q->handle = handle;
q->cap = DEFAULT_QUEUE_SIZE;
q->head = 0;
q->tail = 0;
SPIN_INIT(q)
// When the queue is create (always between service create and service init) ,
// set in_global flag to avoid push it to global queue .
// If the service init success, skynet_context_new will call skynet_mq_push to push it to global queue.
/* toby@2022-03-02): 这里注释写错了,应该是直接调用 skynet_globalmq_push */
q->in_global = MQ_IN_GLOBAL;
q->release = 0;
q->overload = 0;
q->overload_threshold = MQ_OVERLOAD;
q->queue = skynet_malloc(sizeof(struct skynet_message) * q->cap);
q->next = NULL;
return q;
}
static void
_release(struct message_queue *q) {
assert(q->next == NULL);
SPIN_DESTROY(q)
skynet_free(q->queue);
skynet_free(q);
}
uint32_t
skynet_mq_handle(struct message_queue *q) {
return q->handle;
}
int
skynet_mq_length(struct message_queue *q) {
int head, tail,cap;
SPIN_LOCK(q)
head = q->head;
tail = q->tail;
cap = q->cap;
SPIN_UNLOCK(q)
if (head <= tail) {
return tail - head;
}
return tail + cap - head;
}
int
skynet_mq_overload(struct message_queue *q) {
if (q->overload) {
int overload = q->overload;
q->overload = 0;
return overload;
}
return 0;
}
int
skynet_mq_pop(struct message_queue *q, struct skynet_message *message) {
int ret = 1;
SPIN_LOCK(q)
if (q->head != q->tail) {
/* toby@2022-03-02): 头游标不等于尾游标 表示还有消息待读 */
*message = q->queue[q->head++];
ret = 0;
int head = q->head;
int tail = q->tail;
int cap = q->cap;
if (head >= cap) {
q->head = head = 0;
}
int length = tail - head;
if (length < 0) {
length += cap;
}
while (length > q->overload_threshold) {
q->overload = length;
q->overload_threshold *= 2;
}
} else {
// reset overload_threshold when queue is empty
q->overload_threshold = MQ_OVERLOAD;
}
if (ret) {
/* toby@2022-03-02): 没有消息可处理 */
q->in_global = 0;
}
SPIN_UNLOCK(q)
return ret;
}
static void
expand_queue(struct message_queue *q) {
struct skynet_message *new_queue = skynet_malloc(sizeof(struct skynet_message) * q->cap * 2);
int i;
for (i=0;i<q->cap;i++) {
new_queue[i] = q->queue[(q->head + i) % q->cap];
}
q->head = 0;
q->tail = q->cap;
q->cap *= 2;
skynet_free(q->queue);
q->queue = new_queue;
}
void
skynet_mq_push(struct message_queue *q, struct skynet_message *message) {
assert(message);
SPIN_LOCK(q)
q->queue[q->tail] = *message;
if (++ q->tail >= q->cap) {
q->tail = 0;
}
if (q->head == q->tail) {
expand_queue(q);
}
if (q->in_global == 0) {
q->in_global = MQ_IN_GLOBAL;
skynet_globalmq_push(q);
}
SPIN_UNLOCK(q)
}
void
skynet_mq_init() {
struct global_queue *q = skynet_malloc(sizeof(*q));
memset(q,0,sizeof(*q));
SPIN_INIT(q);
Q=q;
}
void
skynet_mq_mark_release(struct message_queue *q) {
SPIN_LOCK(q)
assert(q->release == 0);
q->release = 1;
if (q->in_global != MQ_IN_GLOBAL) {
skynet_globalmq_push(q);
}
SPIN_UNLOCK(q)
}
static void
_drop_queue(struct message_queue *q, message_drop drop_func, void *ud) {
struct skynet_message msg;
while(!skynet_mq_pop(q, &msg)) {
drop_func(&msg, ud);
}
_release(q);
}
void
skynet_mq_release(struct message_queue *q, message_drop drop_func, void *ud) {
SPIN_LOCK(q)
if (q->release) {
SPIN_UNLOCK(q)
_drop_queue(q, drop_func, ud);
} else {
skynet_globalmq_push(q);
SPIN_UNLOCK(q)
}
}
xmind:
代码也就那么几十百把行,大家都可以读读看,后续代码看的更多,框架了解更全面后应该会有新的发现。