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

一、精简的Linux的概念模型

image
Linux可以抽象为上图的概念,最外层为应用程序,应用程序可以通过shell,或者API,来进行系统调用,或者直接系统调用,来帮助自己完成自己的目的,系统调用后则会进入到Linux内核,在内核中完成系统调用后,再一层层返回,最终回应到应用程序层面。

内核实现了操作系统的三大功能,分别为进程管理、内存管理和文件系统

二、进程管理

Linux会用一个数据结构来描述进程,这个数据结构包括进程的状态,进程的pid,父进程等信息,Linux会维护这一个数据结构来实现对进程的状态管理。

而每个进程都由其父进程来创建,最初的父进程由Linux的内核创建。

进程状态

进程状态与操作系统模型不同,Linux进程状态有以下五种:
可运行态:运行态和就绪态的合并,表示进程正在运行或准备运行,Linux 中使用 TASK_RUNNING 宏表示此状态。
浅度睡眠态:进程正在睡眠(被阻塞),等待资源到来是唤醒,也可以通过其他进程信号或时钟中断唤醒,进入运行队列。Linux 使用 TASK_INTERRUPTIBLE 宏表示此状态。
深度睡眠态:其和浅度睡眠基本类似,但有一点就是不可其他进程信号或时钟中断唤醒。Linux 使用TASK_UNINTERRUPTIBLE 宏表示此状态。
暂停状态:进程暂停执行接受某种处理。如正在接受调试的进程处于这种状态,Linux 使用 TASK_STOPPED 宏表示此状态。
僵死状态:进程已经结束但未释放PCB,Linux 使用 TASK_ZOMBIE 宏表示此状态。

他们转换关系如下图
image

进程调度
可以看到进程的状态转换主要由资源和CPU调度来实现,而什么时候进行CPU切换,这就由Linux的进程调度来决定

在上图可以看到调度使用了schedule()函数,这个函数调用的时机有两种,一种是中断处理过程中的进程调度时机,一种是内核线程主动调用

而在进程调度后怎样选择下一个要运行的进程呢,这就是进程调度策略,Linux针对不同的进程分类,采用不同的调度策略,当前Linux对于实时进程采用FIFO(先进先出)或者RR(轮转调度)的调度策略,而对于其他进程,采用CFS(Completely Fair Scheduler)调度器核心思想是完全公平。

进程切换
在执行完进程调度后,即决定了下一个要运行的进程,则需要进程切换,由于上一个进程没有执行完,所以需要保存当时的执行现场(进程上下文),然后把新的进程上下文放到寄存器中,需要保存的信息包括:用户地址空间、控制信息、进程的CPU上下文。

三、内存管理

Linux操作系统采用虚拟内存管理技术,使得每个进程都有各自互不干涉的进程地址空间。进程所能直接操作的地址都为虚拟地址。当进程需要内存时,从内核获得的仅仅是虚拟的内存区域,而不是实际的物理地址,进程并没有获得物理内存。

这种方式使得程序也不能直接操作物理内存,同一由Linux系统接管,这也让系统的安全性大大增加,在用户态上只能访问用户态的地址,而在内核态下才能访问内核地址。

四、文件系统

在Linux中,贯彻着一个概念,一切皆文件,文件类型主要有正规文件、目录文件、符号链接、设备文件、管道文件和套接字,Linux采用了虚拟文件系统VFS来达到支持多种文。件系统格式的目标,VFS是一个软件层,用来处理与Unix标准文件系统相关的所有系统调用,VFS抽象了四种对象类型来描述文件,分别是超级块对象、索引节点对象、文件对象以及目录项对象

五、系统调用

出于安全考虑,用户态不能直接访问内核内存,所以内核向外提供了一系列的系统调用,它为用户态进程与硬件设备进行交互提供了一组接口,它把用户从底层的硬件编程解放出来,提高了系统安全性并使得程序具有可移植性。

image

六、举例验证模型

就假设一个读取文件的例子,假如有一个读文件的应用程序,选择文件打开后,假如是C语言,则会有open()函数和read()函数,我们以read()函数为例子,这个就是公共库函数,在他底层会有一个读的系统调用,然后CPU触发中断,内核就会在硬盘上读出我们所需要的信息,然后返回中断,在系统调用返回后,read()函数则会进一步返回出读出来的内容,应用程序便获得了读内容。

七、影响系统性能的因素

cache的命中率会很大程度上影响系统性能,如下代码

int array[1024][1024]
for(int i = 0 ; i < 100 ; ++i)
	for(int j = 0 ; j < 100; ++j)
		cout<<array[i][j];

for(int i = 0 ; i < 100 ; ++i)
	for(int j = 0 ; j < 100; ++j)
		cout<<array[j][i];

在array[i][j]的输出方式因为可以把后续的元素提前装进cache中,而array[j][i]不行,所以第一个for循环的cache命中率要比第二个for循环高,所以第一个for循环会远远快于第二个for循环

因此cache命中率对Linux系统性能有很大影响,我们在设计程序的时候需要考虑这一点

posted @ 2021-05-16 14:27  小伙伴们JY  阅读(107)  评论(0)    收藏  举报