嵌入式Linux环境下线程CPU消耗跟踪

本文介绍了一种在嵌入式系统中定位高CPU消耗线程的方法,包括获取线程ID及通过分析/proc/<pid>/task/<tid>/stat文件来计算各线程CPU使用率。

在比较大型的项目中,通常都会使用多线程技术,而且通常是多人合作开发,各方自测OK之后,整合在一起往往会出现一些问题,CPU使用率过高就是其中之一。如何在不熟悉所有模块代码的情况下,快速的定位到具体哪一个线程在消耗CPU,显得很有必要。在X86上,可以借助一些工具进行定位分析,但是在嵌入式系统中,工具就比较匮乏,各命令功能也比较简单,就不好定位。现介绍一种简单通用的办法:

第一步:

获取各个线程的tid,Linux环境下的线程其实就是轻量级的进程,只不过通过top\ps等常用命令一般都查不到具体的线程号tid(指在嵌入式系统中),需要在各个线程实现代码中获取线程ID。

#include <sys/syscall.h> 

pid_t gettid()
{
     return syscall(SYS_gettid);   
}
参考自: Linux打印真实pid的方法 http://blog.youkuaiyun.com/gaoxuelin/article/details/9718189


第二步:

通过线程ID获得各线程的CPU使用率。

主要是通过分析/proc/<pid>/task/<tid>/stat文件获得,pid为程序的PID,tid为程序的各个线程的ID号(就是第一步输出的线程ID),stat文件就是一些调度的基本信息,具体可参阅:Linux proc/pid/task/tid/stat文件详解 http://blog.youkuaiyun.com/ctthuangcheng/article/details/18090701

线程比较多的时候一个线程一个线程去分析该文件比较费劲,可通过脚本一次解析完成,参数为进程PID,运行成功会输出该进程的所有线程tid、用户层CPU使用、内核态CPU使用,数值越高表示消耗CPU资源越多。

#!/bin/sh
#get /proc/pid/task/tid/stat
#$1 is tid
#$14  is user cpu 
#$15 is sys cpu
echo "tid user sys"
for file in /proc/$1/task/*
do
    if test -d $file
    then
        cat $file/stat | awk -F" " '{print $1 " " $14 " " $15}'
    fi
done



<think> 在嵌入式系统中,进CPU消耗与多个因素相关,包括进本身的特性和系统环境。以下我将详细解释这些因素,并提供一个监控进CPU消耗的示例代码(使用C语言在Linux嵌入式环境)。 ### 影响进CPU消耗的因素 1. **计算密集型任务**: - 大量数学运算(如加密解密、信号处理) - 循环和条件判断密集的代码 2. **I/O操作**: - 读写文件、网络通信等I/O操作会导致进阻塞,减少CPU占用 - 非阻塞I/O或异步I/O可能增加CPU使用(轮询) 3. **进优先级和调度策略**: - 高优先级进获得更多CPU时间 - 实时调度策略(如SCHED_FIFO)可能独占CPU 4. **中断处理**: - 高频率中断会增加系统上下文切换开销 - 中断服务序(ISR)的执行时间 5. **多线程/多进**: - 线程/进间通信(IPC)开销 - 锁竞争导致的等待 6. **系统负载**: - 高负载系统可能因资源竞争导致进等待 7. **编译器优化**: - 优化级别影响代码执行效率 8. **硬件性能**: - CPU主频、缓存大小、内存速度等 ### 监控进CPU消耗的示例代码 以下是一个在Linux嵌入式系统中监控特定进CPU使用率的C序: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> // 获取进CPU时间(用户态+内核态) unsigned long long get_process_cpu_time(int pid) { char stat_path[256]; sprintf(stat_path, "/proc/%d/stat", pid); FILE *fp = fopen(stat_path, "r"); if (!fp) { perror("fopen"); return 0; } // 读取stat文件内容 char buffer[1024]; if (fgets(buffer, sizeof(buffer), fp) == NULL) { fclose(fp); return 0; } fclose(fp); // 解析stat文件(第14项utime, 15项stime, 16项cutime, 17项cstime) unsigned long long utime, stime, cutime, cstime; char *token = strtok(buffer, " "); for (int i = 1; i <= 17; i++) { token = strtok(NULL, " "); if (i == 14) utime = strtoull(token, NULL, 10); if (i == 15) stime = strtoull(token, NULL, 10); if (i == 16) cutime = strtoull(token, NULL, 10); if (i == 17) cstime = strtoull(token, NULL, 10); } // 返回进CPU时间(包括子进) return utime + stime + cutime + cstime; } // 获取系统总CPU时间 unsigned long long get_total_cpu_time() { FILE *fp = fopen("/proc/stat", "r"); if (!fp) { perror("fopen"); return 0; } char buffer[1024]; fgets(buffer, sizeof(buffer), fp); // 读取第一行(cpu总行) fclose(fp); unsigned long long user, nice, system, idle, iowait, irq, softirq; sscanf(buffer, "cpu %llu %llu %llu %llu %llu %llu %llu", &user, &nice, &system, &idle, &iowait, &irq, &softirq); return user + nice + system + idle + iowait + irq + softirq; } // 计算进CPU使用率 float calculate_cpu_usage(int pid, float interval_sec) { unsigned long long start_proc_time = get_process_cpu_time(pid); unsigned long long start_total_time = get_total_cpu_time(); usleep((useconds_t)(interval_sec * 1000000)); // 等待一段时间 unsigned long long end_proc_time = get_process_cpu_time(pid); unsigned long long end_total_time = get_total_cpu_time(); if (end_proc_time < start_proc_time || end_total_time <= start_total_time) { return 0.0; // 无效数据 } // 计算差值 unsigned long long proc_diff = end_proc_time - start_proc_time; unsigned long long total_diff = end_total_time - start_total_time; return (proc_diff * 100.0) / total_diff; // 百分比 } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage: %s <pid>\n", argv[0]); return 1; } int pid = atoi(argv[1]); while (1) { float cpu_usage = calculate_cpu_usage(pid, 1.0); // 每秒采样 printf("PID %d CPU Usage: %.2f%%\n", pid, cpu_usage); fflush(stdout); } return 0; } ``` ### 代码解释 1. **`get_process_cpu_time`函数**: - 读取`/proc/[pid]/stat`文件获取进CPU时间 - 包含用户态(utime)、内核态(stime)及子进时间(cutime/cstime) - 返回单位为jiffies(系统时钟滴答) 2. **`get_total_cpu_time`函数**: - 读取`/proc/stat`的第一行(系统总CPU时间) - 包含所有状态(用户、空闲、系统等)的累计时间 3. **`calculate_cpu_usage`函数**: - 在时间间隔前后分别采样进和系统CPU时间 - 计算差值后得出进占用系统总CPU时间的百分比 - 公式:`(进时间差 / 系统总时间差) * 100` 4. **主序**: - 每秒计算并输出指定进CPU使用率 - 通过命令行参数传递目标进PID ### 编译与运行 ```bash # 交叉编译示例(ARM嵌入式系统) arm-linux-gnueabihf-gcc -o cpu_monitor cpu_monitor.c # 运行 ./cpu_monitor <target_pid> ``` ### 注意事项 1. **时间精度**: - 嵌入式系统时钟滴答频率(`HZ`)通常为100或250 - 高精度测量需要内核配置`CONFIG_HIGH_RES_TIMERS` 2. **资源消耗**: - 频繁读取`/proc`文件系统会增加系统负载 - 实际部署建议使用内核模块或`perf`工具 3. **多核系统**: - 上述代码统计的是所有CPU核心的总和使用率 - 多核系统中进可能超过100%(如200%表示占满两个核心) ### 优化建议 - **降低采样频率**:减少对系统性能影响 - **使用事件驱动**:通过epoll监控`/proc`文件变化 - **内核模块**:直接读取内核数据结构避免文件操作 ### §§相关问题§§: 1. 如何实时监控嵌入式系统中所有进CPU使用率? 2. 在嵌入式Linux中,如何限制特定进CPU使用率? 3. 如何分析嵌入式系统CPU使用率过高的原因? 4. 多核嵌入式系统中如何统计每个核心的CPU使用率? </think> ### 嵌入式系统中进CPU消耗的影响因素 在嵌入式系统中,进CPU消耗由多种因素共同决定,这些因素可以分为软件层面和硬件层面。以下是关键影响因素及详细解释: #### 1. 软件层面因素 ```c // 示例:计算密集型任务(高CPU消耗) void compute_intensive_task() { for (int i = 0; i < 1000000; i++) { // 复杂的数学运算 double result = sin(i) * cos(i) / tan(i); } } // 示例:I/O密集型任务(低CPU消耗) void io_intensive_task() { FILE *file = fopen("data.txt", "r"); while (!feof(file)) { // 文件读取(大部分时间等待I/O) char buffer[1024]; fread(buffer, 1, sizeof(buffer), file); } fclose(file); } ``` **关键因素:** - **算法复杂度**:O(n²)算法比O(n)消耗更多CPU - **任务类型**: - 计算密集型:持续占用CPU - I/O密集型:频繁等待外部设备 - **代码优化**: - 编译器优化级别(-O0 vs -O3) - 循环展开、内联函数等优化技术 - **系统调用频率**: - 频繁的系统调用导致用户态/内核态切换 - **多线程/多进**: - 线程/进间通信开销 - 锁竞争导致的等待时间 #### 2. 硬件层面因素 ```c // 示例:硬件差异对性能的影响 void hardware_dependent_task() { // 浮点运算(有无FPU差异巨大) float a = 3.14159, b = 2.71828; for (int i = 0; i < 1000000; i++) { a = a * b - b / a; } } ``` **关键因素:** - **CPU架构**: - ARM Cortex-M vs RISC-V - 有无浮点运算单元(FPU) - **时钟频率**: - 100MHz vs 1GHz处理器 - **缓存大小**: - L1/L2缓存命中率对性能影响显著 - **内存带宽**: - 内存访问速度瓶颈 - **外设接口**: - DMA使用减少CPU干预 #### 3. 系统层面因素 ```c // 示例:不同优先级任务的影响 void high_priority_task() { while (1) { // 实时任务占用大量CPU process_real_time_data(); } } void low_priority_task() { while (1) { // 后台任务可能被抢占 log_system_status(); } } ``` **关键因素:** - **调度策略**: - FIFO vs 轮询调度 - 实时优先级设置 - **中断频率**: - 高中断率导致频繁上下文切换 - **系统负载**: - 并发运行的进数量 - **电源管理**: - DVFS(动态电压频率调整) - **资源竞争**: - 内存、总线等资源的争用 ### CPU消耗监控示例代码 以下是在嵌入式Linux系统中监控进CPU使用率的C代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> float get_cpu_usage(int pid) { char stat_path[256]; sprintf(stat_path, "/proc/%d/stat", pid); // 读取进统计信息 FILE *fp = fopen(stat_path, "r"); if (!fp) return -1; char buffer[1024]; fgets(buffer, sizeof(buffer), fp); fclose(fp); // 解析CPU时间(第14-17字段) char *token = strtok(buffer, " "); unsigned long utime = 0, stime = 0, cutime = 0, cstime = 0; for (int i = 1; i <= 17; i++) { token = strtok(NULL, " "); if (i == 14) utime = strtoul(token, NULL, 10); if (i == 15) stime = strtoul(token, NULL, 10); if (i == 16) cutime = strtoul(token, NULL, 10); if (i == 17) cstime = strtoul(token, NULL, 10); } // 计算总CPU时间 unsigned long total_time = utime + stime + cutime + cstime; // 获取系统总时间 static unsigned long last_total_time = 0; static unsigned long last_sys_time = 0; FILE *stat_fp = fopen("/proc/stat", "r"); fgets(buffer, sizeof(buffer), stat_fp); fclose(stat_fp); unsigned long user, nice, sys, idle; sscanf(buffer, "cpu %lu %lu %lu %lu", &user, &nice, &sys, &idle); unsigned long sys_time = user + nice + sys + idle; // 计算使用率 if (last_total_time == 0) { last_total_time = total_time; last_sys_time = sys_time; return 0; } float usage = 100.0 * (total_time - last_total_time) / (sys_time - last_sys_time); last_total_time = total_time; last_sys_time = sys_time; return usage; } int main() { int pid = getpid(); // 监控当前进 while (1) { float usage = get_cpu_usage(pid); printf("CPU Usage: %.2f%%\n", usage); sleep(1); } return 0; } ``` ### 优化CPU使用的技术 1. **算法优化**: - 选择时间/空间复杂度更优的算法 - 使用查表法替代实时计算 2. **硬件加速**: ```c // 使用DMA代替CPU数据搬运 void dma_transfer(void *src, void *dest, size_t size) { DMA_Config(src, dest, size); DMA_Start(); while (!DMA_Complete()); // 等待完成 } ``` 3. **功耗管理**: - 空闲时进入低功耗模式 - 动态调整CPU频率 4. **中断优化**: - 使用零拷贝技术 - 合并高频中断 5. **任务划分**: - 将任务拆分到不同优先级 - 使用RTOS的任务调度 ### 典型嵌入式系统CPU消耗分布 | 任务类型 | CPU使用占比 | 特征 | |---------|------------|------| | 控制循环 | 30-60% | 实时性要求高 | | 通信协议 | 10-30% | 突发性负载 | | 数据处理 | 5-20% | 计算密集型 | | 系统空闲 | 0-100% | 低功耗模式 | ###
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值