Zephyr OS 入门与实战

第6章:线程间通信(IPC)基础

1. 信号量(k_sem)与互斥量(k_mutex)

1.1 信号量(Semaphore)

信号量是Zephyr RTOS中最基本的线程间同步机制之一,用于控制对共享资源的访问或线程间的同步。

信号量的主要API:

                /* 初始化信号量 */
                void k_sem_init(struct k_sem *sem, unsigned int initial_count, unsigned int limit);
                
                /* 获取信号量 */
                int k_sem_take(struct k_sem *sem, k_timeout_t timeout);
                
                /* 释放信号量 */
                void k_sem_give(struct k_sem *sem);
                
                /* 重置信号量 */
                void k_sem_reset(struct k_sem *sem);
            

信号量使用示例:

                #include <zephyr.h>
                #include <sys/printk.h>
                
                struct k_sem my_sem;
                
                void thread_a(void *p1, void *p2, void *p3) {
                    while (1) {
                        printk("Thread A waiting for semaphore\n");
                        k_sem_take(&my_sem, K_FOREVER);
                        printk("Thread A got semaphore\n");
                        k_sleep(K_MSEC(1000));
                        k_sem_give(&my_sem);
                        printk("Thread A released semaphore\n");
                    }
                }
                
                void thread_b(void *p1, void *p2, void *p3) {
                    while (1) {
                        printk("Thread B waiting for semaphore\n");
                        k_sem_take(&my_sem, K_FOREVER);
                        printk("Thread B got semaphore\n");
                        k_sleep(K_MSEC(500));
                        k_sem_give(&my_sem);
                        printk("Thread B released semaphore\n");
                    }
                }
                
                void main(void) {
                    k_sem_init(&my_sem, 1, 1);  // 二进制信号量(互斥信号量)
                    
                    k_thread_create(&thread_a_data, thread_a_stack,
                                    K_THREAD_STACK_SIZEOF(thread_a_stack),
                                    thread_a, NULL, NULL, NULL,
                                    THREAD_A_PRIORITY, 0, K_NO_WAIT);
                    
                    k_thread_create(&thread_b_data, thread_b_stack,
                                    K_THREAD_STACK_SIZEOF(thread_b_stack),
                                    thread_b, NULL, NULL, NULL,
                                    THREAD_B_PRIORITY, 0, K_NO_WAIT);
                }
            
注意: 信号量常用于资源计数或线程同步,但不适合用于保护临界区,因为它没有所有权概念。

1.2 互斥量(Mutex)

互斥量是专门设计用于保护共享资源的同步原语,具有所有权概念,只有获取互斥量的线程才能释放它。

互斥量的主要API:

                /* 初始化互斥量 */
                void k_mutex_init(struct k_mutex *mutex);
                
                /* 锁定互斥量 */
                int k_mutex_lock(struct k_mutex *mutex, k_timeout_t timeout);
                
                /* 解锁互斥量 */
                int k_mutex_unlock(struct k_mutex *mutex);
            

互斥量使用示例:

                #include <zephyr.h>
                #include <sys/printk.h>
                
                struct k_mutex shared_mutex;
                int shared_counter = 0;
                
                void increment_thread(void *p1, void *p2, void *p3) {
                    while (1) {
                        k_mutex_lock(&shared_mutex, K_FOREVER);
                        shared_counter++;
                        printk("Counter: %d\n", shared_counter);
                        k_mutex_unlock(&shared_mutex);
                        k_sleep(K_MSEC(100));
                    }
                }
                
                void decrement_thread(void *p1, void *p2, void *p3) {
                    while (1) {
                        k_mutex_lock(&shared_mutex, K_FOREVER);
                        shared_counter--;
                        printk("Counter: %d\n", shared_counter);
                        k_mutex_unlock(&shared_mutex);
                        k_sleep(K_MSEC(150));
                    }
                }
                
                void main(void) {
                    k_mutex_init(&shared_mutex);
                    
                    k_thread_create(&inc_thread_data, inc_thread_stack,
                                    K_THREAD_STACK_SIZEOF(inc_thread_stack),
                                    increment_thread, NULL, NULL, NULL,
                                    PRIORITY, 0, K_NO_WAIT);
                    
                    k_thread_create(&dec_thread_data, dec_thread_stack,
                                    K_THREAD_STACK_SIZEOF(dec_thread_stack),
                                    decrement_thread, NULL, NULL, NULL,
                                    PRIORITY, 0, K_NO_WAIT);
                }
            

1.3 信号量与互斥量的比较

特性 信号量 (k_sem) 互斥量 (k_mutex)
所有权 无所有权概念,任何线程都可以释放 有所有权,只有获取锁的线程才能释放
计数 可以有多个计数 只能是0或1(锁定/未锁定)
优先级继承 不支持 支持(可配置)
使用场景 线程同步、资源计数 保护临界区、共享资源访问
性能 通常更快 由于优先级继承可能稍慢
警告: 避免在中断上下文中使用互斥量,因为互斥量可能导致上下文切换,而中断上下文不允许阻塞。

2. 优先级继承机制实战

优先级继承是互斥量的一个重要特性,用于解决优先级反转问题。当高优先级线程因等待低优先级线程持有的锁而被阻塞时,低优先级线程会临时继承高优先级线程的优先级。

2.1 优先级反转问题

优先级反转是指高优先级线程因为等待低优先级线程释放资源而被阻塞,而低优先级线程又可能被中等优先级线程抢占,导致高优先级线程长时间得不到执行。

2.2 Zephyr中的优先级继承

Zephyr RTOS的互斥量默认启用了优先级继承机制。当高优先级线程尝试获取已被低优先级线程锁定的互斥量时:

  1. 低优先级线程临时继承高优先级线程的优先级
  2. 低优先级线程执行并尽快释放互斥量
  3. 互斥量释放后,低优先级线程恢复其原始优先级

2.3 优先级继承实战示例

                #include <zephyr.h>
                #include <sys/printk.h>
                
                #define LOW_PRIORITY 5
                #define MEDIUM_PRIORITY 3
                #define HIGH_PRIORITY 1
                
                struct k_mutex shared_mutex;
                
                void low_priority_thread(void *p1, void *p2, void *p3) {
                    printk("Low priority thread started\n");
                    k_mutex_lock(&shared_mutex, K_FOREVER);
                    printk("Low priority thread acquired mutex\n");
                    
                    // 模拟长时间持有锁
                    for (int i = 0; i < 5; i++) {
                        printk("Low priority thread working (%d/5)\n", i+1);
                        k_sleep(K_MSEC(500));
                    }
                    
                    k_mutex_unlock(&shared_mutex);
                    printk("Low priority thread released mutex\n");
                }
                
                void medium_priority_thread(void *p1, void *p2, void *p3) {
                    printk("Medium priority thread started\n");
                    while (1) {
                        printk("Medium priority thread running\n");
                        k_sleep(K_MSEC(300));
                    }
                }
                
                void high_priority_thread(void *p1, void *p2, void *p3) {
                    k_sleep(K_MSEC(100));  // 确保低优先级线程先获取锁
                    printk("High priority thread started\n");
                    
                    printk("High priority thread trying to acquire mutex\n");
                    k_mutex_lock(&shared_mutex, K_FOREVER);
                    printk("High priority thread acquired mutex\n");
                    
                    k_mutex_unlock(&shared_mutex);
                    printk("High priority thread released mutex\n");
                }
                
                void main(void) {
                    k_mutex_init(&shared_mutex);
                    
                    k_thread_create(&low_thread_data, low_thread_stack,
                                    K_THREAD_STACK_SIZEOF(low_thread_stack),
                                    low_priority_thread, NULL, NULL, NULL,
                                    LOW_PRIORITY, 0, K_NO_WAIT);
                    
                    k_thread_create(&med_thread_data, med_thread_stack,
                                    K_THREAD_STACK_SIZEOF(med_thread_stack),
                                    medium_priority_thread, NULL, NULL, NULL,
                                    MEDIUM_PRIORITY, 0, K_NO_WAIT);
                    
                    k_thread_create(&high_thread_data, high_thread_stack,
                                    K_THREAD_STACK_SIZEOF(high_thread_stack),
                                    high_priority_thread, NULL, NULL, NULL,
                                    HIGH_PRIORITY, 0, K_NO_WAIT);
                }
            

预期输出分析:

  1. 低优先级线程启动并获取互斥量
  2. 高优先级线程启动并尝试获取互斥量,被阻塞
  3. 此时低优先级线程临时继承高优先级
  4. 中等优先级线程无法抢占低优先级线程(因为它现在是高优先级)
  5. 低优先级线程完成工作后释放互斥量,恢复原始优先级
  6. 高优先级线程获取互斥量并执行
调试技巧: 可以使用Zephyr的线程分析工具(如thread_analyzer)来验证优先级继承是否按预期工作。

2.4 禁用优先级继承

在某些特殊情况下,可能需要禁用优先级继承。可以通过配置内核选项来实现:

                # 在prj.conf文件中
                CONFIG_PRIORITY_INHERITANCE=n
            
警告: 禁用优先级继承可能导致系统出现优先级反转问题,除非有特殊需求,否则不建议禁用。
上一章:线程管理 下一章:消息队列

扫描二维码访问课程店铺

课程客服小姐姐(优先添加)

讲师微信(备用)

客服微信二维码