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

一、Linux系统概念模型

  Linux内核维护网站上这样介绍Linux:Linux 包含有一个完善的Unix系统所应该有的功能组件,包含真实的多任务、虚拟内存管理、共享库、按需加载、写时复制机制、严密的存储管理以及多种网络协议栈如IPv4、IPv6。接下来简要介绍一下中断与系统调用、进程管理、内存管理、文件管理、设备驱动程序等Linux系统核心功能。

  1.1 中断与系统调用

  中断就是在计算机执行程序的过程中,由于出现了某些特殊事情,使得CPU暂停对程序的执行,转而去执行处理这一事件的程序。等这些特殊事情处理完之后再回去执行之前的程序。中断可分为外部中断(硬件中断)和内部中断,其中外部中断主要是指在用户进程执行时,硬件中断信号到达,导致进入内核态,执行对应的中断服务程序;内部中断主要是包括故障和陷阱两部分。

  能够发出中断请求的硬件设备控制器都有一条称为 IRQ(Interrupt Request)的输出线。所有的 IRQ 线都与一个中断控制器的输入引脚相连,中断控制器与CPU的INTR引脚相连,各硬件通过此来提出中断服务请求。而每个能够产生中断的设备或者模块都会在内核中注册一个中断处理程序,当产生中断时,根据中断向量找到中断描述符,而中断描述符这个结构中包含中断处理程序,通过此可以执行中断处理程序,在中断处理程序中,首先会保存中断向量号和上下文,之后执行对应的中断服务例程。

  作为软中断的一种重要形式系统调用,可以理解是操作系统为用户提供的一系列操作的接口(API),这些接口提供了对系统硬件设备功能的操作。举个例子,我们最熟悉的 hello world 程序会在屏幕上打印出信息。程序中调用了 printf() 函数,而库函数 printf 本质上是调用了系统调用 write() 函数,实现了终端信息的打印功能。

  1.2 内存管理

  Linux操作系统采用虚拟内存管理技术,使得每个进程都有各自互不干涉的进程地址空间。该空间是块大小为4G的线性虚拟空间,用户所看到和接触到的都是该虚拟地址,无法看到实际的物理内存地址。利用这种虚拟地址不但能起到保护操作系统的效果(用户不能直接访问物理内存),而且更重要的是,用户程序可使用比实际物理内存更大的地址空间。

  物理内存按大小被分成页框,每块物理内存可以被映射为一个或多个虚拟内存页。这块映射关系,由操作系统的页表来保存,页表是有层级的。层级最低的页表,保存实际页面的物理地址,较高层级的页表包含指向低层级页表的物理地址,指向顶级的页表的地址,驻留在寄存器中。当执行地址转换时,先从寄存器获取顶级页表地址,然后依次索引,找到具体页面的物理地址。

  1.3 进程管理

  进程定义:进程是由正文段(代码)、用户数据段(进程直接进行操作的所有数据, 以及进程使用的进程堆栈)和系统数据段(进程的控制信息)组成的一个动态实体。在系统数据段存放着进程控制块PCB,PCB是名为task_struct的数据结构,存放着进程的相关信息。进程创建时建立一个task_struct结构体,进程结束时销毁。Linux在内存中专门开辟了一个空间来存放进程结构体PCB,与进程的内核堆栈一起,放在一个8KB的动态内存中,通过esp就能访问进程描述符。

  进程状态:运行态、就绪态、阻塞态、创建态、终止态。

 

  

  进程调度:先来先服务、短作业优先优先、优先级调度算法(分为抢占式和非抢占式)、多级反馈队列、时间片轮转算法。

  1.4 文件管理

  虚拟文件系统是Linux内核的子系统之一,它为用户程序提供文件和文件系统操作的统一接口,屏蔽不同文件系统的差异和操作细节。借助VFS可以直接使用open()read()write()这样的系统调用操作文件,而无须考虑具体的文件系统和实际的存储介质。

  1.5 设备驱动程序

  设备驱动程序在内核中的角色:它们是一个个独立的“黑盒子”,使某个特定的硬件响应一个定义良好的内部编程接口,这些接口完全隐藏了设备的工作细节。(说白了,驱动程序除了对外提供特定的接口外,任何实现细节对应用程序都是不可见的。)用户的操作通过一组标准化的调用执行,而这些调用独立于特定的驱动程序。驱动程序的任务是把这些标准化调用映射到实际硬件的设备特有操作上。

  1.6 举例验证模型——读写文件

  读文件

  进程调用库函数向内核发起读文件请求;②内核通过检查进程的文件描述符定位到虚拟文件系统的已打开文件列表表项;③调用该文件可用的系统调用函数read(),read()函数通过文件表项链接到目录项模块,根据传入的文件路径,在目录项模块中检索,找到该文件的inode;④在inode中,通过文件内容偏移量计算出要读取的页;⑤通过inode找到文件对应的address_space;⑥在address_space中访问该文件的页缓存树,查找对应的页缓存结点;⑦文件内容读取成功。

  写文件

  前5步和读文件一致,在address_space中查询对应页的页缓存是否存在。⑥如果页缓存命中,直接把文件内容修改更新在页缓存的页中。写文件就结束了。这时候文件修改位于页缓存,并没有写回到磁盘文件中去;⑦如果页缓存缺失,那么产生一个页缺失异常,创建一个页缓存页,同时通过inode找到文件该页的磁盘地址,读取相应的页填充该缓存页。此时缓存页命中,进行第6步;⑧一个页缓存中的页如果被修改,那么会被标记成脏页。脏页需要写回到磁盘中的文件块。

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

  2.1 硬件限制

  CPU的时钟频率、cache的容量、内存的容量、磁盘的存取速度及网络带宽大小等硬件资源都会影响系统的性能。

  2.2 软件因素

  程序自身的实现算法复杂度、使用的数据结构、应用程序的链接方式等都会对程序的运行效率造成影响。

  通过程序进行性能分析

  使用Linux内核中的Perf工具对程序进行分析。

// 局部性原理
int func1(int a[][N]) {
    int sum = 0;
    for(int i = 0; i < N; i++) {
        for(int j = 0; j < N; j++) {
            sum += a[i][j];
        }
    }
    return sum;
}

int func2(int a[][N]) {
    int sum = 0;
    for(int j = 0; j < N; j++) {
        for(int i = 0; i < N; i++) {
            sum += a[i][j];
        }
    }
    return sum;
}

使用gcc编译代码

运行结果

 使用Perf工具进行分析,生成 perf.data 文件

使用perf report(e展开看结果,c关闭)命令查看结果,发现fun2占用CPU时间多于func1

func1 与 func2 汇编代码比较,在执行加法操作时fun2由于不使用局部性原理,会比较耗时。

    

三、总结

  经过这学期的Linux操作系统分析课程的学习,对Linux操作系统的核心组件有了更加深入的认识。这里感谢孟老师、李老师的辛苦付出,通过几次操作性强的实验课,从简单到复杂,从表面到深入地理解了Linux内部运行过程,受益匪浅。

 

posted @ 2021-05-18 16:30  JeffreyGuoYJ  阅读(408)  评论(0)    收藏  举报