这本书有两个关切点:系统内存(用户层)和性能优化。
这本书和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章 内存的测量
作者在开头的一段话说明了本书采用的方法论:
关于系统内存使用,将按照(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=私有。
第三列,代表在进程地址里的偏移量。
第四列,映射文件的的主从设备号。
第五列,映像文件的节点号。
第六列,映像文件的路径。
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
#include
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.加入头文件
2.在需要内存泄露检查的代码开始调用void mtrace(),在需要内存泄露检查代码结尾调用void muntrace()。如果不调用muntrace,程序自然结束后也会显示内存泄露
3.用debug模式编译检查代码(-g或-ggdb)
4.在运行程序前,先设置环境变量MALLOC_TRACE为一个文件名,这一文件将存有内存分配信息
5.运行程序,内存分配的log将输出到MALLOC_TRACE所执行的文件中。
代码如下:
#include
#include
#include
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. 达到性能要求,停止优化。不达目标,继续分析。
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,TASKLET_SOFTIRQ,SCHED_SOFTIRQ,HRTIMER_SOFTIRQ,RCU_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
#include
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