Support us and view this ad

可选:点击以支持我们的网站

免费文章

引言:GATT并发读写的锁竞争困境 在蓝牙低功耗(BLE)协议栈中,通用属性协议(GATT)层为应用开发者提供了标准化的数据交互接口。然而,在多任务或高吞吐场景下,多个任务对同一个GATT特性(Characteristic)发起并发读写操作时,会引发严重的锁竞争问题。HSK协议栈作为一款面向资源受限嵌入式设备的轻量级BLE实现,其GATT层采用了细粒度锁机制,但不当的并发设计仍可能导致死锁、优先级反转或吞吐量骤降。本文将深入解析HSK协议栈中GATT并发读写的锁机制,并给出基于状态机的性能优化方案。 核心原理:分布式锁与读写状态机 HSK的GATT层并未采用全局互斥锁,而是为每个连接句柄(Connection Handle)维护一个独立的读写锁(rwlock)。其核心数据结构如下: // HSK GATT连接上下文(简化版) typedef struct { uint16_t conn_handle; // 连接句柄 volatile uint32_t lock_state; // 0:空闲 1:读锁定 2:写锁定 uint8_t pending_queue[8]; // 待处理请求队列(环形缓冲区) uint16_t mtu; // 当前MTU大小 } gatt_conn_ctx_t; 每个连接上下文的lock_state字段通过原子操作(如__sync_val_compare_and_swap)实现状态转换。当任务A发起GATT读请求时,会尝试将lock_state从0(空闲)CAS(Compare-And-Swap)为1(读锁定)。若失败(例如已被写锁定),则任务A被挂起并插入pending_queue。写操作具有更高优先级:当写请求到来时,若当前状态为读锁定,写请求会阻塞后续读请求,直到所有读操作释放锁。 时序描述:假设连接句柄0x0001上,任务1发起读请求(t0),任务2发起写请求(t1),任务3发起读请求(t2)。在HSK的实现中: t0: 读锁定成功,lock_state=1。 t1: 写请求尝试CAS(1->2)失败,将自身插入pending_queue,并设置请求类型为写。 t2: 读请求发现pending_queue中有写请求,直接失败返回(避免写饿死)。 t3: 任务1完成读操作,释放锁(lock_state=0),检查pending_queue,发现写请求,立即唤醒任务2。 实现过程:核心API与代码示例 以下为HSK协议栈中GATT并发读写的核心实现片段(C语言,基于FreeRTOS): // 读操作函数(非阻塞版本) hsk_err_t gatt_read_char(uint16_t conn_handle, uint16_t handle, uint8_t* buf, uint16_t* len) { gatt_conn_ctx_t* ctx = &gatt_conn_table[conn_handle]; uint32_t old_state; // 1. 检查是否有写请求等待 if (ctx->pending_queue[0] & 0x02) { // 高位表示写请求 return HSK_ERR_BUSY; } // 2. 尝试获取读锁(CAS操作) old_state = __sync_val_compare_and_swap(&ctx->lock_state, 0, 1); if (old_state !...

继续阅读完整内容

支持我们的网站,请点击查看下方广告

正在加载广告...

登陆