内存数据结构 Channel
,类似于 Go
的 chan
通道,底层基于 共享内存 + Mutex
互斥锁实现,可实现用户态的高性能内存队列。Channel
可用于多进程环境下,底层在读取写入时会自动加锁,应用层不需要担心数据同步问题。
channel
在之前的文章中出现过,当时用于 manager
和 worker
进程之间进行通信的重要数据结构,主要用于 worker
进程通知 manager
进程重启相应 worker
进程。
channel
数据结构channel
数据结构的属性比较多,head
是队列的头部位置,tail
是队列的尾部位置,size
是申请的队列内存大小,maxlen
是每个队列元素的大小,head_tag
和 tail_tag
用于指定队列的头尾是否循环被重置回头部。bytes
是当前 channel
队列占用的内存大小,flag
用来指定是否使用共享内存、是否使用锁、是否使用 pipe
通知。mem
是 channel
的内存首地址。
typedef struct _swChannel_item
{
int length;
char data[0];
} swChannel_item;
typedef struct _swChannel
{
off_t head;
off_t tail;
size_t size;
char head_tag;
char tail_tag;
int num;
int max_num;
/**
* Data length, excluding structure
*/
size_t bytes;
int flag;
int maxlen;
/**
* memory point
*/
void *mem;
swLock lock;
swPipe notify_fd;
} swChannel;
channel
队列swChannel_new
创建队列创建队列就是根据 flags
来初始化队列的各个属性,值得注意的是 maxlen
,当申请内存的时候会多申请这些内存,用来防止内存越界。
swChannel* swChannel_new(size_t size, int maxlen, int flags)
{
assert(size >= maxlen);
int ret;
void *mem;
//use shared memory
if (flags & SW_CHAN_SHM)
{
mem = sw_shm_malloc(size + sizeof(swChannel) + maxlen);
}
else
{
mem = sw_malloc(size + sizeof(swChannel) + maxlen);
}
if (mem == NULL)
{
swWarn("swChannel_create: malloc(%ld) failed.", size);
return NULL;
}
swChannel *object = mem;
mem += sizeof(swChannel);
bzero(object, sizeof(swChannel));
//overflow space
object->size = size;
object->mem = mem;
object->maxlen = maxlen;
object->flag = flags;
//use lock
if (flags & SW_CHAN_LOCK)
{
//init lock
if (swMutex_create(&object->lock, 1) < 0)
{
swWarn("mutex init failed.");
return NULL;
}
}
//use notify
if (flags & SW_CHAN_NOTIFY)
{
ret = swPipeNotify_auto(&object->notify_fd, 1, 1);
if (ret < 0)
{
swWarn("notify_fd init failed.");
return NULL;
}
}
return object;
}
swChannel_push
入队入队的时候,首先要先加锁,然后调用 swChannel_in
。
swChannel_in
逻辑很简单,向队列的尾部推送数据,如果当前 channel
尾部被重置,head
还未被重置,就需要先判断剩余的内存是否够用。
如果当前 channel
尾部未被重置,就可以放心的追加元素,因为 object->size
和真正申请的内存之前还有 maxlen
可以富余,不必考虑内存越界的问题。
int swChannel_push(swChannel *object, void *in, int data_length)
{
assert(object->flag & SW_CHAN_LOCK);
object->lock.lock(&object->lock);
int ret = swChannel_in(object, in, data_length);
object->lock.unlock(&object->lock);
return ret;
}
#define swChannel_full(ch) ((ch->head == ch->tail && ch->tail_tag != ch->head_tag) || (ch->bytes + sizeof(int) * ch->num == ch->size))
int swChannel_in(swChannel *object, void *in, int data_length)
{
assert(data_length <= object->maxlen);
if (swChannel_full(object))
{
return SW_ERR;
}
swChannel_item *item;
int msize = sizeof(item->length) + data_length;
if (object->tail < object->head)
{
//no enough memory space
if ((object->head - object->tail) < msize)
{
return SW_ERR;
}
item = object->mem + object->tail;
object->tail += msize;
}
else
{
item = object->mem + object->tail;
object->tail += msize;
if (object->tail >= object->size)
{
object->tail = 0;
object->tail_tag = 1 - object->tail_tag;
}
}
object->num++;
object->bytes += data_length;
item->length = data_length;
memcpy(item->data, in, data_length);
return SW_OK;
}
swChannel_push
出队swChannel_push
出队的逻辑比较简单,获取队列头部位置,然后拷贝首部数据即可。当 head
超过 size
值,即可重置 head
。
int swChannel_pop(swChannel *object, void *out, int buffer_length)
{
assert(object->flag & SW_CHAN_LOCK);
object->lock.lock(&object->lock);
int n = swChannel_out(object, out, buffer_length);
object->lock.unlock(&object->lock);
return n;
}
#define swChannel_empty(ch) (ch->num == 0)
int swChannel_out(swChannel *object, void *out, int buffer_length)
{
if (swChannel_empty(object))
{
return SW_ERR;
}
swChannel_item *item = object->mem + object->head;
assert(buffer_length >= item->length);
memcpy(out, item->data, item->length);
object->head += (item->length + sizeof(item->length));
if (object->head >= object->size)
{
object->head = 0;
object->head_tag = 1 - object->head_tag;
}
object->num--;
object->bytes -= item->length;
return item->length;
}
Original url: Access
Created at: 2018-10-10 17:13:15
Category: default
Tags: none
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
java windows火焰图_mob64ca12ec8020的技术博客_51CTO博客 - 在windows下不可行,不知道作者是怎样搞的 监听SpringBoot 服务启动成功事件并打印信息_监听springboot启动完毕-CSDN博客 SpringBoot中就绪探针和存活探针_management.endpoint.health.probes.enabled-CSDN博客 u2u转换板 - 嘉立创EDA开源硬件平台 Spring Boot 项目的轻量级 HTTP 客户端 retrofit 框架,快来试试它!_Java精选-CSDN博客 手把手教你打造一套最牛的知识笔记管理系统! - 知乎 - 想法有重合-理论可参考 安宇雨 闲鱼 机械键盘 客制化 开贴记录 文本 linux 使用find命令查找包含某字符串的文件_beijihukk的博客-CSDN博客_find 查找字符串 ---- mac 也适用 安宇雨 打字音 记录集合 B站 bilibili 自行搭建 开坑 真正的客制化 安宇雨 黑苹果开坑 查找工具包maven pom 引用地 工具网站 Dantelis 介绍的玩轴入坑攻略 --- 关于轴的一些说法 --- 非官方 ---- 心得而已 --- 长期开坑更新 [本人问题][新开坑位]关于自动化测试的工具与平台应用 机械键盘 开团 网站记录 -- 能做一个收集的程序就好了 不过现在没时间 -- 信息大多是在群里发的 - 你要让垃圾佬 都去一个地方看难度也是很大的 精神支柱 [超级前台]sprinbboot maven superdesk-app 记录 [信息有用] [环境准备] [基本完成] [sebp/elk] 给已创建的Docker容器增加新的端口映射 - qq_30599553的博客 - CSDN博客 [正在研究] Elasticsearch, Logstash, Kibana (ELK) Docker image documentation elasticsearch centos 安装记录 及 启动手记 正式服务器 39 elasticsearch 问题合集 不断更新 6.1.1 | 6.5.1 两个版本 博客程序 - 测试 - bug记录 等等问题 laravel的启动过程解析 - lpfuture - 博客园 OAuth2 Server PHP 用 Laravel 搭建带 OAuth2 验证的 RESTful 服务 | Laravel China 社区 - 高品质的 Laravel 和 PHP 开发者社区 利用Laravel 搭建oauth2 API接口 附 Unauthenticated 解决办法 - 煮茶的博客 - SegmentFault 思否 使用 OAuth2-Server-php 搭建 OAuth2 Server - 午时的海 - 博客园 基于PHP构建OAuth 2.0 服务端 认证平台 - Endv - 博客园 Laravel 的 Artisan 命令行工具 Laravel 的文件系统和云存储功能集成 浅谈Chromium中的设计模式--终--Observer模式 浅谈Chromium中的设计模式--二--pre/post和Delegate模式 浅谈Chromium中的设计模式--一--Chromium中模块分层和进程模型 DeepMind 4 Hacking Yourself README.md update 20211011
Laravel China 简书 知乎 博客园 CSDN博客 开源中国 Go Further Ryan是菜鸟 | LNMP技术栈笔记 云栖社区-阿里云 Netflix技术博客 Techie Delight Linkedin技术博客 Dropbox技术博客 Facebook技术博客 淘宝中间件团队 美团技术博客 360技术博客 古巷博客 - 一个专注于分享的不正常博客 软件测试知识传播 - 测试窝 有赞技术团队 阮一峰 语雀 静觅丨崔庆才的个人博客 软件测试从业者综合能力提升 - isTester IBM Java 开发 使用开放 Java 生态系统开发现代应用程序 pengdai 一个强大的博主 HTML5资源教程 | 分享HTML5开发资源和开发教程 蘑菇博客 - 专注于技术分享的博客平台 个人博客-leapMie 流星007 CSDN博客 - 舍其小伙伴 稀土掘金 Go 技术论坛 | Golang / Go 语言中国知识社区
最新评论