Zephyr OS 中的互斥信号量

目录

概述

1 互斥信号量介绍

1.1 基本概念

1.2 主要特性

2 Zephyr OS中的互斥信号量API

2.1 K_MUTEX_DEFINE函数

2.2 k_mutex_lock函数

2.3 k_mutex_unlock

3 Zephyr OS互斥信号量的使用

3.1 基本用法

3.2 递归锁定与释放

4 互锁信号量应用注意点

4.1 关键特性

4.2  常见错误


概述

本文主要介绍Zephyr OS 中的互斥信号量的相关内容。互斥信号量(Mutex)是操作系统中的一种线程同步机制,用于保护共享资源,防止多个线程同时访问导致的竞态条件。它具有互斥性、原子性、阻塞机制和所有权等特性。在ZephyrOS中,互斥信号量的API包括K_MUTEX_DEFINE、k_mutex_lock和k_mutex_unlock等函数,用于静态定义、获取和释放互斥量。使用互斥信号量时,需注意保持临界区短小、配合RAII模式、带超时使用以及递归锁定与释放等。同时,应避免常见错误,如忘记解锁、错误线程解锁、不平衡的锁定/解锁和解锁未锁定的互斥量。

1 互斥信号量介绍

1.1 基本概念

互斥信号量(Mutex,全称 Mutual Exclusion)是操作系统中最常用的线程同步机制之一,用于保护共享资源,防止多个线程同时访问导致的竞态条件。

1.2 主要特性

互斥信号量是一种特殊的二进制信号量,用于实现对共享资源的互斥访问。与普通信号量不同,互斥信号量具有以下特性:

  1. 互斥性:同一时刻只允许一个线程持有互斥量

  2. 原子性:对互斥量的操作是不可分割的

  3. 阻塞机制:获取失败的线程会进入阻塞状态

  4. 所有权:只有获取互斥量的线程才能释放它

2 Zephyr OS中的互斥信号量API

2.1 K_MUTEX_DEFINE函数

K_MUTEX_DEFINE 是 Zephyr RTOS 中用于静态定义和初始化互斥信号量的宏,它是创建互斥量最常用的方式之一。

基本语法

K_MUTEX_DEFINE(mutex_name);

功能说明

  1. 静态初始化:在编译时完成互斥量的内存分配和初始化

  2. 零开销初始化:不需要运行时调用初始化函数

  3. 全局可见:定义的互斥量可以在定义它的文件内外使用

 应用Demo

#include <zephyr/kernel.h>

// 静态定义一个名为my_mutex的互斥量
K_MUTEX_DEFINE(my_mutex);

void thread_func(void) 
{
    // 获取互斥量
    if (k_mutex_lock(&my_mutex, K_MSEC(100)) {
        // 处理获取失败
        return;
    }
    
    // 临界区代码
    // ...
    
    // 释放互斥量
    k_mutex_unlock(&my_mutex);
}

2.2 k_mutex_lock函数

k_mutex_lock 是 Zephyr RTOS 中用于获取互斥信号量的核心函数,它提供了线程安全的资源访问控制机制。

函数原型

int k_mutex_lock(struct k_mutex *mutex, k_timeout_t timeout);

参数说明

参数类型说明
mutexstruct k_mutex*指向要获取的互斥量的指针
timeoutk_timeout_t指定等待超时时间,可以是:
K_NO_WAIT:不等待,立即返回
K_FOREVER:无限等待
- 具体时间值(如 K_MSEC(100)

 返回值

返回值说明
0成功获取互斥量
-EBUSY使用 K_NO_WAIT 时互斥量已被占用
-EAGAIN在指定超时时间内未能获取互斥量
-EDEADLK检测到死锁(某些配置下)

 带超时的用法

void critical_operation(void)
{
    // 等待最多50ms获取互斥量
    int ret = k_mutex_lock(&data_mutex, K_MSEC(50));
    
    if (ret == 0) {
        // 成功获取锁
        do_work();
        k_mutex_unlock(&data_mutex);
    } else if (ret == -EAGAIN) {
        // 超时处理
        handle_timeout();
    } else {
        // 其他错误处理
        handle_error();
    }
}

非阻塞尝试用法

void try_operation(void)
{
    if (k_mutex_lock(&data_mutex, K_NO_WAIT) == 0) {
        // 成功获取锁
        do_work();
        k_mutex_unlock(&data_mutex);
    } else {
        // 互斥量已被占用,执行替代操作
        alternative_work();
    }
}

2.3 k_mutex_unlock

k_mutex_unlock 是 Zephyr RTOS 中用于释放互斥信号量的核心函数,它与 k_mutex_lock 配对使用来管理临界区访问。

函数原型

int k_mutex_unlock(struct k_mutex *mutex);

参数说明

参数类型说明
mutexstruct k_mutex*指向要释放的互斥量的指针

返回值

返回值说明
0成功释放互斥量
-EPERM当前线程不是互斥量的所有者
-EINVAL无效参数(如mutex为NULL)或互斥量未被锁定

3 Zephyr OS互斥信号量的使用

3.1 基本用法

1)线程中保护共享资源的用法

K_MUTEX_DEFINE(data_mutex);

void thread_function(void)
{
    // 获取互斥量
    if (k_mutex_lock(&data_mutex, K_FOREVER) == 0) {
        // 临界区 - 安全访问共享资源
        access_shared_data();
        
        // 释放互斥量
        int ret = k_mutex_unlock(&data_mutex);
        if (ret != 0) {
            printk("解锁失败: %d\n", ret);
        }
    }
}

2)保持临界区短小

k_mutex_lock(&mutex, K_FOREVER);
// 只包含必要的共享资源访问代码
k_mutex_unlock(&mutex);

3)配合RAII模式(资源获取即初始化)

void guarded_operation(void) 
{
    if (k_mutex_lock(&mutex, K_FOREVER) != 0) {
        return; // 错误处理
    }
    
    // 确保任何退出路径都会释放互斥量
    do {
        if (error_condition) {
            break;
        }
        // ...操作...
    } while (0);
    
    k_mutex_unlock(&mutex);
}

4) 带超时的用法

void critical_operation(void)
{
    // 等待最多50ms获取互斥量
    int ret = k_mutex_lock(&data_mutex, K_MSEC(50));
    
    if (ret == 0) {
        // 成功获取锁
        do_work();
        k_mutex_unlock(&data_mutex);
    } else if (ret == -EAGAIN) {
        // 超时处理
        handle_timeout();
    } else {
        // 其他错误处理
        handle_error();
    }
}

3.2 递归锁定与释放

void recursive_function(struct k_mutex *m, int level)
{
    k_mutex_lock(m, K_FOREVER);
    
    if (level > 0) {
        recursive_function(m, level-1);
    }
    
    // 每次递归调用都会匹配一个unlock
    k_mutex_unlock(m);
}

// 使用示例
K_MUTEX_DEFINE(recursive_mutex);
recursive_function(&recursive_mutex, 3);  // 锁定3次,解锁3次

4 互锁信号量应用注意点

4.1 关键特性

1)优先级继承:

  • 当高优先级线程等待低优先级线程持有的互斥量时

  • 系统会临时提升低优先级线程的优先级

  • 防止"优先级反转"问题

2) 递归锁定

  • 同一线程可以多次锁定同一个互斥量

  • 必须释放相同次数才能真正释放互斥量

k_mutex_lock(&mutex, K_FOREVER);  // 第一次锁定
k_mutex_lock(&mutex, K_FOREVER);  // 第二次锁定(递归)

k_mutex_unlock(&mutex);  // 第一次释放
k_mutex_unlock(&mutex);  // 第二次释放

 3)线程所有权

  • 只有锁定互斥量的线程才能解锁它

  • 其他线程尝试解锁会返回错误

4)性能考虑

1. 解锁操作通常很快,但在以下情况可能有额外开销:

1)有高优先级线程在等待该互斥量
2)需要恢复原始优先级


2 避免过于频繁的锁定/解锁,如下demo中,过于频繁加锁-解锁

// 不推荐 - 过于频繁的锁定
for (int i = 0; i < 100; i++) {
    k_mutex_lock(&mutex, K_FOREVER);
    array[i] = value;
    k_mutex_unlock(&mutex);
}

// 推荐 - 单次锁定保护整个操作
k_mutex_lock(&mutex, K_FOREVER);
for (int i = 0; i < 100; i++) {
    array[i] = value;
}
k_mutex_unlock(&mutex);

4.2  常见错误

1) 忘记解锁

k_mutex_lock(&mutex, K_FOREVER);
// 临界区代码...
// 忘记调用k_mutex_unlock()

2) 错误线程解锁

// 线程A
k_mutex_lock(&mutex, K_FOREVER);

// 线程B尝试解锁
k_mutex_unlock(&mutex);  // 将返回-EPERM

3) 不平衡的锁定/解锁

k_mutex_lock(&mutex, K_FOREVER);
k_mutex_lock(&mutex, K_FOREVER); // 递归锁定

k_mutex_unlock(&mutex); 
// 仍有一个锁定未释放

4) 解锁未锁定的互斥量

struct k_mutex mutex;
k_mutex_init(&mutex);
k_mutex_unlock(&mutex);  // 返回-EINVAL

在STM32 ZET6这样的嵌入式环境中,通常会使用RTOS(实时操作系统),如FreeRTOSZephyr OS或RTOS API(如ARM Mbed OS)来管理任务和同步机制。为了实现生产者消费者模型以及任务间的通信,你可以采取以下几个步骤: 1. **创建任务**: - 创建生产者任务(Producer Task):它负责生成整数,并将其放入队列中。当队列满时,可以采用阻塞策略,让生产者暂停直到有空间可用。 - 创建消费者任务(Consumer Task):从队列中取出并处理数据,特别是检测到的数据是5的倍数时,通过串口发送出去。 - 创建LED控制任务(Indicator Task):周期性地检查系统的状态,例如队列是否满或空,以此来改变LED的状态作为指示。 2. **队列操作**: - 使用固定大小的环形缓冲区(Ring Buffer)作为队列,这里设其深度为5。生产者写入元素,消费者读取元素,需要保证原子性的插入和删除操作,以避免数据混乱。 - STM32通常提供了硬件协助的互斥锁(Mutex)、信号量(Semaphore)或条件变量(Condition Variables)来保护队列操作,防止并发时的竞态条件。 3. **任务通信**: - 生产者在存入数据到队列前,可以通过信号量通知消费者有新数据可用。 - 消费者取出数据后,如果发现是5的倍数,可以通过中断或者其他同步机制告知LED控制任务更新状态。 4. **任务协作**: - 使用事件循环或者延时函数,设置定时器来调度各任务轮流执行,确保任务之间的协调。 ```markdown
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值