Zephyr spinlock

文章介绍了SMP(对称多处理技术)环境下,自旋锁作为并发控制的一种手段,与互斥锁的区别。自旋锁在获取锁失败时不使线程睡眠,而是持续检查锁的状态。在加锁和解锁过程中,涉及到了中断的管理以及多核环境下的锁竞争。自旋锁适用于锁持有时间短的情况,以保证高效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SMP 与自旋锁

  • SMP 即对称多处理技术,在多核CPU上,各CPU之间共享内存和其他资源,但是只有一个操作系统,操作系统将各个任务均匀的分配该多个CPU执行,从而极大的提高系统的并行处理能力。

  • 由于多个CPU之间的内存是共享的,当CPU使用共享资源时需要通过互斥机制获得其使用权,实现其互斥的方式有两种,一种是互斥锁,另一种是自旋锁,两者在同一时间都只能一个持有者,但是在工作方式上有很大区别,互斥锁在获取锁时如果发现资源不可用时会使调用线程进入睡眠,而自旋锁不同,获取互斥锁失败时会在原地等待资源可用,不会使调用线程进入睡眠。

  • 单核计算机系统中多个进程访问同一个共享资源时通过屏蔽中断实现互斥,但是在多核计算机系统中,该方式不再有效,屏蔽中断只是让当前CPU不再响应中断请求,但是其他CPU仍然可以响应中断,为此引入了互斥锁,当系统中存在多个CPU时,竞争自旋锁的CPU首先屏蔽中断,然后通过自旋竞争获得共享资源的使用权,而竞争失败的CPU需要等待自旋锁被释放重新进行竞争使用权。

  • 自旋锁适用于锁使用者持锁时间比较短的情况,正是由于自旋锁使用者一般保持锁时间非常短,因此选择自旋而不是睡眠是非常必要的,自旋锁的效率远高于互斥锁。

spinlock 加锁

static ALWAYS_INLINE k_spinlock_key_t k_spin_lock(struct k_spinlock *l)
{
	ARG_UNUSED(l);
	k_spinlock_key_t k;

	/* 关中断 */
	k.key = arch_irq_lock();

#ifdef CONFIG_SPIN_VALIDATE
	/* 确认当前CPU未获取到互斥锁 */
	__ASSERT(z_spin_lock_valid(l), "Recursive spinlock %p", l);
# ifdef CONFIG_KERNEL_COHERENCE
	__ASSERT_NO_MSG(z_spin_lock_mem_coherent(l));
# endif
#endif

#ifdef CONFIG_SMP
	/* 自旋等待锁可用,获取失败继续尝试直到成功 */
	while (!atomic_cas(&l->locked, 0, 1)) {
	}
#endif

#ifdef CONFIG_SPIN_VALIDATE
    /* 设置自旋锁持有持有线程和持有CPU */
	z_spin_lock_set_owner(l);
#if defined(CONFIG_SPIN_LOCK_TIME_LIMIT) && (CONFIG_SPIN_LOCK_TIME_LIMIT != 0)
	l->lock_time = sys_clock_cycle_get_32();
#endif /* CONFIG_SPIN_LOCK_TIME_LIMIT */
#endif/* CONFIG_SPIN_VALIDATE */
	return k;
}

spinlock 解锁

static ALWAYS_INLINE void k_spin_unlock(struct k_spinlock *l,
					k_spinlock_key_t key)
{
	ARG_UNUSED(l);
#ifdef CONFIG_SPIN_VALIDATE
	/* 确认锁的持有者为当前CPU和当前线程 */
	__ASSERT(z_spin_unlock_valid(l), "Not my spinlock %p", l);

#if defined(CONFIG_SPIN_LOCK_TIME_LIMIT) && (CONFIG_SPIN_LOCK_TIME_LIMIT != 0)
	uint32_t delta = sys_clock_cycle_get_32() - l->lock_time;

	__ASSERT(delta < CONFIG_SPIN_LOCK_TIME_LIMIT,
		 "Spin lock %p held %u cycles, longer than limit of %u cycles",
		 l, delta, CONFIG_SPIN_LOCK_TIME_LIMIT);
#endif /* CONFIG_SPIN_LOCK_TIME_LIMIT */
#endif /* CONFIG_SPIN_VALIDATE */

#ifdef CONFIG_SMP
	/* Strictly we don't need atomic_clear() here (which is an
	 * exchange operation that returns the old value).  We are always
	 * setting a zero and (because we hold the lock) know the existing
	 * state won't change due to a race.  But some architectures need
	 * a memory barrier when used like this, and we don't have a
	 * Zephyr framework for that.
	 */

	 /* 自旋锁解锁 */
	atomic_clear(&l->locked);
#endif
	/* 开启中断 */
	arch_irq_unlock(key.key);
}
#include <zephyr/kernel.h> #include <zephyr/device.h> #include <zephyr/devicetree.h> #include <zephyr/drivers/gpio.h> #include <zephyr/logging/log.h> #include <zephyr/drivers/uart.h> #include "GVL.h" #define LOG_MODULE_NAME rs485_thread LOG_MODULE_REGISTER(LOG_MODULE_NAME); // 获取设备树节点 #define ZEPHYR_USER_NODE DT_PATH(zephyr_user) // 声明设备 static const struct device *const rs485_uart = DEVICE_DT_GET(DT_ALIAS(myuart)); static const struct gpio_dt_spec direct_pin = GPIO_DT_SPEC_GET(ZEPHYR_USER_NODE, rs485_gpios); // 延时工作 static struct k_work_delayable uart_work; // RS485接收状态机 enum rx_state { STATE_IDLE, STATE_GOT_AA, STATE_RECEIVING }; static struct { enum rx_state state; uint8_t buffer[sizeof(rs485_raw_data_t) + 3]; // AA + BB + payload + CRC size_t index; } rx_state_machine; static struct k_spinlock data_spinlock; // CRC校验函数 static uint8_t calculate_crc(const uint8_t *data, size_t len) { uint8_t crc = 0; for (size_t i = 0; i < len; i++) { crc ^= data[i]; } return crc; } // 统一数据结构处理 static void process_rs485_packet(uint8_t *packet, size_t len) { // CRC校验 (最后1字节是CRC) if (calculate_crc(packet, len - 1) != packet[len - 1]) { LOG_WRN("Invalid RS485 packet CRC"); return; } // 解析原始RS485数据 rs485_raw_data_t raw_data; memcpy(&raw_data, packet + 2, sizeof(raw_data)); // 跳过包头 // 转换为统一数据结构 unified_data_point_t data_point = { .timestamp = k_uptime_get_32(), .acc_x = raw_data.acc_x, .acc_y = raw_data.acc_y, .acc_z = raw_data.acc_z, .gyro_x = raw_data.gyro_x, .gyro_y = raw_data.gyro_y, .gyro_z = raw_data.gyro_z, .data_source = 2 // 标记为RS485设备数据 }; // 添加到批处理 k_spinlock_key_t key = k_spin_lock(&batch_lock); batch_container_t *batch = &data_batches[active_batch_index]; if (batch->count < BATCH_SIZE) { batch->data[batch->count++] = data_point; // 批次满或超时处理 if (batch->count >= BATCH_SIZE || (batch->count > 0 && (k_uptime_get() - batch->start_timestamp) > MAX_BATCH_TIMEOUT_MS)) { batch->ready = true; batch->start_timestamp = k_uptime_get(); // 切换到备用缓冲区 active_batch_index = (active_batch_index + 1) % 2; data_batches[active_batch_index].count = 0; data_batches[active_batch_index].ready = false; // 通知BLE线程 k_sem_give(&ble_data_ready_sem); } } else { LOG_WRN("Batch buffer overflow"); } k_spin_unlock(&batch_lock, key); } static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data) { static size_t aborted_len; struct uart_data_t *buf; static uint8_t *aborted_buf; static bool disable_req; k_spinlock_key_t key; switch (evt->type) { case UART_TX_DONE: LOG_DBG("UART_TX_DONE"); // 设置方向为接收模式 gpio_pin_set_dt(&direct_pin, 0); if ((evt->data.tx.len == 0) || (!evt->data.tx.buf)) { return; } if (aborted_buf) { buf = CONTAINER_OF(aborted_buf, struct uart_data_t, data[0]); aborted_buf = NULL; aborted_len = 0; k_free(buf); } else { buf = CONTAINER_OF(evt->data.tx.buf, struct uart_data_t, data[0]); k_free(buf); } // 发送下一个数据包 buf = k_fifo_get(&fifo_uart_tx_data, K_NO_WAIT); if (buf) { // 设置方向为发送模式 gpio_pin_set_dt(&direct_pin, 1); k_busy_wait(RS485_DIR_PIN_DELAY_US); if (uart_tx(rs485_uart, buf->data, buf->len, SYS_FOREVER_MS)) { LOG_WRN("Failed to send data over UART"); gpio_pin_set_dt(&direct_pin, 0); // 确保切换回接收模式 k_free(buf); } } break; case UART_RX_RDY: const uint8_t *data = evt->data.rx.buf; size_t len = evt->data.rx.len; for (size_t i = 0; i < len; i++) { uint8_t byte = data[i]; switch (rx_state_machine.state) { case STATE_IDLE: if (byte == 0xAA) { rx_state_machine.state = STATE_GOT_AA; } break; case STATE_GOT_AA: if (byte == 0xBB) { rx_state_machine.state = STATE_RECEIVING; rx_state_machine.index = 0; rx_state_machine.buffer[rx_state_machine.index++] = 0xAA; rx_state_machine.buffer[rx_state_machine.index++] = 0xBB; } else { rx_state_machine.state = (byte == 0xAA) ? STATE_GOT_AA : STATE_IDLE; } break; case STATE_RECEIVING: rx_state_machine.buffer[rx_state_machine.index++] = byte; // 检查完整数据包 (AA + BB + payload + CRC) if (rx_state_machine.index >= sizeof(rx_state_machine.buffer)) { process_rs485_packet(rx_state_machine.buffer, rx_state_machine.index); rx_state_machine.state = STATE_IDLE; } break; } } break; case UART_RX_DISABLED: LOG_DBG("UART_RX_DISABLED"); disable_req = false; buf = k_malloc(sizeof(*buf)); if (buf) { buf->len = 0; uart_rx_enable(rs485_uart, buf->data, sizeof(buf->data), UART_RX_TIMEOUT_MS); } else { LOG_WRN("Not able to allocate UART receive buffer"); k_work_reschedule(&uart_work, K_MSEC(UART_WAIT_FOR_RX)); } break; case UART_RX_BUF_REQUEST: LOG_DBG("UART_RX_BUF_REQUEST"); buf = k_malloc(sizeof(*buf)); if (buf) { buf->len = 0; uart_rx_buf_rsp(rs485_uart, buf->data, sizeof(buf->data)); } else { LOG_WRN("Not able to allocate UART receive buffer"); } break; case UART_RX_BUF_RELEASED: LOG_DBG("UART_RX_BUF_RELEASED"); buf = CONTAINER_OF(evt->data.rx_buf.buf, struct uart_data_t, data[0]); k_free(buf); break; case UART_TX_ABORTED: LOG_DBG("UART_TX_ABORTED"); gpio_pin_set_dt(&direct_pin, 0); // 确保切换回接收模式 if (!aborted_buf) { aborted_buf = (uint8_t *)evt->data.tx.buf; aborted_len = evt->data.tx.len; } struct uart_data_t *tx_buf = CONTAINER_OF(aborted_buf, struct uart_data_t, data); size_t remaining = tx_buf->len - aborted_len; if (remaining > 0) { gpio_pin_set_dt(&direct_pin, 1); k_busy_wait(RS485_DIR_PIN_DELAY_US); uart_tx(rs485_uart, &tx_buf->data[aborted_len], remaining, SYS_FOREVER_MS); } break; default: break; } } static void uart_work_handler(struct k_work *item) { struct uart_data_t *buf = k_malloc(sizeof(*buf)); if (buf) { buf->len = 0; uart_rx_enable(rs485_uart, buf->data, sizeof(buf->data), UART_RX_TIMEOUT_MS); } else { LOG_WRN("Not able to allocate UART receive buffer"); k_work_reschedule(&uart_work, K_MSEC(UART_WAIT_FOR_RX)); } } static int rs485_init(void) { int err; if (!device_is_ready(rs485_uart)) { LOG_ERR("UART device not ready"); return -ENODEV; } // 初始化方向控制引脚 if (!device_is_ready(direct_pin.port)) { LOG_ERR("GPIO device not ready"); return -ENODEV; } err = gpio_pin_configure_dt(&direct_pin, GPIO_OUTPUT_INACTIVE); if (err) { LOG_ERR("GPIO config error: %d", err); return err; } // 初始化引脚为接收 gpio_pin_set_dt(&direct_pin, 0); // 初始化接收状态机 rx_state_machine.state = STATE_IDLE; rx_state_machine.index = 0; // 初始化UART k_work_init_delayable(&uart_work, uart_work_handler); err = uart_callback_set(rs485_uart, uart_cb, NULL); if (err) { LOG_ERR("Cannot set UART callback: %d", err); return err; } // 启动接收 k_work_schedule(&uart_work, K_NO_WAIT); return 0; } void rs485_send(const uint8_t *data, size_t len) { struct uart_data_t *tx = k_malloc(sizeof(*tx) + len); if (!tx) { LOG_ERR("Not enough memory for UART send"); return; } memcpy(tx->data, data, len); tx->len = len; // 如果发送队列为空,直接发送,否则加入队列 if (k_fifo_is_empty(&fifo_uart_tx_data)) { // 设置方向为发送模式 gpio_pin_set_dt(&direct_pin, 1); k_busy_wait(RS485_DIR_PIN_DELAY_US); if (uart_tx(rs485_uart, tx->data, tx->len, SYS_FOREVER_MS)) { LOG_WRN("Failed to start UART send"); gpio_pin_set_dt(&direct_pin, 0); // 切换回接收模式 k_free(tx); } } else { k_fifo_put(&fifo_uart_tx_data, tx); } } void rs485_thread(void) { if (rs485_init() != 0) { LOG_ERR("RS485 initialization failed"); return; } LOG_INF("RS485 thread started"); // 初始化自旋锁 k_spinlock_init(&data_spinlock); // 线程主循环 while (1) { k_sleep(K_FOREVER); } } K_THREAD_DEFINE(rs485_thread_id, 2048, rs485_thread, NULL, NULL, NULL, 5, 0, 0);
06-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咕咚.萌西

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值