从系统的角度分析影响程序执行性能的因素

 

1. 精简的Linux系统模型概念

 

 

 

 

下面从Linux操作系统的几个管理功能来,统摄整顿本课程及相关的知识信息,一一阐述对应的模型概念,并从模型上、逻辑上详细讲解,再分析影响程序执行性能的因素。

 

2.模型分解

一、内存管理

linux的内存从宏观上分为内核区与用户区,Linux内存有内核和外核的区分,主要在于两种模式下,能访问的到的内存地址空间不一样。内核状态能访问内存的所有地址,但是外核只能访问一大部分地址。

内核进程可以访问所有内存,而用户进程只能访问用户区。在编写linux内核模块时,如果内核模块需要访问用户区内存,需要从用户区内存复制数据,而不是直接访问。

在内存管理时,内存按段、页来分割,以方便管理。同时通过虚拟内存和硬盘空间,让所有程序共享整个内存。基于LRU的调度算法和程序的连续性规则,将长期不用的内存页面置换到硬盘上,将需要的内存页面置换到内存中,从而造成每个进程都拥有完整内存的假象。

从进程的角度看,内存分为数据段、代码段、BSS段、堆和栈。

在内存的页面调度上,我们可以通过根据实际进程情况,来选择良好的调度页面算法,例如FIFO调度,LRU,最佳置换算法,时钟置换算法,改良的时钟置换算法。如果有必要的情况下,甚至可以给优先级进程分配更大空间的内存地址。

 

二、进程管理

描述:

  • 进程是程序的一次执行过程
  • 进程是内存区域中的一组指令序列与其数据在处理机上顺序执行的一次活动
  • 进程是一个可并发执行的程序在一个数据集上的一次运行,是操作系统资源分配的基本单位

Linux中,用于描述进程的结构体为task_struct,struct task_struct的数据结构非常庞大,其中state是进程状态,stack是堆栈等,以及控制台tty、文件系统fs的描述、进程打开文件的文件描述符files、,内存管理的描述mm还有进程间通信的信号signal的描述等,还有描述线程的thread.

进程初始化:

init_task 是 0 号进程的进程描述符结构体变量,其初始化通过硬编码方式固定;其他所有进程都是通过 do_fork() 复制父进程的方式初始化。

之后0号进程又会fork出1号和2号进程,分别去使用户态和内核态。1号进程是所有用户进程的祖先,而2号进程负责所有内核线程的调度和管

进程基本状态

    • TASK_RUNNING:进程准备执行或正在执行。

    • TASK_INTERRUPTIBLE:进程被阻塞,可提前被信号打断唤醒。

    • TASK_UNINTERRUPTIBLE:进程被阻塞,不可提前被信号打断唤醒。

    • TASK_STOPPED:进程暂停运行。

    • TASK_ZOMBIE:进程被终止但未回收。

未被调入内存时,为处于在外存的挂起状态,需要重新调和装载进内存等待。

 

Linux支持的调度策略

SCHED_RR
实时,基于优先级的轮转法,一个具有SCHED_FIFO调度的策略的进程有

在三种情况下 可能 会让出CPU
1、主动让出
2、被更高优先级的进程剥夺执行权
3、时间片用完

SCHED_FIFO
实时,先进先出算法,一个具有SCHED_FIFO调度的策略的进程只,有在两种情况下会让出CPU

1、主动让出
2、 被更高优先级的进程剥夺执行

 

SCHED_NORMAL, 非实时,基于优先级的轮转法

只要有实时进程,采用SCHED_NORMAL策略的进程就得不到运行,同采用SCHED_NORMAL策略的进程参考优先级进行调度

 

调度算法:

1、优先级(实时进程>交互进程>普通进程,另外还可设置其他优先级)

2、运行队列

3、CFS(按照运行时间的权重进行排列)

 

进程的调度是通过中断实现的,下面将会详述。。在切换过程中,会保存进程上下文。linux进程上下文包括进程的虚拟地址空间和硬件上下文,会通过调用schedule调用的switch_to去实现切换。

进程调度的有利于让CPU资源和其他不同的资源得到利用的最大化。从而提高整体所有进程的程序执行性能。

 

 

三、中断处理

为了保护系统安全,一个 Linux 系统分为两部分,用户空间和内核空间,一般用户程序还有 shell,一些 lib 库运行于用户空间,完整的 Linux 内核运行于内核空间。

内外核模式为了应用程序的正常运行和操作系统资源的管理和安全提供有效的保证,而中断正是将内核和外核和联系起来的关键。

 

应用程序不直接与系统资源打交道,当需要这些资源时,用户程序通过系统调用(中断)从用户态切换到核心态,并将任务委派给内核执行,在内核中,一般每个系统调用对应一个系统调用的封装例程,函数库再用这些封装例程定义出给程序员调用的 API,这样把系统调用(中断)最终封装成方便程序员使用的库函数。系统

 Linux的中断处理过程实际上就是堆栈的变换过程,和一些上下文的变换。

例如:

1、发生的是普通系统调用,就是由用户态堆栈esp3切换到内核堆栈esp0再切换到esp3的用户态堆栈过程,堆栈切换的过程过程还要保存其他的中断上下文,例如上一层中eip下一条指令的地址,ebp,eflags等等中断上下文。

2、发生的schedule和exceve等,除了会发生系统调用的中断,还会发生进程的调度,例如A进程调度切换成了B进程,堆栈变换过程是由A用户堆栈---A内核堆栈---B内核堆栈---B用户堆栈,而不是返回到A的用户堆栈,

这个过程不仅要保存中断上下文,还要保存对应文件描述符的进程上下文。

普通系统调用(中断)过程

 

 

fork(中断)过程(有可能返回新进程的起点,也有可能返回原父进程的下一条指令,因为有两次返回:ret_from_fork   和ret,返回哪一个取决于是否发生调度,返回的pid属于是父进程(pid>0)还是子进程(pid=0)的情况)

 

 

execve中断调用过程(返回到新进程的起点)

 

 

 

 

 

除了上面所说的内中断,实际上还有许多来自外部的外中断,例如外部硬件的中断,IO设备的处理也是通过可以通过中断这种方法来响应的,因为IO的数据一般是需要立刻处理否则就会消失或者被覆盖的,所以一般来说中断处理IO设备(DMA)是比轮询要更好的方法,下面将详细介绍IO管理。

 

四、IO管理(套接字,网卡,磁盘等)

 

对于一次IO访问,数据会先被拷贝到内核的缓冲区中,然后才会从内核的缓冲区拷贝到应用程序的地址空间。需要经历两个阶段:

  1. 准备数据
  2. 将数据从内核缓冲区拷贝到进程地址空间

由于存在这两个阶段,Linux产生了下面五种IO模型(以socket为例):

1、阻塞IO模型

2、非阻塞IO模型

3、IO复用模型

4、信号驱动IO模型

5、异步IO模型

以下主要介绍一下IO复用模型:

select,poll,epoll都是IO多路复用中的模型

 select:单个进程监控的文件描述符有限,通常为1024*8个文件描述符,内核/用户数据拷贝频繁,操作复杂,轮询时间效率低,水平触发

poll:poll操作比select稍微简单点。select采用三个位图来表示fd_set,poll使用pollfd的指针,内外核之间传递的是链表,其他和select几乎一样

epoll:

1)调用epoll_create建立一个epoll对象,这个对象包含了一个红黑树和一个双向链表。并与底层建立回调机制。
2)调用epoll_ctl向epoll对象中添加这100万个连接的套接字
3)调用epoll_wait收集发生事件的连接

而们调用epoll_wait时就相当于以往调用select/poll,但是这时却不用传递socket句柄给内核,因为内核已经在epoll_ctl中拿到了要监控的句柄列表

epoll实际上就是文件系统里面建立了一个红黑树的文件,这个红黑树用监控套接字和文件描述符的数据结构,只需要检查这个文件的红黑树即可,不需要频繁传输要监控的端口或者文件描述符

 

五、文件管理

 

 

 

在LInux操作系统里面,一切东西皆为文件,所以文件管理起到非常重要的作用,Linux的文件的数据结构类型也有很多种类。

 

Linux 系统内核在用户进程与实际文件系统之间加入了一个抽象层,称为虚拟文件系统。主要意义在于支持不同的文件系统,提供管理文件目录的统一方法以及允许访问其他操作系统的文件。

要实现操作系统对其它各种不同文件系统的支持,就要将对各种不同文件系统的操作和管理纳入到一个统一的框架中。对用户程序隐去各种不同文件系统的实现细节,为用户程序提供一个统一的、抽象的、虚拟的文件系统界面,这就是所谓的虚拟文件系统VFS(Virtual File System)。

 

在Linux操作系统中,可以将DOS格式的磁盘或分区,即文件系统,“安装”到系统中,然后用户程序就可以按完全相同的方式访问这些文件,就好像它们也是Ext2格式的文件一样,一般分为三个层次。

虚拟文件系统(Virtual File System,  VFS)在linux的文件管理中扮演了重要的角色。VFS是 Linux 内核中的一个软件层,用于给用户空间的程序提供文件系统接口;同时,它也提供了内核中的一个 抽象功能,允许不同的文件系统共存。VFS直接与底层驱动打交道,负责将数据从缓冲区写入硬盘,负责抽象底层复杂的接口,向上层提供统一的调用接口。因为IO是一件非常耗时的事情,每一次调用write写文件都立刻写硬盘非常浪费时间。鉴于程序的连续性,每一次调用write都不是立刻将数据写入硬盘,而是写入缓冲区中,等待系统统一将数据写入硬盘,这样可以减少硬盘寻道浪费的时间,加快了IO速度。

 

 

 

 

3.影响程序执行性能的因素:

经过以上系统模型的分析以后,我们可以得知影响应用程序执行性能的因素和优化如下:

一、CPU:

1、CPU是操作系统稳定运行的根本,CPU的速度与性能在很大程度上决定了系统整体的性能,因此,CPU数量越多、主频越高,服务器性能也就相对越好。

但事实并非完全如此。 目前大部分CPU在同一时间内只能运行一个线程,超线程的处理器可以在同一时间运行多个线程,因此,可以利用处理器的超线程特性提高系统性能。在Linux系统下,只有运行SMP内核才能支持超线程。但是,安装的CPU数量越多,从超线程获得的性能方面的提高就越少。另外,Linux内核会把多核的处理器当作多个单独的CPU来识别。

例如两个4核的CPU,在Lnux系统下会被当作8个单核CPU。但是从性能角度来讲,两个4核的CPU和8个单核的CPU并不完全等价,所以要把CPU的配置和性能放在主要位置。

2、良好的调度算法是使得CPU得以最大效率利用的又一个关键,例如IO型进程在等待设备IO数据信息的时候应该尽量被调度出去让出CPU等待中断唤醒或者轮询唤醒,计算型进程应该尽量提早调入操作。

3、可以使用top, vmstat等工具查看分析CPU,负载等情况。

 

二、内存:

1、在内存上,内存小会使系统进程堵塞,性能下降。而内存过大,会导致资源浪费。

2、Linux中有虚拟内存和物理内存的概念,可以创建虚拟内存来缓解物理内存的不足,但是如果占用过多的虚拟内存,会造成应用程序的性能明显下降。

3、建议使用64位Linux因为64位操作系统可以使用更大的内存,而且在32位Linux操作系统中,应用程序的单个进程最大只能用2G内存,而64位系统没有这个限制。

4、如果内存的空间大小无法进一步提升的情况之下,我们可以利用垃圾清除算法定时清理内存的不再用到的垃圾,来保持内存的大小空间可以持续利用,利用CMS、G1等算法去回收内存垃圾。

5、同时,我们在进行一些进程的内存的页框的调度时,要根据实际情况选择更好的页面调度算法,例如CPU计算密集型的进行使用FIFO的页面调度,另外的调度算法还有LRU时钟算法等等。

6、Linux也会在内存适当配备一些cache缓存来提高换入换出的性能。除此之外,内存的频率、容量大小都是很重要的因素。可以使用free, vmstat等工具查看分析内存占用情况。

 

三、磁盘I/O:

磁盘的I/O性能直接影响应用程序的性能,在一个有频繁读写的应用中,如果磁盘I/O性能得不到满足,就会导致应用停滞。好在现今的磁盘都采用了很多方法来提高I/O性能,比如常见的磁盘RAID技术。现在可以使用RAID技术来提高I/O性能。包括外存类型、传输速率、容量大小。可以使用fdisk等工具查看磁盘使用情况。

 

四、网络宽带

因为现在大多数Linux应用都是基于网络的,所以要建立稳定、高速的带宽。

1、现在的各种应用,一般都是基于网络的,因此网络带宽也是影响性能的一个重要因素,低速的、不稳定的网络将导致网络应用程序的访问阻塞。现在的网络一般都是光纤网络,带宽问题对应用程序性能造成的影响也在逐步降低。无论是磁盘IO还是socket IO,都可以通过IO多路复用或者异步复用的系统管理技术来提高进一步程序执行性能。

2、除此之外,包括网络带宽、网卡性能也可以提高需要通信IO的应用程序的性能。选择Linux系统最匹配的IO类型,也能更进一步去提高带有IO操作的程序的执行性能。

3、可以使用ping、traceroute等命令查看网络情况。

 

4、总结:

通过孟宁老师和李春杰老师这一门课的教学,让我们深入的了解到Linux操作系统的深入基础和底层知识,其中清晰有条理的源码更是深入浅出让我清晰的了解到了Linux内核的运行过程和特点。学完过后,对Linux系统的IO管理、内存管理、文件管理、中断处理等功能有了深刻认识,这也为我以后部署的应用程序,使用对应的分析指令,根据实际情况去执行系统优化提供了很大的帮助

 

posted @ 2021-05-18 16:31  姚金甫  阅读(357)  评论(0)    收藏  举报