Zephyr RTOS中的k_stack相关函数

目录

概述

[1 核心函数介绍](#1 核心函数介绍)

[1.1 核心函数](#1.1 核心函数)

[1.2 栈的核心概念](#1.2 栈的核心概念)

[1.3 栈与其他数据结构的对比](#1.3 栈与其他数据结构的对比)

[2 函数详解与使用示例](#2 函数详解与使用示例)

[2.1 初始化栈](#2.1 初始化栈)

[2.2 压入数据:k_stack_push()](#2.2 压入数据:k_stack_push())

[2.3 弹出数据:k_stack_pop()](#2.3 弹出数据:k_stack_pop())

[2.4 完整应用示例](#2.4 完整应用示例)

[2.5 高级特性与内存池初始化](#2.5 高级特性与内存池初始化)

[3 应用与配置](#3 应用与配置)

[3.1 Kconfig 配置](#3.1 Kconfig 配置)

[3.2 性能优化技巧](#3.2 性能优化技巧)

[3.3 错误处理模式](#3.3 错误处理模式)

[3.4 栈的典型应用场景](#3.4 栈的典型应用场景)

[4 核心点总结](#4 核心点总结)


概述

在 Zephyr RTOS 中,k_stack栈(LIFO - 后进先出) 数据结构的内核实现。它允许多个线程向一个共享的栈空间压入和弹出数据项,是 Zephyr 中基本的 内核对象 之一。

1 核心函数介绍

1.1 核心函数

函数 功能描述 主要用途
k_stack_init() 动态初始化栈 运行时初始化栈对象
k_stack_alloc_init() 从内存池初始化栈 动态分配栈存储空间
k_stack_push() 向栈中压入数据 生产者添加数据项
k_stack_pop() 从栈中弹出数据 消费者取出数据项
k_stack_cleanup() 清理栈资源 释放动态分配的栈

1.2 栈的核心概念

k_stack 管理一个固定大小的 数据项数组 (每个数据项是 stack_data_t 类型,实际上是 uint32_t)。它遵循经典的 后进先出 原则:

cpp 复制代码
          push 56           push 78           pop -> 78
          ┌───┐             ┌───┐             ┌───┐
          │   │             │ 78│             │   │
          ├───┤             ├───┤             ├───┤
          │ 12│    ===>     │ 56│    ===>     │ 56│
          ├───┤             ├───┤             ├───┤
栈底 ───► │ 34│             │ 34│             │ 34│
          └───┘             └───┘             └───┘
          初始状态          压入56,78         弹出顶部

1.3 栈与其他数据结构的对比

特性 栈 (k_stack) 消息队列 (k_msgq) 管道 (k_pipe)
数据顺序 后进先出 (LIFO) 先进先出 (FIFO) 先进先出 (FIFO)
数据单元 32位字 任意大小消息 字节流
阻塞行为 空时弹出阻塞,满时压入失败 空/满时可阻塞 空/满时可阻塞
典型用途 撤销操作、函数调用跟踪、临时存储 任务间通信、事件传递 流数据、串口通信
内存效率 高(固定大小项) 中等(有消息边界开销) 高(纯字节缓冲区)

2 函数详解与使用示例

2.1 初始化栈

Zephyr 提供两种初始化栈的方式:

1) 静态初始化(推荐)

cpp 复制代码
#include <zephyr/kernel.h>

/* 定义栈:最多存储10个32位数据项 */
#define STACK_SIZE 10
K_STACK_DEFINE(my_stack, STACK_SIZE);

void example_static_init(void) {
    printk("栈已静态初始化,容量:%d\n", STACK_SIZE);
}

2) 动态初始化

cpp 复制代码
/* 动态初始化示例 */
void example_dynamic_init(void) {
    stack_data_t stack_buffer[STACK_SIZE];
    struct k_stack my_dyn_stack;
    
    /* 动态初始化,提供已有的缓冲区 */
    k_stack_init(&my_dyn_stack, stack_buffer, STACK_SIZE);
    printk("栈已动态初始化\n");
}

2.2 压入数据:k_stack_push()

1) 函数原型

cpp 复制代码
int k_stack_push(struct k_stack *stack, stack_data_t data);

参数:

  • stack:栈对象指针

  • data:要压入的32位数据

返回值:

  • 0:成功

  • -ENOMEM:栈已满

2) 示例:生产者线程

cpp 复制代码
/* 生产者:向栈压入数据 */
void producer_thread(void *arg1, void *arg2, void *arg3) {
    struct k_stack *stack = (struct k_stack *)arg1;
    uint32_t counter = 0;
    int ret;
    
    while (1) {
        /* 准备数据 */
        uint32_t data = counter++ | 0xABCD0000;
        
        /* 尝试压入栈 */
        ret = k_stack_push(stack, data);
        
        if (ret == 0) {
            printk("[P] 压入: 0x%08x\n", data);
        } else {
            printk("[P] 栈满! 等待...\n");
            k_sleep(K_MSEC(100));
        }
        
        k_sleep(K_MSEC(50));
    }
}

2.3 弹出数据:k_stack_pop()

1) 函数原型

cpp 复制代码
int k_stack_pop(struct k_stack *stack, stack_data_t *data, k_timeout_t timeout);

参数:

  • stack:栈对象指针

  • data:指向存储弹出数据的指针

  • timeout:等待超时(栈空时可能阻塞)

返回值:

  • 0:成功弹出数据

  • -EAGAIN:栈空且超时

2) 示例:消费者线程

cpp 复制代码
/* 消费者:从栈弹出数据 */
void consumer_thread(void *arg1, void *arg2, void *arg3) {
    struct k_stack *stack = (struct k_stack *)arg1;
    stack_data_t data;
    int ret;
    
    while (1) {
        /* 从栈弹出数据(最多等待100ms) */
        ret = k_stack_pop(stack, &data, K_MSEC(100));
        
        if (ret == 0) {
            printk("[C] 弹出: 0x%08x\n", data);
            
            /* 处理数据 */
            process_data(data);
        } else if (ret == -EAGAIN) {
            printk("[C] 栈空,等待数据...\n");
        }
    }
}

/* 数据处理函数 */
void process_data(stack_data_t data) {
    uint32_t id = data & 0xFFFF;
    uint32_t prefix = (data >> 16) & 0xFFFF;
    
    if (prefix == 0xABCD) {
        printk("  处理有效数据: ID=%u\n", id);
    }
}

2.4 完整应用示例

cpp 复制代码
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>

/* 1. 定义栈 */
#define MAX_STACK_ITEMS 8
K_STACK_DEFINE(data_stack, MAX_STACK_ITEMS);

/* 2. 生产者线程 */
void producer(void *stack_ptr, void *unused2, void *unused3) {
    struct k_stack *stack = (struct k_stack *)stack_ptr;
    uint32_t item_count = 0;
    
    while (1) {
        /* 生成数据 */
        uint32_t data = (k_cycle_get_32() & 0xFFFF) | (item_count << 16);
        
        /* 尝试压入栈 */
        if (k_stack_push(stack, data) == 0) {
            item_count++;
            printk("Producer: 压入 item %u (0x%08x)\n", item_count, data);
        } else {
            printk("Producer: 栈满,等待...\n");
        }
        
        /* 监控栈状态 */
        #if CONFIG_STACK_RUNTIME_STATS
        struct k_stack_stats stats;
        k_stack_stats_get(stack, &stats);
        printk("  栈使用: %zu/%d\n", stats.used, MAX_STACK_ITEMS);
        #endif
        
        k_sleep(K_MSEC(200));
    }
}

/* 3. 消费者线程 */
void consumer(void *stack_ptr, void *unused2, void *unused3) {
    struct k_stack *stack = (struct k_stack *)stack_ptr;
    uint32_t items_processed = 0;
    
    k_sleep(K_MSEC(100));  /* 让生产者先启动 */
    
    while (1) {
        stack_data_t data;
        
        /* 弹出数据(永久等待) */
        if (k_stack_pop(stack, &data, K_FOREVER) == 0) {
            items_processed++;
            
            /* 解析数据 */
            uint32_t value = data & 0xFFFF;
            uint32_t seq = (data >> 16) & 0xFFFF;
            
            printk("Consumer: 弹出 seq=%u, value=%u (总计处理: %u)\n",
                   seq, value, items_processed);
            
            /* 模拟处理时间 */
            k_busy_wait(50000);  /* 50us */
        }
        
        /* 每处理几个项目后稍作休息 */
        if (items_processed % 5 == 0) {
            k_sleep(K_MSEC(10));
        }
    }
}

/* 4. 主函数 */
int main(void) {
    printk("=== 栈示例开始 ===\n");
    printk("栈容量: %d 个32位数据项\n", MAX_STACK_ITEMS);
    
    /* 创建生产者和消费者线程 */
    k_thread_create(&producer_thread, producer_stack,
                    K_THREAD_STACK_SIZEOF(producer_stack),
                    producer, &data_stack, NULL, NULL,
                    5, 0, K_NO_WAIT);
    
    k_thread_create(&consumer_thread, consumer_stack,
                    K_THREAD_STACK_SIZEOF(consumer_stack),
                    consumer, &data_stack, NULL, NULL,
                    6, 0, K_NO_WAIT);
    
    k_sleep(K_FOREVER);
    return 0;
}

2.5 高级特性与内存池初始化

1) 使用内存池动态分配栈

cpp 复制代码
#include <zephyr/kernel.h>
#include <zephyr/sys/mempool.h>

/* 定义内存池 */
K_MEM_POOL_DEFINE(stack_pool, 64, 512, 4, 4);

void dynamic_stack_example(void) {
    struct k_stack *dyn_stack;
    int ret;
    
    /* 从内存池分配栈对象 */
    ret = k_mem_pool_alloc(&stack_pool, (struct k_mem_block *)&dyn_stack,
                          sizeof(struct k_stack) + (MAX_ITEMS * sizeof(stack_data_t)),
                          K_MSEC(100));
    
    if (ret == 0) {
        /* 初始化动态分配的栈 */
        stack_data_t *buffer = (stack_data_t *)(dyn_stack + 1);
        k_stack_alloc_init(dyn_stack, MAX_ITEMS);
        
        /* 使用栈... */
        k_stack_push(dyn_stack, 0x12345678);
        
        /* 清理 */
        k_stack_cleanup(dyn_stack);
        k_mem_pool_free(&stack_pool, dyn_stack);
    }
}

2) 多线程竞争场景

cpp 复制代码
/* 多个生产者,一个消费者 */
void multiple_producers(void) {
    #define NUM_PRODUCERS 3
    
    for (int i = 0; i < NUM_PRODUCERS; i++) {
        k_thread_create(&producer_tid[i], producer_stacks[i],
                       K_THREAD_STACK_SIZEOF(producer_stacks[i]),
                       producer_func, &shared_stack, (void *)(long)i, NULL,
                       7, 0, K_NO_WAIT);
    }
    
    /* 栈本身是线程安全的,无需额外同步 */
}

/* 生产者函数(带生产者ID) */
void producer_func(void *stack_ptr, void *id_ptr, void *unused3) {
    struct k_stack *stack = (struct k_stack *)stack_ptr;
    int producer_id = (int)(long)id_ptr;
    
    while (1) {
        uint32_t data = (producer_id << 24) | (k_cycle_get_32() & 0xFFFFFF);
        
        if (k_stack_push(stack, data) == 0) {
            printk("P%d: 压入 0x%08x\n", producer_id, data);
        }
        
        k_sleep(K_MSEC(100 * (producer_id + 1)));
    }
}

3 应用与配置

3.1 Kconfig 配置

cpp 复制代码
# 启用栈支持
CONFIG_STACKS=y

# 启用运行时统计(调试用)
CONFIG_STACK_RUNTIME_STATS=y

# 栈的最大数量
CONFIG_MAX_STACK_COUNT=20

# 栈的默认大小(如使用k_stack_alloc_init)
CONFIG_STACK_DEFAULT_SIZE=16

3.2 性能优化技巧

cpp 复制代码
/* 技巧1:批量操作减少上下文切换 */
void batch_push(struct k_stack *stack, uint32_t *data, size_t count) {
    for (size_t i = 0; i < count; i++) {
        if (k_stack_push(stack, data[i]) != 0) {
            /* 处理栈满情况 */
            k_sleep(K_MSEC(1));
            i--;  /* 重试当前项 */
        }
    }
}

/* 技巧2:非阻塞弹出尝试 */
int try_pop(struct k_stack *stack, uint32_t *data) {
    return k_stack_pop(stack, data, K_NO_WAIT);
}

/* 技巧3:合理的栈深度设置 */
#define CALC_STACK_DEPTH(avg_push_rate, avg_pop_rate, max_latency_ms) \
    ((avg_push_rate - avg_pop_rate) * max_latency_ms / 1000 + 1)

/* 示例:每秒压入100次,弹出80次,最大延迟100ms */
#define OPTIMAL_DEPTH CALC_STACK_DEPTH(100, 80, 100)  /* = 3 */

3.3 错误处理模式

cpp 复制代码
/* 安全的栈操作包装函数 */
int safe_stack_push(struct k_stack *stack, uint32_t data, int max_retries) {
    int retries = 0;
    
    while (retries < max_retries) {
        if (k_stack_push(stack, data) == 0) {
            return 0;  /* 成功 */
        }
        
        retries++;
        
        /* 指数退避策略 */
        k_sleep(K_MSEC(10 * (1 << (retries - 1))));
        
        if (retries % 5 == 0) {
            printk("栈满警告: 已重试 %d 次\n", retries);
        }
    }
    
    return -ENOMEM;  /* 失败 */
}

/* 带超时的弹出 */
int timed_stack_pop(struct k_stack *stack, uint32_t *data, 
                    k_timeout_t base_timeout, int max_attempts) {
    int attempts = 0;
    k_timeout_t timeout = base_timeout;
    
    while (attempts < max_attempts) {
        int ret = k_stack_pop(stack, data, timeout);
        
        if (ret == 0) {
            return 0;  /* 成功 */
        }
        
        attempts++;
        
        /* 每次增加等待时间 */
        timeout = K_MSEC(k_timeout_to_ms(timeout) * 2);
        
        printk("弹出尝试 %d 失败,等待 %lld ms\n", 
               attempts, k_timeout_to_ms(timeout));
    }
    
    return -EAGAIN;  /* 多次尝试后失败 */
}

3.4 栈的典型应用场景

1) 撤销操作栈

cpp 复制代码
#define MAX_UNDO_LEVELS 10
K_STACK_DEFINE(undo_stack, MAX_UNDO_LEVELS);

void execute_command(uint32_t command) {
    /* 保存当前状态到撤销栈 */
    if (k_stack_push(&undo_stack, save_current_state()) == 0) {
        /* 执行命令 */
        run_command(command);
    }
}

void undo_last_command(void) {
    uint32_t previous_state;
    
    if (k_stack_pop(&undo_stack, &previous_state, K_NO_WAIT) == 0) {
        restore_state(previous_state);
    } else {
        printk("没有可撤销的操作\n");
    }
}

2) 递归算法转迭代

cpp 复制代码
/* 使用栈实现深度优先搜索 */
void depth_first_search(struct node *start) {
    K_STACK_DEFINE(node_stack, 100);
    k_stack_push(&node_stack, (stack_data_t)start);
    
    while (1) {
        struct node *current;
        
        if (k_stack_pop(&node_stack, (stack_data_t *)&current, K_NO_WAIT) != 0) {
            break;  /* 栈空,搜索完成 */
        }
        
        process_node(current);
        
        /* 将子节点压栈(后进先出保证深度优先) */
        for (struct node *child = current->first_child; 
             child != NULL; 
             child = child->next_sibling) {
            k_stack_push(&node_stack, (stack_data_t)child);
        }
    }
}

4 核心点总结

1) Zephyr 的 k_stack 提供了一种简单高效的 LIFO 数据结构,适用于:

  1. 撤销/重做功能:保存操作历史

  2. 深度优先遍历:替代递归调用

  3. 临时数据缓冲:需要后进先出顺序时

  4. 线程间简单通信:轻量级数据传递

2) 关键要点:

  • 栈存储的是 32位数据项,不是任意类型数据

  • 操作是 线程安全 的,可被多个线程并发访问

  • 没有内置的溢出保护,需应用程序自己管理栈深度

  • 与消息队列相比更轻量,适合小数据项传输

相关推荐
fitpolo9 天前
向控制台打印消息并进行日志记录
zephyr
智驾21 天前
【瑞萨RA8D1 LVGL/LWIP评测】二、CPKCOR-RA8D1B Zephyr工程适配
瑞萨·zephyr·ra8d1 lvgl/lwip
智驾1 个月前
【瑞萨RA x Zephyr评测】四、在线调试功能
vscode·debug·瑞萨·zephyr·renesas·ra6e2·fpb-ra6e2
智驾1 个月前
【瑞萨RA x Zephyr评测】三、CAN模块测试
嵌入式·瑞萨·canfd·zephyr·renesas·ra6e2
星源~1 个月前
Zephyr - MCU 开发快速入门指南
单片机·嵌入式硬件·物联网·嵌入式开发·zephyr
TedLeeX2 个月前
【Nordic随笔】离线安装NCS3.2.1教程
经验分享·nordic·zephyr·ncs·nrf54
智驾2 个月前
【瑞萨RA x Zephyr评测】二、ADC模块测试
adc·瑞萨·zephyr·fpb-ra6e2
Sean_woo19982 个月前
Zephyr rtos ESP32系列BSP提交流程指南
stm32·单片机·esp32·wsl·zephyr·立创开发板
TedLeeX2 个月前
【Nordic随笔】从零开始学习使用nRF Connect SDK(一、安装ncs3.2.0步骤)
学习·nordic·zephyr·ncs·nrf54