1、一个精简的Linux系统概念模型
1.1 Linux系统模型
从大的方面讲,为了保护内核安全,一个 Linux 系统分为两部分,用户空间和内核空间,一般用户程序还有 shell,一些 lib 库运行于用户空间,完整的 Linux 内核运行于内核空间,向下,它管理系统的所有硬件设备;向上,它通过提供系统调用接口,为用户应用程序提供特权操作的接口。
1.2 Linux内核
Linux 内核管理着所有的操作系统资源,主要组件有:系统调用接口、进程管理、内存管理、虚拟文件系统、网络服务和硬件驱动程序。
- 系统调用接口
应用程序不直接与系统资源打交道,当需要这些资源时,用户程序通过系统调用从用户态切换到核心态,并将任务委派给内核执行,在内核中,一般每个系统调用对应一个系统调用的封装例程,函数库再用这些封装例程定义出给程序员调用的 API,这样把系统调用最终封装成方便程序员使用的库函数。
- 进程管理
负责管理 CPU 资源,Linxu 是一个多任务操作系统,通过进程管理实现多个进程的公平调度尽可能提高系统的吞吐量和资源利用率。
在 Linux 内核中,进程是由一个被称为 task_struct 的结构表示的,此结构包含了表示此进程所必需的数据,此外,还包含了大量的其他数据用来统计和维护与其他进程的关系。其中,init_task 为第一个进程(0号进程)的进程描述符结构体变量,它的初始化是通过硬编码方式固定下来的,除此之外,所有其他进程的初始化都是通过 do_fork 复制父进程的方式初始化的。fork 系统调用会创建一个子进程,而exec 则会用新程序代替当前进程上下文。
进程管理的重点是进程的调度。在内核中,这些进程被称为 Thread,代表了单独的处理器虚拟化(线程代码、数据、堆栈和 CPU 寄存器)。进程的调度时机有:1.进程的时间片用完。2.进程状态发生改变。3.高优先级进程从其他状态转换为就绪态或者高优先级进程被创建。4.系统调用或者中断处理退出。
- 内存管理
负责管理内存资源,以便让各个进程可以安全的共享机器的内存资源。另外,内存管理会提供虚拟内存的机制,该机制可以让进程使用多于系统可用 Memory 的内存,不用的内存会通过文件系统保存在外部非易失存储器中,需要使用的时候再取回到内存中。
- 虚拟文件系统
Linux 内核将不同功能的外部设备,例如磁盘、输入输出设备、显示设备等等,抽象为可以通过统一的文件操作接口(open、close、read、write 等)来访问。这就是 Linux 系统 “一切皆是文件“ 的体现。
- 网络服务
负责管理网络资源,提供了通过网络与外部机器通信的能力。
- 硬件驱动程序
负责管理所有外部硬件,提供对这些硬件设备的操作。
1.3 模型验证
考虑一个读文件的程序,在 Linux 系统模型中要经过以下步骤完成:
- 读文件是一个特权操作,用户态运行的程序首先调用 lib 库的读文件函数
- lib 库中的读文件函数触发系统调用,通过 Trap 陷入内核态,同时设置好相应的中断向量号和参数
- 在中断向量表中找到对应的中断处理子程序并执行,这里有函数调用堆栈保证完成后可以回到之前的进程
- 在中断处理子程序中,通过虚拟文件系统读取需要的文件信息并通过内存管理首先将读取到的信息放到内核空间中,然后拷贝到用户空间,另外由于这是一个耗时的IO过程,可能会通过进程管理暂停原进程而去调度执行另外的进程
- 读文件完成,通过中断通知 CPU,进程管理系统调度原来的进程继续执行,系统调用返回,用户程序获得想要的信息
2、分析影响程序性能表现的因素
接下来我们将一个应用程序放入该系统模型中来系统性的梳理影响程序性能表现的因素:
首先应用程序经过预处理、编译、汇编和链接最终成为一个单一可执行文件
程序在系统中以进程的形式执行,可执行文件首先被装载到内存中,然后分配内存资源完成进程的创建,这个过程中影响性能表现的因素有:
- 应用程序本身的规模大小,这直接影响到程序装载的速度
- 进程创建的方式,写时复制(cow)技术在 fork 子进程的时候不直接复制父进程的所有资源,而是允许其读相同的物理页,只有一个进程写物理页时才拷贝内容并分配给子进程,大大提高了效率
进程创建后处于就绪状态,等待分配 CPU 资源后才能执行,这里影响性能表现的因素是:
- 进程管理,进程调度策略,直接影响到处于就绪状态的进程何时以及如何拥有 CPU 资源,Linux 的进程调度策略进过一系列演变, 目前采用 CFS(完全公平调度)算法
进程成功获得执行后,接下来的性能表现则更多地与应用程序本身的代码和它要完成的任务有关:
- 如果应用程序需要频繁的访问系统资源,就需要通过系统调用进入内核态,内核态和用户态的切换对性能表现有很大影响
- 如果是一个 I/O 密集型程序,进行 I/O 操作时系统会挂起当前进程,等待 I/O 操作完成后再调度执行,频繁的进程调度、上下文切换也会影响程序的性能表现
- 有时需要处理外设发来的 I/O 请求,如网络程序,如何响应也会影响性能表现,从一般的操作系统角度来说,有轮询、中断和DMA三种 I/O 方式。在 Linux 中,用得较多的还是中断方式,并且将中断分为了上半部和下半部,上半部处理紧急的事件,比如接收外设传来的数据;而下半部则是可延迟处理的部分,如内核对接收到的外设数据的处理。将下半部从中断处理例程中抽取出来,可以保证较短的中断响应时间
- 程序中是否进行了大量的函数调用,当发生函数调用时,系统要维护每个函数的函数调用堆栈,这也需要性能开销,所以大量的函数调用以及太深的函数嵌套都会影响程序性能
- 程序是否很好的利用了局部性原理来更多地访问高速硬件,在计算机中,访问速度来说 CPU 高速缓存远大于内存远大于外存,而这些设备之间的数据也都是整块交换,应用程序需要很好的利用局部性原理来避免过多的 cache 缺失和缺页异常来提高性能表现
以上是从软件系统角度分析影响应用程序性能表现的因素,除此之外,硬件能力也是影响性能的直接因素,不过这些因素只能通过升级硬件解决:
- CPU 性能,包括核心数、主频、cache 大小等
- 内存性能,包括内存频率、容量大小等
- 外存性能,包括外存类型、传输速率、容量大小等
- 网络性能,包括网络带宽、网卡性能等