Linux主要分成三部分:
简单结构如下图所示:
以读取文件I/O流为例:
系统调用层中操作系统中处理 read() 函数的入口函数是 sys_read(), sys_read() 会从进程中获取文件描述符以及文件当前的操作位置,接着调用 vfs_read() 函数,而 vfs_read() 函数则是会调用 file 结构中的对应功能函数去完成文件的读操作。
调用关系如下:
sys_read() |--- vfs_read() |--- generic_file_read() |--- generic_file_aio_read() |--- generic_file_direct_IO()
在文件系统层中,Linux支持许多不同的文件系统,并将它们组织成了一个统一的虚拟文件系统(VirtualFileSystem),其旨在为各类文件系统提供一个统一的操作界面和应用编程接口。VFS之上,是对 open、close、read 和 write 等函数的一个通用 API 抽象;而 VFS之下则是文件系统抽象,它定义了上层函数的实现方式。
文件系统层之下的缓冲区缓存为文件系统层提供了一个与具体文件系统无关的通用函数集,优化了对物理设备的访问。
缓冲区缓存之下是设备驱动程序,它实现了特定物理设备的接口。
评价Linux系统性能的好坏,大致可以从Linux完成任务的有效性、稳定性以及响应速度等方面进行考量。而系统中的应用程序执行性能的关键则是在于进程管理。进程管理是操作系统的最重要的功能之一,只有完善的进程管理才能保证一个程序平稳而高效地运行。
应用程序性能的问题,大多可分为三种:
三个问题分别受制于硬件CPU,磁盘I/O,内存的性能。
Perf是Linux kernel自带的系统性能优化工具,用于查看热点函数以及cache未命中的比率,通俗得说就是可以了解程序中哪些地方最耗时间,从而重点分析。Perf能够分析程序运行期间发生的硬件事件,也能够分析软件事件,做到了同时分析应用程序代码和内核,从而方便开发者全面理解应用程序中的性能瓶颈。
构建程序test.c如下:
//test.c #include<stdio> #include<math.h> #include<sys/types.h> #include<linux/unistd.h> int foo(){ double Mypi,h,sum,x; long long n,i; n = 5000000; h = 1.0/n; sum = 0.0; for(i=1;i<=n;i++) { x = h*(i-0.5); sum += 4.0/(1.0+pow(x,2)); } Mypi = h*sum; return 0; } int main(){ printf("pid:%d\n",getpid()); sleep(8); foo(); return 0; }
可以看出test是一个计算pi的计算(CPU)密集型程序。
编译并使用 perf stat
命令查看test的运行性能表现:
针对上面的性能表现报告,可以初步判断进程性能的瓶颈所在。接下来可以用 perf record
命令对程序进行执行性能采样,然后用 perf report -g
命令生成函数调用流程图以及函数的CPU占有时间,由此则可以定位到热点函数。
构建程序test2.c如下:
//test2.c void longa(int a[]) { int i,j; for(i = 0; i < 1000000; i++) j = i; *a = j; } void foo2(int a[]) { int i; for(i=0 ; i < 10; i++) longa(&a[i]); } void foo1(int a[]) { int i; for(i = 0; i< 100; i++) longa(&a[i]); } int main(void) { int a[100]; foo1(a); foo2(a); }
生成其函数调用流程图:
针对这些热点函数进行性能优化迭代,就可以将程序的执行性能优化。