RT-Thread的rt_thread_create函数介绍

本文详细介绍了RT-Thread实时操作系统中线程的创建及调度机制,特别是关于线程优先级与时间片的作用原理。当线程拥有唯一优先级时,时间片参数将不起作用,除非线程主动放弃CPU或等待资源。
thread = rt_thread_create("T_RTU",
						  Thread_RTU, 
						  RT_NULL, 
						  2048, //分配堆栈大小
						  3,    //线程的优先级 
						  20);  //线程所分配的时间片 

当一个线程的优先级独一无二的时候,它的时间片这个参数将失去作用,我们不要认为上面的两个线程运行完 20 个系统 ticks 后就会主动交出 cpu 使用权,当运行完20 个 ticks 后如果它不需等待任何资源,也不主动让出 cpu使用权的话,它还会继续运行,时间片这个参数只在具有相同优先级的线程之间起作用,可是即便如此,这个参数也不能设为 0,因为你不知道后续是否还会创建线程。 
<think>我们正在讨论RT-Thread中使用单向链表(rt_slist)存储命令,并添加命令到链表。用户的问题是:将回调函数添加到链表后如何执行? 回顾用户提供的代码片段: 1. 定义了一个静态的单向链表头:`static rt_slist_t cmd_list = RT_SLIST_OBJECT_INIT(cmd_list);` 2. 定义了一个命令结构体`rt_console_cmd`,其中包含命令名、描述和回调函数。 3. 函数`console_cmd_add`用于将命令添加到链表中。 现在,用户想知道:将回调函数(命令处理函数)添加到链表后,如何执行这些回调函数? 在RT-Thread中,命令通常是通过控制台输入的。因此,执行命令的流程一般是: 1. 解析控制台输入的命令字符串,拆分成命令名和参数(argc, argv)。 2. 在命令链表中查找匹配的命令名。 3. 如果找到,则调用该命令节点中的回调函数(即`cmd->func(argc, argv)`)。 因此,我们需要一个命令解析和执行函数,通常称为`console_cmd_exec`(或类似名称)。这个函数负责上述流程。 以下是实现命令执行的大致步骤: 步骤1:实现命令解析函数 - 输入:一个字符串(例如:"led on") - 输出:参数个数(argc)和参数数组(argv),例如: argc = 2 argv[0] = "led", argv[1] = "on" 步骤2:实现命令查找和执行函数 - 遍历命令链表`cmd_list`,查找命令名与argv[0]匹配的节点。 - 找到后,调用该节点的回调函数:`cmd->func(argc, argv)` 注意:命令链表中存储的是`rt_console_cmd`结构体,我们通过`rt_slist_for_each`遍历链表,然后通过`rt_slist_entry`获取结构体实例。 示例代码: ```c // 命令执行函数 int console_cmd_exec(char *cmd_line) { int argc; char *argv[RT_CONSOLE_ARG_MAX]; // 假设最大参数个数 // 步骤1: 解析命令行为参数数组 argc = console_parse_cmd(cmd_line, argv); if (argc == 0) { return -1; // 没有命令 } // 步骤2: 在命令链表中查找命令 rt_slist_t *node; struct rt_console_cmd *cmd; rt_enter_critical(); rt_slist_for_each(node, &cmd_list) { cmd = rt_slist_entry(node, struct rt_console_cmd, list); if (rt_strcmp(cmd->name, argv[0]) == 0) { // 找到命令,执行回调函数 rt_exit_critical(); return cmd->func(argc, argv); } } rt_exit_critical(); // 没有找到命令 rt_kprintf("Unknown command: %s\n", argv[0]); return -1; } ``` 其中,`console_parse_cmd`是一个命令解析函数,用于将输入字符串拆分成参数数组。RT-Thread通常提供了`finsh_parse`(在finsh组件中)或类似函数,但这里我们假设自己实现一个简单的版本。 简单命令解析函数示例(按空格分割): ```c #define RT_CONSOLE_ARG_MAX 8 // 最大参数个数 #define RT_CONSOLE_CMD_SIZE 80 // 命令行最大长度 int console_parse_cmd(char *cmd, char **argv) { int argc = 0; char *ptr; // 跳过开头的空格 while (*cmd == ' ') cmd++; ptr = cmd; while (*ptr) { if (*ptr == ' ') { *ptr = '\0'; // 将空格替换为字符串结束符 ptr++; // 跳过连续空格 while (*ptr == ' ') ptr++; if (*ptr != '\0') { argv[argc] = ptr; argc++; } } else { ptr++; } } // 第一个参数就是命令名 if (cmd[0] != '\0') { argv[argc] = cmd; argc++; } return argc; } ``` 注意:上面的解析函数是一个简化版本,实际应用中可能需要处理引号、转义等。 综上所述,将命令添加到链表后,我们需要通过一个命令执行函数来解析用户输入,查找命令并执行回调函数。 另外,在RT-Thread中,通常有一个控制台线程(或使用finsh组件)不断读取用户输入,然后调用`console_cmd_exec`执行命令。 例如,一个简单的控制台线程: ```c static void console_thread_entry(void *parameter) { char cmd[RT_CONSOLE_CMD_SIZE]; while (1) { rt_kprintf("> "); rt_memset(cmd, 0, sizeof(cmd)); rt_device_read(console_dev, 0, cmd, sizeof(cmd)); // 假设console_dev是控制台设备 console_cmd_exec(cmd); } } ``` 这样,当用户输入命令并按下回车后,控制台线程读取输入,然后调用`console_cmd_exec`执行命令。 总结: 1. 添加命令:使用`console_cmd_add`将命令结构体添加到链表。 2. 执行命令:用户输入命令字符串,解析后查找链表节点,调用节点的回调函数。 需要注意的是,我们在遍历链表时使用了临界区保护(rt_enter_critical和rt_exit_critical),这是因为链表可能在中断或另一个线程中被修改(比如添加命令)。如果系统支持多线程且链表可能被多个线程访问,建议使用互斥锁(rt_mutex_t)来保护链表操作。 最后,请确保在添加命令时正确初始化了链表节点(`rt_slist_init`),并且添加操作是线程安全的。 希望这能解答您的疑问。 §§相关问题§§ 1. 在RT-Thread中如何实现一个简单的命令行解析器? 2. 如何保护命令链表免受多线程并发访问的影响? 3. 如何实现命令的自动补全和帮助功能? </think>### RT-Thread中执行链表中的回调函数RT-Thread中,通过`rt_slist_append`将回调函数添加到链表后,需要通过以下步骤执行这些回调函数--- ### 1. **遍历链表获取回调函数** 使用单向链表操作API遍历链表,获取存储回调函数的结构体: ```c /* 定义链表头 */ static rt_slist_t cmd_list = RT_SLIST_OBJECT_INIT(cmd_list); /* 定义命令结构体 */ struct rt_console_cmd { const char *name; int (*func)(int argc, char **argv); // 回调函数指针 rt_slist_t list; }; /* 遍历执行回调函数 */ void execute_commands(void) { rt_slist_t *node; struct rt_console_cmd *cmd; // 获取链表首节点 rt_slist_for_each(node, &cmd_list) { // 通过链表节点获取包含它的结构体 cmd = rt_slist_entry(node, struct rt_console_cmd, list); // 执行回调函数(假设传递空参数) if (cmd->func) { cmd->func(0, RT_NULL); } } } ``` 关键API说明: - `rt_slist_for_each()`:遍历链表宏[^1] - `rt_slist_entry()`:通过链表节点获取父结构体(计算偏移量) - `cmd->func()`:执行回调函数 --- ### 2. **触发执行时机** 根据实际场景选择触发方式: #### (1) **事件驱动执行** ```c /* 定义事件标志 */ static rt_event_t cmd_event; void command_thread_entry(void *param) { while (1) { // 等待执行命令事件 rt_event_recv(cmd_event, 0x01, RT_EVENT_FLAG_OR, RT_WAITING_FOREVER, RT_NULL); execute_commands(); // 执行所有回调 } } // 在需要执行命令的地方触发 void trigger_command_exec(void) { rt_event_send(cmd_event, 0x01); } ``` #### (2) **定时轮询执行** ```c static rt_timer_t cmd_timer; void timer_callback(void *param) { execute_commands(); // 定期执行 } void init_timer(void) { // 创建1秒周期的定时器 cmd_timer = rt_timer_create("cmd_tmr", timer_callback, RT_NULL, RT_TICK_PER_SECOND, RT_TIMER_FLAG_PERIODIC); rt_timer_start(cmd_timer); } ``` #### (3) **控制台命令触发** ```c // 注册系统命令 MSH_CMD_EXEC(exec_all, "execute all commands") { execute_commands(); return 0; } ``` 在控制台输入`exec_all`即可执行所有回调[^2] --- ### 3. **关键注意事项** 1. **线程安全** ```c void execute_commands(void) { rt_enter_critical(); // 进入临界区 /* 遍历执行代码 */ rt_exit_critical(); // 退出临界区 } ``` 2. **参数传递** 若需传递参数,可修改回调执行: ```c cmd->func(argc, argv); // 传递实际参数 ``` 3. **错误处理** ```c int ret = cmd->func(0, RT_NULL); if (ret != RT_EOK) { rt_kprintf("Command %s failed: %d\n", cmd->name, ret); } ``` --- ### 执行流程示意图 ```mermaid graph LR A[添加命令到链表] --> B{触发条件} B -->|事件触发| C[执行所有回调] B -->|定时触发| C B -->|命令触发| C C --> D[遍历链表] D --> E[获取回调函数] E --> F[执行函数] ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值