《嵌入式Linux内存使用与性能优化》笔记 第1章 内存的测量   第2章 进程内存优化 第3章 系统内存优化 第4章 内存泄露 第5章 性能优化的流程 第6章 进程启动速度

《嵌入式Linux内存使用与性能优化》笔记
第1章 内存的测量
 
 第2章 进程内存优化
 第3章 系统内存优化
第4章 内存泄露
第5章 性能优化的流程
第6章 进程启动速度

《嵌入式Linux内存使用与性能优化》笔记
第1章 内存的测量
 
 第2章 进程内存优化
 第3章 系统内存优化
第4章 内存泄露
第5章 性能优化的流程
第6章 进程启动速度

这本书有两个关切点:系统内存(用户层)和性能优化。

这本书和Brendan Gregg的《Systems Performance》相比,无论是技术层次还是更高的理论都有较大差距。但是这不影响,快速花点时间简单过一遍。

然后在对《Systems Performance》进行详细的学习。

由于Ubuntu测试验证更合适,所以在Ubuntu(16.04)+Kernel(4.10.0)环境下做了下面的实验。

全书共9章:1~4章着重于内存的使用,尽量降低进程的内存使用量,定位和发现内存泄露;5~9章着重于如何让系统性能优化,提高执行速度。

第1章 内存的测量

第2章 进程内存优化

第3章 系统内存优化

第4章 内存泄露

第5章 性能优化的流程

第6章 进程启动速度

第7章 性能优化的方法

第8章 代码优化的境界

第9章 系统性能优化


用户空间的内存使用量是由进程使用量累积和系统使用之和,所以优化系统内存使用,就是逐个攻克每个进程的使用量和优化系统内存使用。。

俗话说“知己知彼,百战不殆”,要优化一个进程的使用量,首先得使用工具去评估内存使用量(第1章 内存的测量);

然后就来看看进程那些部分耗费内存,并针对性进行优化(第2章 进程内存优化);

最后从系统层面寻找方法进行优化(第3章 系统内存优化)。

内存的使用一个致命点就是内存泄露,如何发现内存泄露,并且将内存泄露定位是重点(第4章 内存泄露)

作者在开头的一段话说明了本书采用的方法论:

关于系统内存使用,将按照(1)明确目标->(2)寻找评估方法(3)了解当前状况->对系统内存进行优化->重新测量,评估改善状况的过程,来阐述系统的内存使用与优化。

(1)明确目标,针对系统内存优化,有两个:

  A.每个守护进程使用的内存尽可能少

  B.长时间运行后,守护进程内存仍然保持较低使用量,没有内存泄露

(2)寻找评估方法,第1章关注点。

(3)对系统内存进行优化,第2章针对进程进行优化,第3章针对系统层面进行内存优化,第4章关注内存泄露。

系统内存测量

free用以获得当前系统内存使用情况。

在一嵌入式设备获取如下:

busybox free
             total         used         free       shared      buffers
Mem:         23940        15584         8356            0            0 (23940=15584+8356)
-/+ buffers:              15584         8356
Swap:            0            0            0

和PC使用的free对比:

             total       used       free     shared    buffers     cached
Mem:      14190636   10494128    3696508     587948    1906824    5608888
-/+ buffers/cache:    2978416   11212220
Swap:      7999484      68844    7930640

可见这两个命令存在差异,busybox没有cached。这和实际不符,实际可用内存=free+buffers+cached。

buffers是用来给Linux系统中块设备做缓冲区,cached用来缓冲打开的文件。下面是通过cat /proc/meminfo获取,可知实际可用内存=8352+0+3508=11860。已经使用内存为=23940-11860=12080。可见两者存在差异,busybox的free不太准确;/proc/meminfo的数据更准确。

MemTotal:          23940 kB
MemFree:            8352 kB
Buffers:               0 kB
Cached:             3508 kB

进程内存测量

在进程的proc中与内存有关的节点有statm、maps、memmap。

cat /proc/xxx/statm

1086 168 148 1 0 83 0

这些参数以页(4K)为单位,分别是:

1086 Size,任务虚拟地址空间的大小。

168 Resident,应用程序正在使用的物理内存的大小。

148 Shared,共享页数。

1 Trs,程序所拥有的可执行虚拟内存的大小。

0 Lrs,被映像到任务的虚拟内存空间的的库的大小。

83 Drs,程序数据段和用户态的栈的大小。

0 dt,脏页数量(已经修改的物理页面)。

Size、Trs、Lrs、Drs对应虚拟内存,Resident、Shared、dt对应物理内存。

cat /proc/xxx/maps

00400000-00401000 r-xp 00000000 08:05 18561374                           /home/lubaoquan/temp/hello
00600000-00601000 r--p 00000000 08:05 18561374                           /home/lubaoquan/temp/hello
00601000-00602000 rw-p 00001000 08:05 18561374                           /home/lubaoquan/temp/hello
00673000-00694000 rw-p 00000000 00:00 0                                  [heap]
7f038c1a1000-7f038c35f000 r-xp 00000000 08:01 3682126                    /lib/x86_64-linux-gnu/libc-2.19.so
7f038c35f000-7f038c55e000 ---p 001be000 08:01 3682126                    /lib/x86_64-linux-gnu/libc-2.19.so
7f038c55e000-7f038c562000 r--p 001bd000 08:01 3682126                    /lib/x86_64-linux-gnu/libc-2.19.so
7f038c562000-7f038c564000 rw-p 001c1000 08:01 3682126                    /lib/x86_64-linux-gnu/libc-2.19.so
7f038c564000-7f038c569000 rw-p 00000000 00:00 0
7f038c569000-7f038c58c000 r-xp 00000000 08:01 3682489                    /lib/x86_64-linux-gnu/ld-2.19.so
7f038c762000-7f038c765000 rw-p 00000000 00:00 0
7f038c788000-7f038c78b000 rw-p 00000000 00:00 0
7f038c78b000-7f038c78c000 r--p 00022000 08:01 3682489                    /lib/x86_64-linux-gnu/ld-2.19.so
7f038c78c000-7f038c78d000 rw-p 00023000 08:01 3682489                    /lib/x86_64-linux-gnu/ld-2.19.so
7f038c78d000-7f038c78e000 rw-p 00000000 00:00 0
7ffefe189000-7ffefe1aa000 rw-p 00000000 00:00 0                          [stack]
7ffefe1c4000-7ffefe1c6000 r--p 00000000 00:00 0                          [vvar]
7ffefe1c6000-7ffefe1c8000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

第一列,代表该内存段的虚拟地址。

第二列,r-xp,代表该段内存的权限,r=读,w=写,x=执行,s=共享,p=私有。

第三列,代表在进程地址里的偏移量。

第四列,映射文件的的主从设备号。

第五列,映像文件的节点号。

第六列,映像文件的路径。

《嵌入式Linux内存使用与性能优化》笔记
第1章 内存的测量
 
 第2章 进程内存优化
 第3章 系统内存优化
第4章 内存泄露
第5章 性能优化的流程
第6章 进程启动速度

kswapd

Linux存在一个守护进程kswapd,他是Linux内存回收机制,会定期监察系统中空闲呢村的数量,一旦发现空闲内存数量小于一个阈值的时候,就会将若干页面换出。

但是在嵌入式Linux系统中,却没有交换分区。没有交换分区的原因是:

1.一旦使用了交换分区,系统系能将下降的很快,不可接受。

2.Flash的写次数是有限的,如果在Flash上面建立交换分区,必然导致对Flash的频繁读写,影响Flash寿命。

那没有交换分区,Linux是如何做内存回收的呢?

对于那些没有被改写的页面,这块内存不需要写到交换分区上,可以直接回收。

对于已经改写了的页面,只能保留在系统中,,没有交换分区,不能写到Flash上。

在Linux物理内存中,每个页面有一个dirty标志,如果被改写了,称之为dirty page。所有非dirty page都可以被回收。

 


第2章 进程内存优化

当存在很多守护进程,又要去降低守护进程内存占用量,如何去推动:

1.所有守护进程内存只能比上一个版本变少。

2.Dirty Page排前10的守护进程,努力去优化,dirty page减少20%。

可以从三个方面去优化:

1.执行文件所占用的内存

2.动态库对内存的影响

3.线程对内存的影响

2.1 执行文件

一个程序包括代码段、数据段、堆段和栈段。一个进程运行时,所占用的内存,可以分为如下几部分:

栈区(stack):由编译器自动分配释放,存放函数的参数、局部变量等

堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可有操作系统来回收

全局变量、静态变量:初始化的全局变量和静态变量在一块区域,未初始化的全局变量和静态变量在另一块区域,程序结束后由系统释放

文字常量:常量、字符串就是放在这里的,程序结束后有系统释放

程序代码:存放函数体的二进制代码

下面结合一个实例分析:

#include <stdlib.h>
#include <stdio.h>

int n=10;
const int n1=20;
int m;

int main()
{
  int s=7;
  static int s1=30;
  char *p=(char *)malloc(20);
  pid_t pid=getpid();

  printf("pid:%d ", pid);
  printf("global variable address=%p ", &n);
  printf("const global address=%p ", &n1);
  printf("global uninitialization variable address=%p ", &m);;
  printf("static variable address=%p ", &s1);
  printf("stack variable address=%p ", &s);
  printf("heap variable address=%p ", &p);
  pause();
}

执行程序结果:

pid:18768
global variable address=0x601058
const global address=0x400768
global uninitialization variable address=0x601064
static variable address=0x60105c
stack variable address=0x7ffe1ff7d0e0
heap variable address=0x7ffe1ff7d0e8

查看cat /proc/17868/maps

00400000-00401000 r-xp 00000000 08:05 18561376                           /home/lubaoquan/temp/example
  (只读全局变量n1位于进程的代码段)
00600000-00601000 r--p 00000000 08:05 18561376                           /home/lubaoquan/temp/example
00601000-00602000 rw-p 00001000 08:05 18561376                           /home/lubaoquan/temp/example
  (全局初始变量n、全局未初始变量m、局部静态变量s1,都位于进程的数据段)
00771000-00792000 rw-p 00000000 00:00 0                                  [heap]
7f7fb86a2000-7f7fb8860000 r-xp 00000000 08:01 3682126                    /lib/x86_64-linux-gnu/libc-2.19.so
7f7fb8860000-7f7fb8a5f000 ---p 001be000 08:01 3682126                    /lib/x86_64-linux-gnu/libc-2.19.so
7f7fb8a5f000-7f7fb8a63000 r--p 001bd000 08:01 3682126                    /lib/x86_64-linux-gnu/libc-2.19.so
7f7fb8a63000-7f7fb8a65000 rw-p 001c1000 08:01 3682126                    /lib/x86_64-linux-gnu/libc-2.19.so
7f7fb8a65000-7f7fb8a6a000 rw-p 00000000 00:00 0
7f7fb8a6a000-7f7fb8a8d000 r-xp 00000000 08:01 3682489                    /lib/x86_64-linux-gnu/ld-2.19.so
7f7fb8c63000-7f7fb8c66000 rw-p 00000000 00:00 0
7f7fb8c89000-7f7fb8c8c000 rw-p 00000000 00:00 0
7f7fb8c8c000-7f7fb8c8d000 r--p 00022000 08:01 3682489                    /lib/x86_64-linux-gnu/ld-2.19.so
7f7fb8c8d000-7f7fb8c8e000 rw-p 00023000 08:01 3682489                    /lib/x86_64-linux-gnu/ld-2.19.so
7f7fb8c8e000-7f7fb8c8f000 rw-p 00000000 00:00 0
7ffe1ff5f000-7ffe1ff80000 rw-p 00000000 00:00 0                          [stack]
  (局部变量s、malloc分配内存指针p都位于栈段)
7ffe1ffbb000-7ffe1ffbd000 r--p 00000000 00:00 0                          [vvar]
7ffe1ffbd000-7ffe1ffbf000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]


第3章 系统内存优化

3.1 守护进程的内存使用

守护进程由于上期运行,对系统内存使用影响很大:

1.由于一直存货,所以其占用的内存不会被释放。

2.即使什么都不做,由于引用动态库,也会占用大量的物理内存。

3.由于生存周期很长,哪怕一点内存泄露,累积下来也会很大,导致内存耗尽。

那么如何降低风险呢?

1.设计守护进程时,区分常驻部分和非常驻部分。尽量降低守护进程的逻辑,降低内存占用,降低内存泄露几率。或者将几个守护进程内容合为一个。

2.有些进程只是需要尽早启动,而不需要变成守护进程。可以考虑加快启动速度,从而使服务达到按需启动的需求。优化方式有优化加载动态库、使用Prelink方法、采用一些进程调度方法等。

3.2 tmpfs分区

Linux中为了加快文件读写,基于内存建立了一个文件系统,成为ramdisk或者tmpfs,文件访问都是基于物理内存的。

使用df -k /tmp可以查看分区所占空间大小:

Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/sda1       77689292 9869612  63850172  14% /

在对这个分区进行读写时,要时刻注意,他是占用物理内存的。不需要的文件要及时删除。

3.3 Cache和Buffer

系统空闲内存=MemFree+Buffers+Cached。

Cache也称缓存,是把从Flash中读取的数据保存起来,若再次读取就不需要去读Flash了,直接从缓存中读取,从而提高读取文件速度。Cache缓存的数据会根据读取频率进行组织,并最频繁读取的内容放在最容易找到的位置,把不再读的内容不短往后排,直至从中删除。
在程序执行过程中,发现某些指令不在内存中,便会产生page fault,将代码载入到物理内存。程序退出后,代码段内存不会立即丢弃,二是作为Cache缓存。

Buffer也称缓存,是根据Flash读写设计的,把零散的写操作集中进行,减少Flash写的次数,从而提高系统性能。

Cache和BUffer区别简单的说都是RAM中的数据,Buffer是即将写入磁盘的,而Cache是从磁盘中读取的

使用free -m按M来显示Cache和Buffer大小:

             total       used       free     shared    buffers     cached
Mem:         13858       1204      12653        206         10        397
-/+ buffers/cache:        796      13061
Swap:         7811          0       7811

降低Cache和Buffer的方法:

sync
  该命令将未写的系统缓冲区写到磁盘中。包含已修改的 i-node、已延迟的块 I/O 和读写映射文件。

/proc/sys/vm/drop_caches
  a)清理pagecache(页面缓存)
  # echo 1 > /proc/sys/vm/drop_caches     或者 # sysctl -w vm.drop_caches=1
  b)清理dentries(目录缓存)和inodes
  # echo 2 > /proc/sys/vm/drop_caches     或者 # sysctl -w vm.drop_caches=2
 
c)清理pagecache、dentries和inodes
  # echo 3 > /proc/sys/vm/drop_caches     或者 # sysctl -w vm.drop_caches=3
  上面三种方式都是临时释放缓存的方法,要想永久释放缓存,需要在/etc/sysctl.conf文件中配置:vm.drop_caches=1/2/3,然后sysctl -p生效即可!

vfs_cache_pressure
vfs_cache_pressure=100    这个是默认值,内核会尝试重新声明dentries和inodes,并采用一种相对于页面缓存和交换缓存比较”合理”的比例。
减少vfs_cache_pressure的值,会导致内核倾向于保留dentry和inode缓存。
增加vfs_cache_pressure的值,(即超过100时),则会导致内核倾向于重新声明dentries和inodes
总之,vfs_cache_pressure的值:
小于100的值不会导致缓存的大量减少
超过100的值则会告诉内核你希望以高优先级来清理缓存。

3.4 内存回收

kswapd有两个阈值:pages_high和pages_low,当空闲内存数量低于pages_low时,kswapd进程就会扫描内存并且每次释放出32个free pages,知道free page数量达到pages_high。

kswapd回收内存的原则?

1.如果物理页面不是dirty page,就将该物理页面回收。

  • 代码段,只读不能被改写,所占内存都不是dirty page。
  • 数据段,可读写,所占内存可能是dirty page,也可能不是。
  • 堆段,没有对应的映射文件,内容都是通过修改程序改写的,所占物理内存都是dirty page。
  • 栈段和堆段一样,所占物理内存都是dirty page。
  • 共享内存,所占物理内存都是dirty page。

就是说,这条规则主要面向进程的代码段和未修改的数据段

2.如果物理页面已经修改并且可以备份回文件系统,就调用pdflush将内存中的内容和文件系统进行同步。pdflush写回磁盘,主要针对Buffers。

3.如果物理页面已经修改但是没有任何磁盘的备份,就将其写入swap分区。

kswapd再回首过程中还存在两个重要方法:LMR(Low on Memory Reclaiming)和OMK(Out of Memory Killer)。

由于kswapd不能提供足够空闲内存是,LMR将会起作用,每次释放1024个垃圾页知道内存分配成功。

当LMR不能快速释放内存的时候,OMK就开始起作用,OMK会采用一个选择算法来决定杀死某些进程。发送SIGKILL,就会立即释放内存。

3.5 /proc/sys/vm优化

此文件夹下面有很多接口控制内存操作行为,在进行系统级内存优化的时候需要仔细研究,适当调整。

block_dump
  表示是否打开Block Debug模式,用于记录所有的读写及Dirty Block写回操作。0,表示禁用Block Debug模式;1,表示开启Block Debug模式。

dirty_background_ratio
  表示脏数据达到系统整体内存的百分比,此时触发pdflush进程把脏数据写回磁盘。

dirty_expires_centisecs
  表示脏数据在内存中驻留时间超过该值,pdflush进程在下一次将把这些数据写回磁盘。缺省值3000,单位是1/100s。

dirty_ratio
  表示如果进程产生的脏数据达到系统整体内存的百分比,此时进程自行吧脏数据写回磁盘。

dirty_writeback_centisecs
  表示pdflush进程周期性间隔多久把脏数据协会磁盘,单位是1/100s。

vfs_cache_pressure
  表示内核回收用于directory和inode cache内存的倾向;缺省值100表示内核将根据pagecache和swapcache,把directory和inode cache报纸在一个合理的百分比;降低该值低于100,将导致内核倾向于保留directory和inode cache;高于100,将导致内核倾向于回收directory和inode cache。

min_free_kbytes
  表示强制Linux VM最低保留多少空闲内存(KB)。

nr_pdflush_threads
  表示当前正在进行的pdflush进程数量,在I/O负载高的情况下,内核会自动增加更多的pdflush。

overcommit_memory
  指定了内核针对内存分配的策略,可以是0、1、2.
  0 表示内核将检查是否有足够的可用内存供应用进程使用。如果足够,内存申请允许;反之,内存申请失败。
  1 表示内核允许分配所有物理内存,而不管当前内存状态如何。
  2 表示内核允许分配查过所有物理内存和交换空间总和的内存。

overcommit_ratio
  如果overcommit_memory=2,可以过在内存的百分比。

page-cluster
  表示在写一次到swap区时写入的页面数量,0表示1页,3表示8页。

swapiness
  表示系统进行交换行为的成都,数值(0~100)越高,越可能发生磁盘交换。

legacy_va_layout
  表示是否使用最新的32位共享内存mmap()系统调用。

nr_hugepages
  表示系统保留的hugetlg页数。


第4章 内存泄露

4.1 如何确定是否有内存泄露

解决内存泄露一个好方法就是:不要让你的进程成为一个守护进程,完成工作后立刻退出,Linux会自动回收该进程所占有的内存。
测试内存泄露的两种方法:

1.模仿用户长时间使用设备,查看内存使用情况,对于那些内存大量增长的进程,可以初步怀疑其有内存泄露。

2.针对某个具体测试用例,检查是否有内存泄露。

在发现进程有漏洞之后,看看如何在代码中检查内存泄露。

4.2 mtrace

glibc针对内存泄露给出一个钩子函数mtrace:

1.加入头文件<mcheck.h>

2.在需要内存泄露检查的代码开始调用void mtrace(),在需要内存泄露检查代码结尾调用void muntrace()。如果不调用muntrace,程序自然结束后也会显示内存泄露

3.用debug模式编译检查代码(-g或-ggdb)

4.在运行程序前,先设置环境变量MALLOC_TRACE为一个文件名,这一文件将存有内存分配信息

5.运行程序,内存分配的log将输出到MALLOC_TRACE所执行的文件中。

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <mcheck.h>

int main(void)
{
  mtrace();

  char *p=malloc(10);
  return 0;
}

编译,设置环境变量,执行,查看log:

gcc -o mem-leakage -g mem-leakage.c

export MALLOC_TRACE=/home/lubaoquan/temp/malloc.og

./mem-leakage

= Start
@ ./mem-leakage:[0x400594] + 0x100d460 0xa (0xa表示泄露的内存大小,和malloc(10)对应)

加入mtrace会导致程序运行缓慢:

1.日志需要写到Flash上(可以将MALLOC_TRACE显示到stdout上。)

2.mtrace函数内,试图根据调用malloc代码指针,解析出对应的函数


性能优化是一个艰苦、持续、枯燥、反复的过程,涉及到的内容非常多,编译器优化、硬件体系结构、软件的各种技巧等等。

另外在嵌入式电池供电系统上,性能的优化也要考虑到功耗的使能。PnP的两个P(Power and Performance)是不可分割的部分。

第5章 性能优化的流程

5.1 性能评价

首先“快”与“慢”需要一个客观的指标,同时明确定义测试阶段的起讫点。

同时优化也要考虑到可移植性以及普适性,不要因为优化过度导致其他问题的出现。

5.2 性能优化的流程

1. 测量,获得数据,知道和目标性能指标的差距。

2. 分析待优化的程序,查找性能瓶颈。

3. 修改程序。

4. 重新测试,验证优化结果。

5. 达到性能要求,停止优化。不达目标,继续分析。

《嵌入式Linux内存使用与性能优化》笔记
第1章 内存的测量
 
 第2章 进程内存优化
 第3章 系统内存优化
第4章 内存泄露
第5章 性能优化的流程
第6章 进程启动速度

5.3 性能评测 

介绍两种方法:可视操作(摄像头)和日志。

话说摄像头录像评测,还是很奇葩的,适用范围很窄。但是貌似还是有一定市场。

5.4 性能分析

导致性能低下的三种主要原因:

(1) 程序运算量很大,消耗过多CPU指令。

(2) 程序需要大量I/O,读写文件、内存操作等,CPU更多处于I/O等待。

(3) 程序之间相互等待,结果CPU利用率很低。

简单来说即是CPU利用率高、I/O等待时间长、死锁情况。

下面重点放在第一种情况,提供三种方法。

1. 系统相关:/proc/stat、/proc/loadavg

cat /proc/stat结果如下:

cpu  12311503 48889 7259266 561072284 575332 0 72910 0 0 0-----分别是user、nice、system、idle、iowait、irq、softirq、steal、guest、guest_nice
  user:从系统启动开始累计到当前时刻,用户态CPU时间,不包含nice值为负的进程。
  nice:
从系统启动开始累计到当前时刻,nice值为负的进程所占用的CPU时间。
  system:从系统启动开始累计到当前时刻,内核所占用的CPU时间。
  idle:从系统启动开始累计到当前时刻,除硬盘IO等待时间以外其他等待时间。
  iowait:从系统启动开始累计到当前时刻,硬盘IO等待时间。
  irq:从系统启动开始累计到当前时刻,硬中断时间。
  softirq:从系统启动开始累计到当前时刻,软中断时间。
  steal:从系统启动开始累计到当前时刻,involuntary wait
  guest:running as a normal guest
  guest_nice:running as a niced guest
cpu0 3046879 11947 1729621 211387242 95062 0 1035 0 0 0 cpu1 3132086 8784 1788117 116767388 60010 0 535 0 0 0 cpu2 3240058 12964 1826822 116269699 353944 0 31989 0 0 0 cpu3 2892479 15192 1914705 116647954 66316 0 39349 0 0 0 intr 481552135 16 183 0 0 0 0 0 0 175524 37 0 0 2488 0 0 0 249 23 0 0 0 0 0 301 0 0 3499749 21 1470158 156 33589268 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-------------------Counts of interrupts services since boot time.Fist column is the total of all interrupts services, each subsequent if total for particular interrupt.
ctxt 2345712926-------------------------------------------------Toal number of context switches performed since bootup accross all CPUs. btime 1510217813------------------------------------------------Give the time at which the system booted, in seconds since the Unix epoch. processes 556059------------------------------------------------Number of processes and threads created, include(but not limited to) those created by fork() or clone() system calls. procs_running 2-------------------------------------------------Current number of runnable threads procs_blocked 1-------------------------------------------------Current number of threads blocked, waiting for IO to complete. softirq 415893440 117 134668573 4001105 57050104 3510728 18 1313611 104047789 0 111301395---总softirq和各种类型softirq产生的中断数:HI_SOFTIRQ,TIMER_SOFTIRQ,NET_TX_SOFTIRQ,NET_RX_SOFTIRQ,BLOCK_SOFTIRQ,IRQ_POLL_SOFTIRQ,/* Preferable RCU should always be the last softirq */

由cpu的各种时间可以推导出:

CPU时间=user+nice+system+idle+iowait+irq+softirq+steal+guest+guest_nice

CPU利用率=1-idle/(user+nice+system+idle+iowait+irq+softirq+steal+guest+guest_nice)

CPU用户态利用率=(user+nice)/(user+nice+system+idle+iowait+irq+softirq+steal+guest+guest_nice)

CPU内核利用率=system/(user+nice+system+idle+iowait+irq+softirq+steal+guest+guest_nice)

IO利用率=iowait/(user+nice+system+idle+iowait+irq+softirq+steal+guest+guest_nice)

cat /proc/loadavg结果如下:

0.46 0.25 0.16 2/658 13300

1、5、15分钟平均负载;

2/658:在采样时刻,运行队列任务数目和系统中活跃任务数目。

13300:最大pid值,包括线程。

2. 进程相关:/proc/xxx/stat

24021 (atop) S 1 24020 24020 0 -1 4194560 6179 53 0 0 164 196 0 0 0 -20 1 0 209898810 19374080 1630 18446744073709551615 1 1 0 0 0 0 0 0 27137 0 0 0 17 1 0 0 0 0 0 0 0 0 0 0 0 0 0

3. top

top是最常用来监控系统范围内进程活动的工具,提供运行在系统上的与CPU关系最密切的进程列表,以及很多统计值。


第6章 进程启动速度

进程启动可以分为两部分:

(1) 进程启动,加载动态库,直到main函数值钱。这是还没有执行到程序员编写的代码,其性能优化有其特殊方法。

(2) main函数之后,直到对用户的操作有所响应。涉及到自身编写代码的优化,在7、8章介绍。

6.1 查看进程的启动过程

hello源码如下:

#include <stdio.h>
#include <stdlib.h>

int main()
{
  printf("Hello world!
");
  return 0;
}

编译:

gcc -o hello -O2 hello.c

strace用于查看系统运行过程中系统调用,同时得知进程在加载动态库时的大概过程,-tt可以打印微妙级别时间戳。

strace -tt ./hello如下:

20:15:10.185596 execve("./hello", ["./hello"], [/* 82 vars */]) = 0
20:15:10.186087 brk(NULL)               = 0x19ad000
20:15:10.186206 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
20:15:10.186358 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f24710ea000
20:15:10.186462 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
20:15:10.186572 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
20:15:10.186696 fstat(3, {st_mode=S_IFREG|0644, st_size=121947, ...}) = 0
20:15:10.186782 mmap(NULL, 121947, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f24710cc000
20:15:10.186857 close(3)                = 0
20:15:10.186975 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
20:15:10.187074 open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
20:15:10.187153 read(3, "177ELF2113        3 >