嵌入式 linux性能问题

这篇文章主要主要记录下我对 linux 开发板的一些性能工具的使用和各问题定位的学习和应用, 分4大块,基础知识,内存泄露的定位,cpu 性能的定位, 还有 coredump 文件分析。 主要是内存泄露的定位。不过,我觉得在定位这些问题首先应该对相关的代码有一定了解,因为对代码了解可以帮助我们更快的定位问题。

一. 基础知识


1.1 Linux 内核的 OOM 机制

Linux内核内存管理使用 OOM killer(Out-Of-Memory killer)机制,在系统内存不足时,选择性杀死一些进程以释放内存,以使系统继续运行。内核会通过特定的算法给每个进程计算一个分数来决定杀哪个进程,每个进程的 oom 分数可以 /proc/PID/oom_score 中找到。
防止重要的系统进程触发(OOM)机制而被杀死:可以设置参数 /proc/PID/oom_adj 为-17,可临时关闭 linux 内核的 OOM 机制。

1.2 glibc 内存管理
  • 理解 glibc malloc

    glibc 内存管理机制:为每个程序的每个线程维护一块内存,称为 arena,随着线程的增加会动态的增加 arena 的数量,arena 申请不回收,并且每次申请大块的内存,自己管理,进程释放并不会立马还给系统而是自己管理,只有当堆顶的内存大于 M_TRIM_THRESHOLD 的值 glibc 才会真正的还给系统。

  • 建议

  1. 对长期驻留的程序,并且会存在一瞬间猛增很多个线程的程序会出现内存爆增的现象,建议使用内存池的方式管理内存
  2. 建议后申请的内存尽量先释放,利于堆顶内存的合并释放,也会相应的会减小内存碎片
  3. glibc 的默认阈值是根据类似 pc 这样的设备设置的,并不适用于小内存的开发板,我们需要根据自己内存的大小和进程的线程数设置合适的阈值。
1.3 linux 的 proc 目录详解

https://www.cnblogs.com/DswCnblog/p/5780389.html

1.4 基础工具的使用

linux 常用的基础工具有 nm,readlf,file,
gdb,gdbserver,strace,pstrace

  • gdbserver的使用
1. 在设备端运行 gdbserver,并 attach 到调试进程(ip是设备ip)
  
gdbserver IP:PORT --attach  ${pid} 


2. 在 pc 端运行 gdb 

target remote IP:PORT
file+测试进程
set sysroot+带符号表的动态库
3. 进行 gdb 调试
  • strace 的使用
查看进程或者线程当前的系统调用         

strace -p  ${pid}/${tid}

1.5 openwrt使用工具链编译各工具的基本方法
1.指定具体开发板使用的编译工具链

export PATH=$PATH:/home/hang/work/test/thub/openwrt/staging_dir/toolchain-aarch64-linux-gnu/bin
export CROSS_COMPILE = aarch64-linux-gnu-
export CC = ${CROSS_COMPILE}gcc
export CPP = ${CROSS_COMPILE}cpp
export CXX=${CROSS_COMPILE}g++
export LD = ${CROSS_COMPILE}ld
export AR = ${CROSS_COMPILE}ar

2. 下载编译对应的工具

二. 内存


2.1 内存统计
  1. 查看 proc 的 status
cat /proc/${pid}/status
  • VSS - Virtual Set Size 虚拟耗用内存(包含共享库占用的内存),可以通过 /proc/${pid}/status 下的 VmSize 得到
  • RSS - Resident Set Size 实际使用物理内存(包含共享库占用的内存),可以通过 /proc/${pid}/status 下的 VmRss 得到,同时还可以得到RssAnon、RssFile、RssShmem。其中RssAnon 匿名内存代表当前进程通过 brk 或 mmap 向系统申请的物理内存(非文件映射),++在统计内存泄漏时比较有用。++
  • USS - Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)
  1. 查看 proc 的 smaps
cat /proc/${pid}/smaps
  • PSS - Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)
2.1 从系统申请和释放内存

申请

  • mmap :申请虚拟内存地址空间,当实际读写时,由page_fault中断将空闲的物理内存页映射到虚拟内存地址。

  • brk : 边界下移时申请内存。

释放

  • munmap:释放虚拟地址空间,同时释放物理内存

  • brk: 边界上移时释放内存

  • madvise(…, MADV_DONTNEED):仅释放物理内存,同时适用于mmap或brk申请的内存

2.3 内存问题侦测工具
【1】. gperftools
  • 优点:无需修改源码,额外的内存开销小,tcmalloc 性能优于 ptmalloc,可以用来优化进程内存
  • 缺点:需要设置环境变量 LD_PRELOAD 来启用侦测,同时需要设置其他环境变量开启使用不同功能,不可以同时使用 heap check 和 heap profile。
  • 原理:基本原理是 proflier 对运行着的程序定时采样,最后根据每次记录的堆栈频度导出采样信息。网上有人这样解释到:相当于用 gdb attach 一个正在运行的进程,然后每隔一段时间中断程序打印堆栈,当然耗时最多的调用最频繁的堆栈是最常被打印出来的。大致的实现就是注册一个定时触发的信号(SIGPROF)处理函数,在此函数中获取当前堆栈信息,通过 hash 算法以此做 hash 表的 key,放入样本统计 hash table中,如果hash table 中已经有同样的堆栈 key 了,value 就加1,这样当采样结束,就把 hash table 的统计信息导出到文件供 pprof 程序解析,从而得到真正直观的 profile 信息。
  • 官方文档:https://gperftools.github.io/gperftools/

示例

heap check :

LD_PRELOAD=/tmp/libtcmalloc.so.4.5.3 env HEAPCHECK=as-is HEAP_CHECK_IGNORE_THREAD_LIVE=false   HEAP_CHECK_DUMP_DIRECTORY=/data/ /usr/bin/iotjs /usr/yoda/services/multimediad/index.js

heap profile :

1.
//注:Dump heap profiling information whenever the high-water memory usage mark increases by the specified number of bytes.

export HEAP_PROFILE_INUSE_INTERVAL=3145728

//注:Dump heap profiling information whenever the specified signal is sent to the process.

export HEAPPROFILESIGNAL=12

//注:Profile mmap, mremap and sbrk calls in addition to malloc, calloc, realloc, and new. NOTE: this causes the profiler to profile calls internal to tcmalloc, since tcmalloc and friends use mmap and sbrk internally for allocations. One partial solution is to filter these allocations out when running pprof, with something like pprof --ignore='DoAllocWithArena|SbrkSysAllocator::Alloc|MmapSysAllocator::Alloc.

export HEAP_PROFILE_MMAP=1

export HEAPPROFILE=/data/tcmalloc.log
LD_PRELOAD=/data/lib/libtcmalloc.so.4.5.3

2. 重启程序

产生 dump 文件 ,在 pc 上使用 pprof 做 diff

pprof --text --lib_prefix=pc上带符号表的库   --base=tcmalloc.log.0001.heap ./bin/test(注:pc上对应的dump 进程) tcmalloc.log.0002.heap --stack
【2】. heaptrack
  • 优点:无需修改源码,内存开销小。原理类似 gperftools,但提供比 gperftools 更易用的脚本 heaptrack,无需手动设置环境变量。可以同时开启 heap check和heap profile。有GUI环境展示侦测结果。
  • 缺点:编译依赖 boost、elfutils、libunwind,GUI分析工具依赖 qt、kde framework,GUI 只在 linux 系统上安装成功了,不能在 mac 上安装
【3】. valgrind 的 memcheck
  • 优点:无需修改源码。使用内置虚拟机运行被侦测程序,理论上可以侦测出所有内存问题,例如读写缓冲区越界等。除了默认的 memcheck 以外,还有 cachegrind、callgrind、helgrind、drd、massif、dhat、sgcheck、bbv等工具。
  • 缺点:CPU执行效率降低数十倍,内存消耗巨大,256M内存以下的设备基本无法运行。被侦测程序必须拥有符号表和调试符号,即不能被strip。
  • 原理:
    Memcheck 能够检测出内存问题,关键在于其建立了两个全局表。
    Valid-Value 表:
    对于进程的整个地址空间中的每一个字节(byte),都有与之对应的 8 个 bits;对于 CPU 的每个寄存器,也有一个与之对应的 bit 向量。这些 bits 负责记录该字节或者寄存器值是否具有有效的、已初始化的值。
    Valid-Address 表:
    对于进程整个空间中的每一个字节(byte),还有与之对应的 1 个 bit,负责记录该地址是否能够被读写。
    当要读写内存中的某个字节时,首先检查这个字节对应的A bit。如果该A bit显示该位置是无效位置,memcheck 则会报告读写错误;内核(core)类似于一个虚拟的CPU环境,这样当内存中的某个字节被加载到真实的CPU时,该字节对应的V bit也会被加载到虚拟的 CPU 环境中。一旦寄存器中的值,被用来产生内存地址,或者该值能够影响程序输出,则 memcheck 会检查对应的V bits,如果该值尚未初始化,则会报告使用未初始化内存错误。

示例

VALGRIND_LIB=/data//lib/valgrind/ valgrind --log-file=/data/val.log --error-limit=no --leak-check=full --show-leak-kinds=all /data/rplayer_demo https://rokidstorycdn.rokid.com/story/track/9677547/wKgDZ1Y4GyqTMGYYAC-S81q-PzI118.mp3

注意

  • 我们的设备的内存很小,很多带符号表的文件无法直接推进去,可以推到 data 目录下,然后路径加载
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$pwd
  • 测试结论依赖程序正常结束
【4】. 其他工具
  • kchat 被侦测程序必须拥有符号表和调试符号,没有实际使用过,大家有兴趣可以自己尝试
  • mtrace:ptmalloc 自带,额外的内存开销小,但需要在源码中调用 mtrace() 和 muntrace(),同时需要设置环境变 量MALLOC_TRACE 用于输出侦测结果

三. CPU高的定位方法


一个应用占用 CPU 很高,除了确实是计算密集型应用之外,通常原因都是出现了死循环。

1.查看进程中的哪个线程cpu的占用率高
  
  top -Hp ${pid} 
2. 如果对代码很熟悉,可以直接使用 strace 查看线程当前的系统调用

注:tid 是通过 1 查看对高 cpu 的线程 id
strace -p  ${tid} 

否则就使用 adbserver attach 进程查看cpu高的线程的堆栈调用

四. CoreDump


4.1. gdb 调试 core 文件
1.gdb+ 程序+ core文件
2.file+程序
3. info share (查看动态库)
4. 没有动态库的话加载动态库
set solib-search-path + 库的路径
5.查看coredump 时的栈信息
bt 或者 where
4.2. 原理

https://blog.csdn.net/u014403008/article/details/54666438

4.3. 扩展

使用的是网页版的 gdb( 有兴趣可以看看网页版gdb的实现 https://www.youtube.com/watch?v=OyWaAJD6hr8

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
嵌入式Linux系统是一种运行在嵌入式设备上的精简版Linux系统。与传统的桌面或服务器Linux系统相比,嵌入式Linux系统在性能内存方面有一些特殊要求和考虑因素。 首先,性能方面。嵌入式Linux系统通常被应用在资源有限的嵌入式设备上,因此对于系统的性能要求相对较低。这主要体现在对计算能力和实时性的要求上。嵌入式设备通常需要处理实时数据,并且对于响应时间较短的任务有较的要求。因此,嵌入式Linux系统需要快速的启动时间和响应时间,以保证设备的正常运行。 其次,内存方面。嵌入式设备的内存容量通常比传统计算机要小得多。因此,嵌入式Linux系统需要尽可能地减小内存占用,以确保系统能够正常运行并满足设备的其他功能需求。为了减小内存占用,嵌入式Linux系统通常会进行精简化,剔除掉不必要的组件和功能,并采用一些优化技术,如压缩内存内存分页等。 此外,嵌入式Linux系统还需要考虑功耗和热量散发等相关因素。由于嵌入式设备通常具有电源限制和空间限制,因此系统的功耗控制至关重要。嵌入式Linux系统需要优化系统运行时的功耗消耗,并确保系统在低功耗模式下仍能保持正常运行。 综上所述,嵌入式Linux系统在性能内存方面需要有一些特殊的考虑和优化。通过降低系统的资源占用和优化系统的运行效率,嵌入式Linux系统能够在有限的资源条件下实现稳定效的运行,从而满足嵌入式设备的要求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值