Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
list_thread
ps
原创征文
list_thread / ps 命令查看线程状态各参数含义详解
5.00
发布于 2022-07-13 11:09:50 浏览:7366
订阅该版
[toc] ------------- 本文结合源码对 `ps / list_thread` 命令查看线程状态获得的各参数的含义进行解释。 # 1 ps 和 list_thread 命令的关系 查看 `ps` 命令的源码,如下所示。从源码中可以看到 `ps` 命令执行后调用了 `list_thread()` 函数,因此这两个命令执行的效果是一致的。 ```c // 文件 rt-thread/components/finsh/msh.c int cmd_ps(int argc, char **argv) { extern long list_thread(void); extern int list_module(void); #ifdef RT_USING_MODULE if ((argc == 2) && (strcmp(argv[1], "-m") == 0)) list_module(); else #endif list_thread(); // 调用了 list_thread() 函数 return 0; } MSH_CMD_EXPORT_ALIAS(cmd_ps, ps, List threads in the system.); ``` # 2 ps / list_thread 命令结果含义解析 本文以 led 线程为例进行分析,创建的 led 处理线程代码如下所示,其中线程的任务栈大小为1024Bytes,优先级为10,时间片大小为20tick。 ```c #include
#include "drv_common.h" #include "rtdevice.h" #define DBG_TAG "main" #define DBG_LVL DBG_LOG #include
#define LED_RTTPIN (GET_PIN(A, 8)) rt_thread_t tid1 = RT_NULL; // 线程控制块句柄 static void led_thread_entry(void *param) { while (1) { rt_pin_write(LED_RTTPIN, PIN_LOW); rt_thread_mdelay(500); rt_pin_write(LED_RTTPIN, PIN_HIGH); rt_thread_mdelay(500); } } void led_init(void) { rt_pin_mode(LED_RTTPIN, PIN_MODE_OUTPUT); // LED引脚初始化 // 动态创建线程 tid1 = rt_thread_create("led_thread", led_thread_entry, RT_NULL, 1024, 10, 20); if (tid1 != RT_NULL) { rt_thread_startup(tid1); // 启动线程 } } ``` 在控制台输入 `ps` 命令查看线程状态,结果如下所示。 ``` msh >ps thread pri status sp stack size max used left tick error ---------- --- ------- ---------- ---------- ------ ---------- --- led_thread 10 suspend 0x000000ac 0x00000400 16% 0x00000014 000 tshell 20 running 0x000000cc 0x00001000 15% 0x00000005 000 tidle0 31 ready 0x00000058 0x00000100 43% 0x00000008 000 timer 4 suspend 0x00000078 0x00000200 23% 0x00000009 000 main 10 suspend 0x000000b8 0x00000800 14% 0x0000000f 000 msh > ``` 每一列的含义如下: | 参数 | 含义 | | ---------- | ------------------------------------------------------------ | | thread | 线程的名字(可以通过修改内核对象名称的最大长度来设置显示的线程名字的长度) | | pri | 线程的优先级,创建线程时指定 | | status | 线程的状态(初始状态、就绪状态、运行状态、 挂起状态、关闭状态) | | sp | 线程指针 thread->sp 距离线程栈栈底的距离 | | stack size | 线程栈的大小,创建线程时指定 | | max used | 线程栈的最大使用率 | | left tick | 线程剩余的时间片大小 | | error | 线程错误码 | 其中 [线程错误码](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/thread/thread?id=%e7%ba%bf%e7%a8%8b%e9%94%99%e8%af%af%e7%a0%81) 含义如下: ```c #define RT_EOK 0 // 无错误 #define RT_ERROR 1 // 普通错误 #define RT_ETIMEOUT 2 // 超时错误 #define RT_EFULL 3 // 资源已满 #define RT_EEMPTY 4 // 无资源 #define RT_ENOMEM 5 // 无内存 #define RT_ENOSYS 6 // 系统不支持 #define RT_EBUSY 7 // 系统忙 #define RT_EIO 8 // IO 错误 #define RT_EINTR 9 // 中断系统调用 #define RT_EINVAL 10 // 非法参数 ``` # 3 对应源码分析 ## 3.1 线程名字和优先级的打印 本文以动态创建线程为例进行讲述。在使用函数 `rt_thread_create()` 动态创建线程时会传入线程的名字和优先级, 线程的名字在函数 `rt_object_allocate()` 里面进行初始化,线程的优先级在函数 `_thread_init()` 里面进行初始化,代码如下。为什么有一个初始优先级一个当前优先级呢?是因为在运行过程中,使用信号量时可能会发生[优先级翻转](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/ipc1/ipc1?id=%e4%ba%92%e6%96%a5%e9%87%8f%e5%b7%a5%e4%bd%9c%e6%9c%ba%e5%88%b6)的问题,解决办法是临时将相应的低优先级线程的优先级提高,此时修改线程的当前优先级,在函数 `rt_thread_control()` 进行操作。 ```c /* priority init */ RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX); thread->init_priority = priority; thread->current_priority = priority; ``` 线程名字和优先级打印的代码如下。其中 maxlen 的大小也是宏定义 RT_NAME_MAX,该宏在 RT-Thread Settings 中可以通过设置内核对象名称的最大长度进行修改,从而控制打印出的线程名字的长度,另外打印的线程的优先级也是线程的当前的优先级。 ```c // 文件 rt-thread/components/finsh/cmd.c 中的函数 list_thread() rt_kprintf("%-*.*s %3d ", maxlen, RT_NAME_MAX, thread->name, thread->current_priority); ``` 注:在上述代码中的 `%-*.*s` 格式控制符中的 `-` 表示打印时左对齐,右边填空格,默认是按照右对齐进行打印;`.`前面的`*` 定义了输出的总宽度,用 `maxlen`进行限制;`.`后面的`*` 定义了输出字符的个数,用 `RT_NAME_MAX` 进行限制,若实际位数大于所定义的精度数,则截去超过的部分。 ## 3.2 线程状态的打印 线程运行的过程中,同一时间内只允许一个线程在处理器中运行,从运行的过程上划分,线程有多种不同的运行状态,如初始状态、挂起状态、就绪状态等。在 RT-Thread 中,线程包含[五种状态](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/thread/thread?id=%e7%ba%bf%e7%a8%8b%e7%8a%b6%e6%80%81),操作系统会自动根据它运行的情况来动态调整它的状态。 | 状态 | 描述 | | ------------------- | ------------------------------------------------------------ | | 初始状态(init) | 当线程刚开始创建还没开始运行时就处于初始状态;在初始状态下,线程不参与调度。此状态在 RT-Thread 中的宏定义为 RT_THREAD_INIT | | 就绪状态(ready) | 在就绪状态下,线程按照优先级排队,等待被执行;一旦当前线程运行完毕让出处理器,操作系统会马上寻找最高优先级的就绪态线程运行。此状态在 RT-Thread 中的宏定义为 RT_THREAD_READY | | 运行状态(running) | 线程当前正在运行。在单核系统中,只有 rt_thread_self() 函数返回的线程处于运行状态;在多核系统中,可能就不止这一个线程处于运行状态。此状态在 RT-Thread 中的宏定义为 RT_THREAD_RUNNING | | 挂起状态(suspend) | 也称阻塞态。它可能因为资源不可用而挂起等待,或线程主动延时一段时间而挂起。在挂起状态下,线程不参与调度。此状态在 RT-Thread 中的宏定义为 RT_THREAD_SUSPEND | | 关闭状态(close) | 当线程运行结束时将处于关闭状态。关闭状态的线程不参与线程的调度。此状态在 RT-Thread 中的宏定义为 RT_THREAD_CLOSE | 线程状态打印的代码如下。根据线程控制块句柄中的成员 `stat` 的不同值来打印出线程当前的状态。 ```c // 文件 rt-thread/components/finsh/cmd.c 中的函数 list_thread() stat = (thread->stat & RT_THREAD_STAT_MASK); if (stat == RT_THREAD_READY) rt_kprintf(" ready "); else if (stat == RT_THREAD_SUSPEND) rt_kprintf(" suspend"); else if (stat == RT_THREAD_INIT) rt_kprintf(" init "); else if (stat == RT_THREAD_CLOSE) rt_kprintf(" close "); else if (stat == RT_THREAD_RUNNING) rt_kprintf(" running"); ``` ## 3.3 线程栈空间信息及错误代码的打印 在线程创建和初始化时对线程栈空间的大小和线程当前的栈位置以及线程栈空间全部初始化为 `'#'` 相关代码如下。 ```c static rt_err_t _thread_init(struct rt_thread *thread, const char *name, void (*entry)(void *parameter), void *parameter, void *stack_start, rt_uint32_t stack_size, rt_uint8_t priority, rt_uint32_t tick) { /* init thread list */ rt_list_init(&(thread->tlist)); thread->entry = (void *)entry; thread->parameter = parameter; /* stack init */ thread->stack_addr = stack_start; // 线程栈的起始地址 thread->stack_size = stack_size; // 线程栈的大小,创建时指定 /* init thread stack */ rt_memset(thread->stack_addr, '#', thread->stack_size); // 将线程栈空间初始化为 '#' #ifdef ARCH_CPU_STACK_GROWS_UPWARD thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter, (void *)((char *)thread->stack_addr), (void *)_thread_exit); #else // 向下增长的栈,STM32为向下增长的栈 thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter, (rt_uint8_t *)((char *)thread->stack_addr + thread->stack_size - sizeof(rt_ubase_t)), (void *)_thread_exit); // 计算线程当前的栈位置 #endif /* ARCH_CPU_STACK_GROWS_UPWARD */ /* priority init */ RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX); thread->init_priority = priority; // 线程初始优先级的初始化 thread->current_priority = priority; // 线程当前优先级的初始化 thread->number_mask = 0; #if RT_THREAD_PRIORITY_MAX > 32 thread->number = 0; thread->high_mask = 0; #endif /* RT_THREAD_PRIORITY_MAX > 32 */ /* tick init */ thread->init_tick = tick; // 线程时间片大小的初始化,时间片大小创建时指定 thread->remaining_tick = tick; // 线程剩余时间片大小的初始化 /* error and flags */ thread->error = RT_EOK; // 线程错误码初始化 thread->stat = RT_THREAD_INIT; // 线程状态初初始化 } ``` 通过分析上面的代码,结合创建的 led_thread 线程,使用在线调试查看线程控制控制块句柄个成员的值,初始化后该线程线程栈的空间分布如下图所示。图中定义的线程栈的总的大小为 1024Bytes,线程栈的起始地址为 0x20002FEC,终止地址为 0x20003340,线程栈的当前的栈位置为 0x20003340,所以已使用的线程栈的大小为 0x200033EC - 0x20003340 = 0xAC = 172(十进制),与上面使用 `ps` 指令打印出的结果一致。 ![无标题.png](https://oss-club.rt-thread.org/uploads/20220713/7392d2a33c753cb23712f9b3f5505f0a.png "无标题.png") 线程栈空间信息及错误代码打印的相关代码如下所示,结合上图中的线程栈空间的分布图,因为在初始化时将线程栈的空间都初始化为 `'#'`,下面代码中根据线程栈空间内容是否为 `'#'` 找到了已使用线程栈空间的结束位置,并将其赋值给变量 `ptr`,所以打印出的第一个参数值得含义为当前线程栈位置距离线程栈栈底的距离。由于使用的线程栈空间不为 `'#'` ,所以即使申请过的栈空间又进行释放,在再次执行 `ps / list_thread` 命令时,变量 `ptr` 的值仍然为找到的不为字符 `'#'`的位置,所以打印出的第三个参数为在运行过程中线程栈空间的最大使用率,在实际项目中可以根据该值灵活的修改线程栈空间的大小,在设置栈空间大小时要留有一定的余量,通常控制栈空间使用率在 70%~80% 较为合适。 ```c // 文件 rt-thread/components/finsh/cmd.c 中的函数 list_thread() ptr = (rt_uint8_t *)thread->stack_addr; // 将 ptr 赋值为线程栈空间的起始地址 while (*ptr == '#')ptr ++; // 找到已使用线程栈空间的结束位置 rt_kprintf(" 0x%08x 0x%08x %02d%% 0x%08x %03d\n", thread->stack_size + ((rt_ubase_t)thread->stack_addr - (rt_ubase_t)thread->sp), // 当前线程栈位置距离线程栈栈底的距离 thread->stack_size, // 线程栈大小 (thread->stack_size - ((rt_ubase_t) ptr - (rt_ubase_t) thread->stack_addr)) * 100 / thread->stack_size, // 线程栈最大使用率 thread->remaining_tick, // 线程栈剩余时间片的大小 thread->error); // 线程错误码 ``` # 4 参考文章 1. [使用 Finsh 查看线程状态中的 sp 代表什么意思](https://club.rt-thread.org/ask/article/6bed31e16dd1925d.html) 2. [MSH ps命令中left tick怎么理解](https://club.rt-thread.org/ask/question/1ed26a04d30172ef.html) 3. [shell的ps打印,error一栏有错误](https://club.rt-thread.org/ask/question/f4810b30e049e655.html)
3
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
crystal266
嵌入式
文章
14
回答
547
被采纳
161
关注TA
发私信
相关文章
1
shell ps命令引起硬件错误
2
ps命令打印结果中error列-02是什么意思
3
shell的ps打印,error一栏有错误
4
ps指令的error是什么意思
5
ps指令线程名字显示不全
6
ps不能显示线程名字不全
7
RTT finsh ps 显示内容不全
8
使用list_thread查看不了已创建的线程
9
msh的list_thread指令显示不全的问题?
10
list_thread()打印的是历史最大使用率,还是实时的?
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
freemodbus
PWM
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
中断
编译报错
Debug
SFUD
rt_mq_消息队列_msg_queue
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
a1012112796
10
个答案
1
次被采纳
踩姑娘的小蘑菇
4
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
Ryan_CW
4
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
5
次点赞
YZRD
2
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
Woshizhapuren
1
篇文章
5
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部