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位数据项,不是任意类型数据

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

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

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

相关推荐
ScilogyHunter12 天前
Zephyr串口驱动开发及构建完全指南
驱动开发·uart·zephyr
ScilogyHunter12 天前
Zephyr Hello World应用开发构建完全指南
zephyr·hello world
ScilogyHunter12 天前
Zephyr Twister测试框架完全指南
zephyr·twister
ScilogyHunter13 天前
west init 命令详解
init·zephyr·west
ScilogyHunter13 天前
使用Kconfig配置Zephyr工程完全指南
kconfig·zephyr
ScilogyHunter13 天前
Zephyr设备树完全指南
zephyr
ScilogyHunter14 天前
Zephyr项目按需配置完全指南
zephyr
ScilogyHunter14 天前
Zephyr最简工程配置指南
zephyr
ScilogyHunter14 天前
Zephyr主仓库目录结构完全指南
zephyr
ScilogyHunter14 天前
Zephyr工程配置完全指南
zephyr