性能:time、strace、ltrace、ps命令追踪特定进程CPU

在用系统级性能工具找出是哪个进程降低了系统速度之后,就需要用特定的进程性能工具来发现这个进程的行为,以便确定:

  • 确定应用程序的运行时间是花费在内核上还是在应用程序上
  • 确定应用程序有哪些库调用和系统调用,以及它们花费的时间

进程性能统计信息

要了解一个应用程序的性能,至关重要的一点就是理解它与操作系统、CPU和存储系统是怎么进行交互的。

内核时间 VS 用户时间

一个应用程序所耗时间最基本的划分是内核时间和用户时间。内核时间是消耗在linux内核上的时间,用户时间是消耗在应用程序或者库代码上的时间。

linux中,有time、ps这样的工具可以大致表明应用程序将其时间是花费在应用程序代码上还是花在了内核代码上。还有比如oprofile和strace这样的命令可以跟踪哪些内核调用是代表该进程发起的,以及每个调用完成需要多少时间。

库时间 VS 应用程序时间

任何应用程序,即使其复杂性非常低,也需要依赖系统库才能执行复杂的操作。这些库可能会导致性能问题。因此,能查看应用程序在某个库中花费了多少时间就很重要了。虽然为了解决一个问题而去修改库的源代码并不总是实用,但是可以改变应用程序代码来调用不同的库函数,或者调用更少的库函数。在库被应用程序使用时,ltrace命令和oprofile工具包提供了分析库性能的途径。linux加载器ld的内置工具帮助你确定使用多个库是否会减慢应用程序的启动时间

细分应用程序时间

当已经知道某应用程序是瓶颈后,linux提供了工具来分析这个应用程序,以找出在这个程序中,时间都花在了哪里。工具gprof和oprofile可以生成应用程序的配置文件,确定是哪些源代码花费了大量时间

工具

time

time(如同秒表一样)可以测量命令执行的时间。它测量的时间有三种类型:

  • 第一种是真正的或者经过的时间,即程序开始到结束之间的时间
  • 第二种是用户时间,即CPU代表该程序执行应用代码所花费的时间
  • 第三种是系统时间,即CPU代表该程序执行系统或内核所花费的时间

语法

与CPU性能相关的选项:

  • 作用是对application程序计时,在其完成后,在标准输出中显示它的CPU使用情况
  • -v:表示详情
time [-v] application

与CPU相关的user输出:

  • User time (seconds):CPU花费在应用程序上的秒数
  • System time (seconds):CPU代表应用程序花费在Linux内核上的秒数
  • Percent of CPU this job got:进程运行时消耗CPU的百分比
  • Elapsed (wall clock) time (h:mm:ss or m:ss):应用程序从启动到完成所经历的时间
  • Major (requiring I/O) page faults:主缺页故障的数量或者需要从磁盘读取内存页的页故障数量
  • Minor (reclaiming a frame) page faults:次缺页故障的数量或者不需要访问磁盘就可以解决的页故障
  • Swaps:进程被交换到磁盘的次数
  • Voluntary context switches:进程让出CPU(比如,进入睡眠状态)的次数
  • Involuntary context switches:进程被迫让出CPU的次数
  • Page size (bytes):系统的页面大小
  • Exit status:应用程序的退出状态

实例

$ /usr/bin/time ls
admin  corefiles  data	oceanstar
0.00user 0.00system 0:00.00elapsed 100%CPU (0avgtext+0avgdata 992maxresident)k
0inputs+0outputs (0major+317minor)pagefaults 0swaps

$ /usr/bin/time --verbose ls
admin  corefiles  data	oceanstar
	Command being timed: "ls"
	User time (seconds): 0.00
	System time (seconds): 0.00
	Percent of CPU this job got: 100%
	Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.00
	Average shared text size (kbytes): 0
	Average unshared data size (kbytes): 0
	Average stack size (kbytes): 0
	Average total size (kbytes): 0
	Maximum resident set size (kbytes): 992
	Average resident set size (kbytes): 0
	Major (requiring I/O) page faults: 0
	Minor (reclaiming a frame) page faults: 317
	Voluntary context switches: 1
	Involuntary context switches: 1
	Swaps: 0
	File system inputs: 0
	File system outputs: 0
	Socket messages sent: 0
	Socket messages received: 0
	Signals delivered: 0
	Page size (bytes): 4096
	Exit status: 0

在这里插入图片描述
另外,base shell有内置time命令,可以提供进程执行信息的子集
在这里插入图片描述

strace

  • strace是当程序执行时,追踪其发起的系统调用的工具。
  • strace可以展示准备的系统调用,它在确定应用程序是如何使用linux内核的方面是非常有用的
  • 在分析大型程序或者你完全不懂的程序时,跟踪系统调用的频率和长度是非常有价值的。通过查看strace的输出,你可以了解应用程序如何使用内核,以及它依赖于什么类型的函数

语法

下面是一些与CPU性能相关的命令行选项:

strace [-c] [-p pid] [-o file] [--help] [commandd [arg ...]]

实例

从下面可以看出read调用时间占用了20%的时间,供消耗了0.44s。它被调用了2427次,平均下来,一次调用的时间为184us。在这些调用中,有26次返回了错误
在这里插入图片描述

ltrace

ltrace与strace的概念相似,但是它跟踪的是应用程序对库的调用而不是对内核的调用。虽然ltrace主要用于提供对库调用的参数和返回值的精确跟踪,但是也可以用它来汇总每个调用所花的时间。从而既可以发现应用程序有哪些库调用,又可以发现每个调用时间是多长。

使用ltrace要小心,因为它会产生误导性结构。如果一个库函数调用了另一个函数,则花费的时间要计算两次。比如,库函数foo()调用了bar(),那么foo()的报告时间 = foo()运行的全部时间 + bar()花费的时间

语法

与cpu性能相关的命令行选项:

  • -c:使得ltrace在命令执行完后打印出所有调用的汇总
  • -s:除了库调用之外,ltrace还跟踪系统调用,该项与strace提供的功能相同
  • -p pid:跟踪给定PID的进程
  • -o file:将ltrace的输出保存到file
  • --help:显示帮助信息

如果ltrace 不带任何选项,那么将在标准错误输出上显示所有的库调用

ltrace [-c] [-p pid] [-o filename] [-S] [--help] command

输出选项:

  • % time:相对库调用所花费的总时间,该项是这个库调用所花的百分比
  • second:调用这个库所用的总秒数
  • users/call:调用这个库所花的微秒数
  • calls:调用这个库的总次数
  • function:这个库的名称

实例

运行ltrace -c xxxx

  • 库函数XSetVMProtocols、hypot、XQuertPointer分布占用了在库所花时间的19.65%、17.19%、12.06%。
  • 消耗第二多的函数hypot,其调用次数为702次,而消耗第一多的XSetVMProtocols,其调用次数仅为1次,除非我们的应用程序能够完全删去XSetVMProtocols,否则不管他需要多少时间,我们都会被这个时间所制约。因此我们最好将注意力转向hypot。
  • hypot函数的每次调用都是相对轻量级的,因此,如果我们能减少它的调用次数,就有可能加快应用程序的速度。如果这个应用程序有性能问题,那么第一个要调查的函数就是hypot
    在这里插入图片描述
    怎么调查呢?第一个我们就是要确定hypot是做什么的,方法:
    (1)通过man手册
    (2)如果不能man,调用就找出这个函数属于哪个库,然后阅读这个库的文档

但是ltrace并不会明显的显示一个函数是来自哪个地方的,因此我们必须借助工具lddobjdump

  • ldd用于显示一个动态连接的应用程序使用了哪些库
  • objdump用于在每个库中查找给定的函数
    • -T:列出库依赖或者提供的所有符号(主要是函数)

具体操作如下:
(1)第一步ldd显示这个应用程序使用的库
在这里插入图片描述
(2)第二步通过objdump去每个库中查找hypot符号

在这里插入图片描述
(3)第三步 【查看应用程序源代码】找出hypot在哪里被调用,如果可能的话,减少其调用的次数;或者尝试优化hypot的源代码

ps(进程状态)

  • ps可以用来跟踪运行进程
  • 它给出正在运行进程的详细的静态和动态统计信息。
    • 静态信息包括命令名和PID
    • 动态信息包括内存和CPU的当前使用情况

语法

与CPU性能最相关的选项:

ps [-o etime, time, pcpu, command] [-u user] [-u user] [PID]
  • -o xxx:该项允许你明确规定想要跟踪的进程通信信息。不同的统计项由一个没有空格的、用逗号分隔的列表指定
    • etime:统计经过时间,也就是从程序开始执行起耗费的总时间
    • time:统计CPU时间,也就是进程运行于CPU所花费的系统时间+用户时间
    • pcpu:统计进程当前消耗的CPU的百分比
    • command:命令名
      • -A:显示所有进程的统计信息
      • -u user:显示指定有效时间ID的所有进程的统计信息
      • -U user:显示指定用户ID的所有进程的统计信息

实例

下面显示中:

  • 使用了88%的CPU
  • 运行了6秒,但是消耗的CPU时间只有5秒
    在这里插入图片描述

下面显示中,我们没有调查具体进行的CPU性能,而是查看了特定用户运行的全部进程。这可以知道特定用户消耗的资源量的信息:netdump用户只运行了bash和top,其中bash不占用任何CPU,而top只占用了0.5%的CPU

在这里插入图片描述
与time不同,ps使得我们能监控当前正在运行的进程的信息。对于运行时间较长的工作,我们可以使用ps定期检测进程的状态(而不是在程序已经执行完毕后监测)

ld.so(动态加载器—这个暂时没什么用)

  • 执行一个动态连接应用程序时,首先运行的是linux加载器ld.so。
  • ld.so加载该应用程序所有的库,并将它使用的符号与库提供的函数关联起来。因为不同的库最初被链接到内存中的不同位置,这些位置还可能是重叠的,链接器需要对所有的符号进行排序,以确保每个符号都位于内存中的不同位置。 一个符号从一个虚拟地址移动到另一个虚拟地址,就被叫做重定位
  • 加载器做这项工作是需要时间的,如果它完全不用去做那就更好了
  • 预连接应用程序的目标就是通过重排整个系统的系统库来完整这项工作,以保证它们不会相互重叠。需要进行大量重定位的应用程序可能没有被预链接过。
  • linux加载器的运行不需要用户进行任何干预,只需要执行一个动态程序即可,它是自动运行的。虽然加载器的执行对用户来说是隐藏的,但是它的执行仍然要花时间,这就有可能会延长应用程序的启动时间。当你要了解加载器的统计信息时,加载器展示的是其工作量,以便你能弄清楚它是否是瓶颈

gprof

剖析linux应用程序的一种强有力的方法是使用gprof分析命令。

  • gprof可以展示应用程序的调用图,应该采样该应用程序的时间都花在了哪里。
  • gprof的工作方式是,首先编译你的应用程序,然后运行该应用程序生成一个采样文件。
  • gprof是非常强大的,但是它需要应用源程序,并且增加了编译开销。尽管gprof可以确定函数被调用的精确次数,以及函数所花的大致时间,但是其编译将有可能改变应用程序的时间特性,延缓程序的执行。

语法

要用gprof剖析一个应用程序,第一步编译源程序:

  • -pg:开启剖析功能(注意不要与可执行文件剥离)
  • -g3:开启符号。符号信息对使用gprof的源注释特性是必须的
g++ -pg -g3 -o app main.c

第二步是运行编译后的程序,会生成一个输出文件gmon.out

./app

第三步是使用gprof命令来显示结果,语法如下:

  • --brief:简化gprof的输出。默认情况下,pgrof输出全部的性能信息,并用图例解释每个指标的含义。该选项删除了图例
  • -p 或者--flat-profile:显示应用程序中每个函数花费的总时间和其调用次数
  • -q或者--graph:打印出已剖析的应用程序的调用图。其显示了程序中的函数是如何相互调用的,每个函数所花的时间,以及其子函数所花的时间
  • -A或者-annotated-source:在原始源代码的下面显示剖析信息
gprof [-p -flat-profile -q --graph --brief -A -annotated-source] app

对于一个特定的剖析来说,并不是所有的输出统计信息都是可以得到的。哪个输出统计信息是可得的取决于应用程序是如何为了剖徐而被编译的

实例

运行一个gprof --brief -p xxx的例子:

  • 程序中有两个函数a()和b(),每个函数都调用了一次。
  • a()完成的时间(91%)是b()完成的时间(8.99%)的10倍。
  • 函数a()花费的时间为5.06s,函数b()花费的时间是0.5s
    在这里插入图片描述

运行一个gprof --brief -q xxx的例子(展示程序的调用图):
在这里插入图片描述
另外,gprof还可以对源代码进行注释,以展示每个函数调用的频率。如下,没有展示函数消耗的时间,但是显示了函数被调用的次数。

$ gprof --brief  -A app 
*** File /home/oceanstar/CLionProjects/lib_fiber/main.c:
                #include <stdio.h>
                #include <stdlib.h>
                
                int a(void)
       50000 -> {
                    int i=0,g=0;
                    while(i++<100000)
                    {
                        g+=i;
                    }
                    return g;
                }
                
                int b(void)
       50000 -> {
                    int i=0,g=0;
                    while(i++<400000)
                    {
                        g +=i;
                    }
                    return g;
                }
                int main(int argc, char** argv)
       ##### -> {
                    int iterations;
                    if(argc != 2)
                    {
                        printf("Usage %s <No of Iterations>\n", argv[0]);
                        exit(-1);
                    }
                    else
                        iterations = atoi(argv[1]);
                    printf("No of iterations = %d\n", iterations);
                    while(iterations--)
                    {
                        a();
                        b();
                    }
                }

Top 10 Lines:

     Line      Count

        5      50000
       15      50000

Execution Summary:

        3   Executable lines in this file
        3   Lines executed
   100.00   Percent of the file executed

   100000   Total number of line executions
 33333.33   Average executions per line

oprofile

【待添加】

小结

本文主要介绍了怎样跟踪单个进程的CPU性能瓶颈。

  • 可以确定一个应用程序消耗的时间是如何分配到linux内核、系统库,甚至该应用程序本身的
  • 可以知道怎样找出哪些调用是对内核的,哪些是对系统库的,以及完成它们分别花了多少时间
  • 可以了解如何剖析应用程序,确定源代码的那个行消耗了大量的时间。

在优化了CPU瓶颈之后,接下来就应该去优化那些不受CPU约束的瓶颈,比如磁盘或者超载网络的IO瓶颈

参考

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值