/** * 自然写教室智能网关管理软件 V1.0 * * ring_buffer.c - 线程安全环形缓冲区实现 * * 功能说明: * - 固定大小的无锁环形缓冲区(单生产者单消费者场景) * - 支持变长消息的读写(消息头+负载格式) * - 水位线监控与溢出保护 * - 批量读取支持(减少锁竞争) * - 统计信息:写入/读取/丢弃计数 * * 用途:BLE接收线程 → 环形缓冲区 → MQTT发送线程 */ #include #include #include #include #include #include /* ======================== 常量定义 ======================== */ /* 默认缓冲区大小 2MB (可存储约60,000条笔迹坐标) */ #define DEFAULT_BUFFER_SIZE (2 * 1024 * 1024) /* 单条消息最大长度 */ #define MAX_MESSAGE_SIZE 4096 /* 水位线阈值(百分比) */ #define HIGH_WATERMARK_PCT 80 /* 高水位告警阈值 */ #define LOW_WATERMARK_PCT 20 /* 低水位恢复阈值 */ /* 消息头魔数,用于数据完整性校验 */ #define MSG_HEADER_MAGIC 0xBEEF /* ======================== 数据结构 ======================== */ /** * 消息头结构(每条消息在缓冲区中的前缀) * 用于在环形缓冲区中标识消息边界 */ typedef struct { uint16_t magic; /* 魔数校验 0xBEEF */ uint16_t msg_type; /* 消息类型(笔迹/事件/状态) */ uint32_t payload_len; /* 负载数据长度 */ uint32_t timestamp; /* 写入时间戳(秒) */ } __attribute__((packed)) ring_msg_header_t; /** * 环形缓冲区统计信息 */ typedef struct { uint64_t total_write; /* 累计写入消息数 */ uint64_t total_read; /* 累计读取消息数 */ uint64_t total_dropped; /* 因缓冲区满而丢弃的消息数 */ uint64_t total_bytes_in; /* 累计写入字节数 */ uint64_t total_bytes_out; /* 累计读取字节数 */ uint32_t peak_usage; /* 历史最大使用量(字节) */ uint32_t overflow_count; /* 溢出次数 */ } ring_buffer_stats_t; /** * 环形缓冲区主结构 * 采用读写指针追赶模型:write_pos追赶read_pos表示满 */ typedef struct { uint8_t *buffer; /* 缓冲区内存 */ uint32_t capacity; /* 缓冲区总容量 */ volatile uint32_t write_pos; /* 写入位置(生产者更新) */ volatile uint32_t read_pos; /* 读取位置(消费者更新) */ pthread_mutex_t mutex; /* 互斥锁(多生产者场景) */ pthread_cond_t not_empty; /* 非空条件变量 */ pthread_cond_t not_full; /* 非满条件变量 */ ring_buffer_stats_t stats; /* 统计信息 */ bool high_watermark; /* 高水位标志 */ bool initialized; /* 初始化标志 */ } ring_buffer_t; /* ======================== 内部工具函数 ======================== */ /** * 计算缓冲区当前已使用字节数 */ static uint32_t ring_buffer_used(const ring_buffer_t *rb) { uint32_t wp = rb->write_pos; uint32_t rp = rb->read_pos; if (wp >= rp) { return wp - rp; } else { /* 写指针已回绕 */ return rb->capacity - rp + wp; } } /** * 计算缓冲区剩余可用字节数 * 预留1字节防止读写指针重合导致空/满状态混淆 */ static uint32_t ring_buffer_free(const ring_buffer_t *rb) { return rb->capacity - ring_buffer_used(rb) - 1; } /** * 将数据写入环形缓冲区(处理回绕) * 内部函数,调用者需确保空间足够 */ static void ring_write_bytes(ring_buffer_t *rb, const uint8_t *data, uint32_t len) { uint32_t wp = rb->write_pos; /* 计算到缓冲区末尾的连续空间 */ uint32_t tail_space = rb->capacity - wp; if (len <= tail_space) { /* 无需回绕,直接拷贝 */ memcpy(rb->buffer + wp, data, len); } else { /* 需要回绕:先写尾部,再写头部 */ memcpy(rb->buffer + wp, data, tail_space); memcpy(rb->buffer, data + tail_space, len - tail_space); } /* 更新写指针(使用取模运算处理回绕) */ rb->write_pos = (wp + len) % rb->capacity; } /** * 从环形缓冲区读取数据(处理回绕) * 内部函数,调用者需确保数据充足 */ static void ring_read_bytes(ring_buffer_t *rb, uint8_t *data, uint32_t len) { uint32_t rp = rb->read_pos; /* 计算到缓冲区末尾的连续数据 */ uint32_t tail_data = rb->capacity - rp; if (len <= tail_data) { memcpy(data, rb->buffer + rp, len); } else { /* 回绕读取 */ memcpy(data, rb->buffer + rp, tail_data); memcpy(data + tail_data, rb->buffer, len - tail_data); } /* 更新读指针 */ rb->read_pos = (rp + len) % rb->capacity; } /** * 窥探缓冲区数据但不移动读指针 * 用于预读消息头判断消息长度 */ static void ring_peek_bytes(const ring_buffer_t *rb, uint8_t *data, uint32_t len) { uint32_t rp = rb->read_pos; uint32_t tail_data = rb->capacity - rp; if (len <= tail_data) { memcpy(data, rb->buffer + rp, len); } else { memcpy(data, rb->buffer + rp, tail_data); memcpy(data + tail_data, rb->buffer, len - tail_data); } } /** * 检查并更新水位线状态 * 高水位时触发告警,低水位时恢复 */ static void check_watermark(ring_buffer_t *rb) { uint32_t used = ring_buffer_used(rb); uint32_t usage_pct = (used * 100) / rb->capacity; /* 更新峰值记录 */ if (used > rb->stats.peak_usage) { rb->stats.peak_usage = used; } if (!rb->high_watermark && usage_pct >= HIGH_WATERMARK_PCT) { rb->high_watermark = true; printf("[环形缓冲] 高水位告警: 使用率=%u%%, 已用=%u/%u字节\n", usage_pct, used, rb->capacity); } else if (rb->high_watermark && usage_pct <= LOW_WATERMARK_PCT) { rb->high_watermark = false; printf("[环形缓冲] 水位恢复正常: 使用率=%u%%\n", usage_pct); } } /* ======================== 公共接口 ======================== */ /** * 创建并初始化环形缓冲区 * * @param capacity 缓冲区容量(字节),0表示使用默认值2MB * @return 缓冲区指针,NULL表示失败 */ ring_buffer_t *ring_buffer_create(uint32_t capacity) { ring_buffer_t *rb = (ring_buffer_t *)calloc(1, sizeof(ring_buffer_t)); if (rb == NULL) { printf("[环形缓冲] 内存分配失败\n"); return NULL; } rb->capacity = (capacity > 0) ? capacity : DEFAULT_BUFFER_SIZE; rb->buffer = (uint8_t *)malloc(rb->capacity); if (rb->buffer == NULL) { printf("[环形缓冲] 缓冲区内存分配失败, 请求=%u字节\n", rb->capacity); free(rb); return NULL; } /* 初始化同步原语 */ pthread_mutex_init(&rb->mutex, NULL); pthread_cond_init(&rb->not_empty, NULL); pthread_cond_init(&rb->not_full, NULL); rb->write_pos = 0; rb->read_pos = 0; rb->high_watermark = false; rb->initialized = true; memset(&rb->stats, 0, sizeof(rb->stats)); printf("[环形缓冲] 初始化完成, 容量=%u字节 (%.1f MB)\n", rb->capacity, (float)rb->capacity / (1024 * 1024)); return rb; } /** * 销毁环形缓冲区,释放所有资源 */ void ring_buffer_destroy(ring_buffer_t *rb) { if (rb == NULL) return; pthread_mutex_destroy(&rb->mutex); pthread_cond_destroy(&rb->not_empty); pthread_cond_destroy(&rb->not_full); if (rb->buffer) { free(rb->buffer); } printf("[环形缓冲] 已销毁, 总写入=%lu, 总读取=%lu, 丢弃=%lu\n", rb->stats.total_write, rb->stats.total_read, rb->stats.total_dropped); free(rb); } /** * 写入一条消息到环形缓冲区 * 消息格式:[ring_msg_header_t][payload_data] * * @param rb 缓冲区指针 * @param msg_type 消息类型 * @param payload 消息负载数据 * @param payload_len 负载长度 * @return 0=成功, -1=消息过大, -2=缓冲区满 */ int ring_buffer_write(ring_buffer_t *rb, uint16_t msg_type, const uint8_t *payload, uint32_t payload_len) { if (rb == NULL || !rb->initialized) return -1; /* 检查消息大小限制 */ uint32_t total_size = sizeof(ring_msg_header_t) + payload_len; if (payload_len > MAX_MESSAGE_SIZE || total_size > rb->capacity / 2) { return -1; } pthread_mutex_lock(&rb->mutex); /* 检查剩余空间 */ if (ring_buffer_free(rb) < total_size) { /* 缓冲区空间不足,丢弃消息 */ rb->stats.total_dropped++; rb->stats.overflow_count++; pthread_mutex_unlock(&rb->mutex); return -2; } /* 构建消息头 */ ring_msg_header_t header; header.magic = MSG_HEADER_MAGIC; header.msg_type = msg_type; header.payload_len = payload_len; header.timestamp = (uint32_t)time(NULL); /* 写入消息头 */ ring_write_bytes(rb, (const uint8_t *)&header, sizeof(header)); /* 写入消息负载 */ if (payload_len > 0) { ring_write_bytes(rb, payload, payload_len); } /* 更新统计 */ rb->stats.total_write++; rb->stats.total_bytes_in += total_size; /* 检查水位线 */ check_watermark(rb); /* 通知等待的消费者 */ pthread_cond_signal(&rb->not_empty); pthread_mutex_unlock(&rb->mutex); return 0; } /** * 从环形缓冲区读取一条消息 * * @param rb 缓冲区指针 * @param msg_type 输出: 消息类型 * @param payload 输出: 消息负载缓冲区 * @param payload_max 负载缓冲区最大长度 * @param payload_len 输出: 实际负载长度 * @return 0=成功, -1=缓冲区空, -2=消息头损坏 */ int ring_buffer_read(ring_buffer_t *rb, uint16_t *msg_type, uint8_t *payload, uint32_t payload_max, uint32_t *payload_len) { if (rb == NULL || !rb->initialized) return -1; pthread_mutex_lock(&rb->mutex); /* 检查是否有数据可读 */ uint32_t available = ring_buffer_used(rb); if (available < sizeof(ring_msg_header_t)) { pthread_mutex_unlock(&rb->mutex); return -1; } /* 预读消息头(不移动读指针) */ ring_msg_header_t header; ring_peek_bytes(rb, (uint8_t *)&header, sizeof(header)); /* 验证消息头魔数 */ if (header.magic != MSG_HEADER_MAGIC) { /* 消息头损坏 - 尝试跳过一个字节寻找下一个有效消息头 */ rb->read_pos = (rb->read_pos + 1) % rb->capacity; pthread_mutex_unlock(&rb->mutex); return -2; } /* 检查完整消息是否可用 */ uint32_t total_size = sizeof(ring_msg_header_t) + header.payload_len; if (available < total_size) { /* 消息不完整,等待更多数据 */ pthread_mutex_unlock(&rb->mutex); return -1; } /* 跳过消息头 */ rb->read_pos = (rb->read_pos + sizeof(ring_msg_header_t)) % rb->capacity; /* 读取消息负载 */ uint32_t read_len = header.payload_len; if (read_len > payload_max) { read_len = payload_max; /* 跳过剩余无法容纳的部分 */ uint8_t discard_buf[256]; uint32_t skip = header.payload_len - payload_max; while (skip > 0) { uint32_t chunk = (skip > sizeof(discard_buf)) ? sizeof(discard_buf) : skip; ring_read_bytes(rb, discard_buf, chunk); skip -= chunk; } } if (read_len > 0) { ring_read_bytes(rb, payload, read_len); } /* 输出结果 */ if (msg_type) *msg_type = header.msg_type; if (payload_len) *payload_len = read_len; /* 更新统计 */ rb->stats.total_read++; rb->stats.total_bytes_out += total_size; /* 通知等待的生产者 */ pthread_cond_signal(&rb->not_full); pthread_mutex_unlock(&rb->mutex); return 0; } /** * 获取缓冲区使用率百分比 */ uint32_t ring_buffer_usage_percent(const ring_buffer_t *rb) { if (rb == NULL || rb->capacity == 0) return 0; return (ring_buffer_used(rb) * 100) / rb->capacity; } /** * 获取缓冲区统计信息副本 */ void ring_buffer_get_stats(const ring_buffer_t *rb, ring_buffer_stats_t *stats) { if (rb == NULL || stats == NULL) return; memcpy(stats, &rb->stats, sizeof(ring_buffer_stats_t)); } /** * 清空缓冲区所有数据 */ void ring_buffer_flush(ring_buffer_t *rb) { if (rb == NULL) return; pthread_mutex_lock(&rb->mutex); rb->write_pos = 0; rb->read_pos = 0; rb->high_watermark = false; printf("[环形缓冲] 已清空, 丢弃消息=%lu\n", rb->stats.total_dropped); pthread_mutex_unlock(&rb->mutex); }