实验总结分析报告:从系统的角度分析影响程序执行性能的因素

1、计算机硬件系统

冯·诺依曼体系计算机由运算器,控制器,存储器,输入设备和输出设备5部分组成。

输入设备接受外界信息(程序和数据),控制器发出指令将数据送入(内))存储器,然后向内存储器发出取指令命令。在取指令命令下,程序指令逐条送入控制器。控制器对指令进行译码,并根据指令的操作要求,向存储器和运算器发出存数、取数命令和运算命令,经过运算器计算并把计算结果存在存储器内。最后在控制器发出的取数和输出命令的作用下,通过输出设备输出计算结果

2、Linux内核的核心功能

Linux内核只是Linux操作系统一部分。对下,它管理系统的所有硬件设备;对上,它通过系统调用,向Library Routine(例如C库)或者其它应用程序提供接口。因此,其核心功能就是:管理硬件设备,供应用程序使用。

 

3、Linux操作系统整体架构

 

对于操作系统的目的,对底层来说,与硬件交互管理所有的硬件资源;对上层来说,为用户程序(应用程序)提供一个良好的执行环境。最关键的是三个部分,进程管理、内存管理和文件管理。进程管理是系统的核心,内存管理负责内存分配与地址空间的映射,文件管理负责磁盘空间的管理。

3.1、进程管理

进程管理是Linux内核的核心

 

 

 

 

计算机有“三大法宝“:存储程序计算机、函数调用堆栈机制、中断,操作系统有”两把宝剑“:中断上下文、进程上下文。

存储程序计算机:将编写好的程序和数据先写入存储器中,然后启动计算机工作。

函数调用堆栈机制:记录函数调用框架、传递函数参数、保存返回值的地址、提供函数内部局部变量的存储空间

中断:CPU在运行目前任务时,当有特别事件发生时,CPU应当暂停正在执行的程序,转向执行处理该事件的子程序;事件处理完毕后,恢复原来的状态,再继续执行原来的程序。进程中断又分为硬中断和软中断(异常),系统调用也是一种特殊的中断。

中断上下文切换:当内核执行一个中断处理程序时,内核处于中断上下文中。中断上下文和进程无关。因为没有后备进程,所以中断上下文不可以睡眠,不能在里面进行有可能使它睡眠的操作。中断上下文会一直运行至结束,不会被抢占。

进程上下文切换:进程上下文是一种内核所处的操作模式,此时内核代表进程执行-------例如,执行系统调用或运行内核线程。在线程上下文,可以通过current宏关联当前进程。因为进程是以进程上下文的形式连接到内核中断,因此,进程上下文可以睡眠,也可以调度程序。运行于进程上下文的内核代码是可抢占的。

1、进程的描述

在Linux内核中⽤⼀个数据结构struct task_struct来描述进程,直接或间接提供了进程相关的所有信息。struct task_struct的结构包括了进程的状态、进程双向链表的管理,以及控制台tty、⽂件系统fs的描述、进程打开⽂件的⽂件描述符files、内存管理的描述mm,还有进程间通信的信号signal的描述等内容。

 

2、进程的创建

init_task为第⼀个进程(0号进程)的进程描述符结构体变量,它的初始化是通过硬编码⽅式固定下来的。除此之外,所有其他进程的初始化都是通过do_fork复制⽗进程的⽅式初始化的。1号和2号进程的创建是start_kernel初始化到最后由rest_ init通kernel_thread创建了两个内核线程:⼀个是kernel_init,最终把⽤户态的进程init给启动起来,是所有⽤户进程的祖先;另⼀个是kthreadd内核线程,kthreadd内核线程是所有内核线程的祖先,负责管理所有内核线程

3、进程的状态

Linux 内核中,当进程是 TASK_RUNNING 状态时,它是可运行的,也就是就绪态,是否在运行取决于它有没、有获得 CPU 的控制权,也就是说这个进程有没有在 CPU 中实际执行。如果在 CPU 中实际执行了,进程状态就是运行态;如果被内核调度出去了,在等待队列里就是就绪态。

一个正在运行的进程在等待特定的事件或资源时会进入阻塞态。阻塞态也有两种: TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE。TASK_INTERRUPTIBLE 状态是 可以被信号和 wake_up()唤醒的,当信号到来时,进程会被设置为 TASK_RUNNING(就绪态,但是没有在运行),而 TASK_UNINTERRUPTIBLE 只能被 wake_up() 唤醒。如果事件发生或者资源可用,进程被唤醒并被放到运行队列上(操 作系统原理的说法应该是就绪队列)

 

4、进程调度

当前Linux系统的解决方案是,对于实时进程,Linux采用FIFO(先进先出)或者Round Robin(时间片轮转)的调度策略。对其他进程,当前Linux采用CFS(Completely Fair Scheduler)调度器,核心思想是“完全公平”。

Linux系统中常用的几种调度策略为SCHED_NORMAL、SCHED_FIFO、SCHED_RR、SCHED_BATCH。其中SCHED_NORMAL是用于普通进程的调度类,而SCHED_FIFO和SCHED_RR是用于实时进程的调度类,优先级高于SCHED_NORMAL。内核中根据进程的优先级来区分普通进程与实时进程,Linux内核进程优先级为0~139,数值越高,优先级越低,0为最高优先级。实时进程的优先级取值为0~99;而普通进程只具有nice值,nice值映射到优先级为100~139。子进程会继承父进程的优先级。对于实时进程,Linux系统会尽量使其调度延时在一个时间期限内。

5、进程切换

为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复执行以前挂起的某个进程。这种行为被称为进程切换,任务切换或进程上下文切换。

 

3.2、内存管理

Linux采用虚拟内存管理技术,利用虚拟内存技术让每个进程都有4GB 互不干涉的虚拟地址空间。

用户进程部分分段存储内容可由栈、堆、BSS段、数据段、代码段组成。 在将应用程序加载到内存空间执行时,操作系统负责代码段、数据段和BSS段的加载,并在内存中为这些段分配空间。栈也由操作系统分配和管理;堆由程序员自己管理,即显式地申请和释放空间。

3.3、文件管理

       文件系统的实质,就是“存储和组织数据的方法”,文件系统的表现形式,就是“从某个设备中读取数据和向某个设备写入数据”。

Linux内核将不同功能的外部设备,例如Disk设备(硬盘、磁盘、NAND Flash、Nor Flash等)、输入输出设备、显示设备等等,抽象为可以通过统一的文件操作接口(open、close、read、write等)来访问。这就是Linux系统“一切皆是文件”的体现。

随着计算机技术的进步,存储和组织数据的方法也是在不断进步的,从而导致有多种类型的文件系统,例如FAT、FAT32、NTFS、EXT2、EXT3等等。而为了兼容,操作系统或者内核,要以相同的表现形式,同时支持多种类型的文件系统,这就延伸出了虚拟文件系统(VFS)的概念。VFS的功能就是管理各种各样的文件系统,屏蔽它们的差异,以统一的方式,为用户程序提供访问文件的接口。

 

4、举例

读写文件

  1. 用户态运行的程序触发文件读写系统调用
  2. 通过软中断-陷阱(Trap)进入内核态,并通过中断向量表查询触发的系统调用
  3. 通过系统调用读写文件
  4. 触发中断,返回用户态执行

5、影响程序性能的原因

影响性能因素

1、CPU负载

CPU 是操作系统稳定运行的根本,CPU 的速度与性能很大一部分决定了系统整体的性能,因此 CPU 数量越多、主频越高,服务器性能也就相对越好,可以通过top命令查看CPU使用率等。

 

2、内存

内存的大小也是影响 Linux 性能的一个重要的因素。通过free查看内存使用情况

 

3、I/O

磁盘的 I/O 能力会直接影响应用程序的性能。比如说,在一个需要频繁读写的应用中,如果磁盘 I/O 性能得不到满足,就会导致应用的停滞。可通过iostat命令查看,该命令包含在sysstat包中。

 

Perf性能优化工具

1、源程序

考查下面这个例子程序。其中函数 longa() 是个很长的循环,比较浪费时间。函数 foo1 和 foo2 将分别调用该函数 10 次,以及 100 次。编译它

 void longa() 
 { 
   int i,j; 
   for(i = 0; i < 1000000; i++) 
   j=i; //am I silly or crazy? I feel boring and desperate. 
 } 
 void foo2() 
 { 
   int i; 
   for(i=0 ; i < 10; i++) 
        longa(); 
 } 
 void foo1() 
 { 
   int i; 
   for(i = 0; i< 100; i++) 
      longa(); 
 } 
 int main(void) 
 { 
   foo1(); 
   foo2(); 
 }

2、查看整体情况 

程序 t1 是一个 CPU bound 型,因为 task-clock-msecs 接近 1

对 t1 进行调优应该要找到热点 ( 即最耗时的代码片段 ),再看看是否能够提高热点代码的效率。

有些程序慢是因为计算量太大,其多数时间都应该在使用 CPU 进行计算,这叫做 CPU bound 型;有些程序慢是因为过多的 IO,这种时候其 CPU 利用率应该不高,这叫做 IO bound 型;对于 CPU bound 程序的调优和 IO bound 的调优是不同的。

3、查看报告进行优化

 

 

 

不出所料,hot spot 是 longa( ) 函数。

 

实际中,可以通过这样查看hot spot是哪个,然后相应的对其进行优化。

 

posted @ 2021-05-16 17:41  黄志柳  阅读(136)  评论(0编辑  收藏  举报