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

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

一、Linux概念模型

  Linux体系结构可以分为两块:a.用户空间:用户空间中包含了,用户的应用程序,C库 b.内核空间:内核空间包括系统调用,内核,以及与平台架构相关的代码Linux系统的核心是内核。内核控制着计算机系统上的所有硬件和软件,在必要时分配硬件,并根据需要执行软件。

  内核是操作系统的核心,具有很多最基本功能,它负责管理系统的进程、内存、设备驱动程序、文件和网络系统,决定着系统的性能和稳定性。

  Linux概念模型如图:

Linux概念模型

Linux操作系统最核心的内容即是“进程管理”功能。为了实现这一核心功能,这就引入了“三大法宝”和“两把宝剑”。三大法宝:存储程序计算器、中断、函数调用栈;两把宝剑:中断上下文和进程中断上下文。

Linux 内核由如下几部分组成:进程管理、内存管理、设备驱动程序、文件系统和网络管理等。

 

1.1 进程管理

进程管理用来负责控制进程对 CPU 资源的使用。所采取的调度策略是各进程能够公平合理地访问 CPU,同时保证内核能及时地执行硬件操作。数据结构struct task_struct是描述进程的,保存了程序执行过程中的一些必要信息,如进程的id,进程的状态,堆栈等。进程主要分为进程的创建、进程的切换、进程的调度算法和进程的状态。

 (1)进程描述和创建:

进程结构体struct task_struct如图:

  创建一个进程是复制当前进程的信息,就是通过fork系统调用_do_fork函数来创建了一个新进程。因为父进程和子进程的绝大部分信息是完全一样的,但是有些信息是不能一样的,比如 pid 的值和内核堆栈。还有将新进程链接到各种链表中,要保存进程执行到哪个位置,有一个thread数据结构记录进程执行上下文的关键信息。

(2)进程切换

  在实际代码中,每个进程切换基本由两个步骤组成。切换页全局目录(CR3)以安装一个新的地址空间,这样不同进程的虚拟地址如0x8048400(32位x86)就会经过不同的页表转换为不同的物理地址。切换内核态堆栈和进程的CPU上下文,因为进程的CPU上下文提供了内核执行新进程所需要的所有信息,包含所有CPU寄存器状态。

(3)进程调度的策略

  Linux系统中常用的几种调度策略为SCHED_NORMAL、SCHED_FIFO、SCHED_RR、SCHED_BATCH。

(4)进程调度的时机

  进程调度时机就是内核调用schedule函数的时机,有如下三种情况:

 

    a. 用户进程上下文中主动调用特定的系统调用进入中断上下文,系统调用返回用户态之前进行进程调度。    
    b. 内核线程或可中断的中断处理程序,执行过程中发生中断进入中断上下文,在中断返回前进行进程调度。  
    c. 内核线程主动调用schedule函数进行进程调度。

 

 

1.2 内存管理

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

内存管理主要有分为如下几个功能:地址映射、虚拟地址管理、物理内存管理、内核空间管理、页面换入换出策略和用户空间内存管理

 

1.3 文件系统

可以说整个Linux系统都是构建在文件系统之上的,Linux支持多种文件系统,包括ext2、ext3、vfat等。Linux采用虚拟虚拟文件系统VFS来达到支持多种文件系统格式的目标。VFS为各类文件系统提供了一个统一的操作界面和编程接口。向上提供了统一的编程接口,向下对各种文件系统进行兼容。

 

1.4 时钟管理

时钟一般分为两个部分:实时时钟和计时器/定时器。
实时时钟: 一般靠电池供电,即使系统断电,也可以维持日期和时间。 实时时钟独立于操作系统,所以也被称为硬件时钟,为整个系统提供一个计时标准。
定时器/计数器: 实时内核需要一个定时器作为系统时钟(或称 OS 时钟),并由实时内核控制系统时钟工作。 一 般情况下,系统时钟的最小粒度是由应用和操作系统的特点决定的。
实时时钟是系统时钟的时间基准,实时内核通过读取实时时钟来初始化系统时钟,此后二者保持同步运行,共同维系系统时间。

当时钟初始化(即tick_init、Time_init)后,系统就会周期性的调用tick_periodic这个函数,该函数的功能为:

    更新自系统启动以来所经过的时间(Jiffies)

    更新时间和日期(RTC)

    确定当前进程的执行时间,考虑是否要抢占

    更新资源使用统计计数

    检查到期的软定时器

 

例子:文件打开关闭过程

文件打开流程:

  1、打开文件即使用 open 函数,它首先通过按名查找,看在高速缓存中有没有查找的 inode,如果有那么对其进行引用并加 1。如果没有将创建新的 vfs的 inode 对象和目录项对象等。 Open 函数调用的系统服务例程是 sys_open 函数,其接受的参数为文件目录和访问的模式。如果文件存在则返回一个文件描述符fd,如果不存在则返回-1.

  2、标准读取文件的方式是通过两个系统调用实现的:read() 和 write()。当应用程序调用 read() 系统调用读取一块数据的时候,如果该块数据已经在内存中了,那么就直接从内存中读出该数据并返回给应用程序;如果该块数据不在内存中,那么数据会被从磁盘上读到页高缓存中去,然后再从页缓存中拷贝到用户地址空间中去。对于写数据操作来说,当一个进程调用了 write() 系统调用往某个文件中写数据的时候,数据会先从用户地址空间拷贝到操作系统内核地址空间的页缓存中去,然后才被写到磁盘上。但是对于这种标准的访问文件的方式来说,在数据被写到页缓存中的时候,write() 系统调用就算执行完成,并不会等数据完全写入到磁盘上。Linux 在这里采用的是我们前边提到的延迟写机制( deferred writes )。

文件关闭流程:

    1、用户程序通过close ( )系统调用关闭打开的文件,该函数接收的参数为要关闭文件的文件描述符。

     2、内核调用sys_close ( )函数。

可以看到,在以文件打开关闭过程的例子中,整体的操作过程,基本与上述的Linux系统概念原型一致,从而验证了我们的系统概念原型的正确性,逻辑自洽性。

 

 

二.应用程序在Linux上的性能分析

2.1 评价应用程序性能的指标

  我们可以用以下几个指标来对程序的性能进行评价

  (1)响应时间。响应时间是指系统对请求做出响应的时间

  (2)吞吐量  是指单位时间内处理请求的数量

  (3)CPU利用率

然后来分析一下可能会对这些指标造成影响的因素

硬件级别:

1、 CPU  CPU的频率决定了处理速度,利用率可以表现程序的多数时间是花费在CPU上还是IO上,和CPU密切相关的另一个影响性能的因素就是指令集。如RISC指令集和CISC指令集,他们的运用场景不同,在特定情景的性能下的表现自然也不尽相同。

2、 内存 内存使用率过高会使得CPU负担过大,过低则说明内存资源并没有被很好的利用起来。

3、缓冲 缓冲作为一种解决速度不匹配的方式 如果利用起来可以得到很客观的速度提升效果

6、磁盘读取速度。该项决定了系统读盘的速度。

软件级别:

1、进程调度算法:

2、程序的代码级别优化。

3、I/O方式

  常见的I/O方式有以下几种:直接程序控制方式,中断驱动控制方式、DMA方式等。

  直接程序控制方式虽然非常简单,不需要太多的硬件支持,但是由于在这种工作模式下,CPU需要一直等待进行轮询,造成CPU利用率不高的问题。而当我们采用了中断控制方式的时候,当且仅当输入或者输出完成一个数据后,才需要CPU花费时间进行中断处理,这又一次应证了中断是Linux操作系统的“三大法宝”之一。

 

 

举例:

1、延时函数,通常使用的延时函数均采用自加的形式:

void delay (void)
     {
unsigned int i;
     for (i=0;i<1000;i++)
     ;
     }
将其改为自减延时函数:
void delay (void)
     {
unsigned int i;
         for (i=1000;i>0;i--)
     ;
     }

 两个函数的延时效果相似,但几乎所有的C编译对后一种函数生成的代码均比前一种代码少1~3个字节,因为几乎所有的MCU均有为0转移的指令,采用后一种方式能够生成这类指令。

 

2、二维数组的操作

for (i = 0; i < 9999; i++)
	{
		for (j = 0; j < 9999;j++)
		{
			printf("%d ", a[i][j]);
		}
		printf("\n");
	}
//将其改为先循环j后循环i
	for (j = 0; j < 9999; j++)
	{
		for (i = 0; i < 9999;i++)
		{
			printf("%d ", a[i][j]);
		}
		printf("\n");
	}

 分析:两段代码实现的功能一样,但一般情况下第一段代码的速度会高于第二段,因为在c语言中数组是连续存储的,程序二就可能要频繁地发生缺页中断,导致频繁的IO操作和页面的换入换出,IO的速度一般是程序运行速度的瓶颈,因为IO的速度远小于CPU的处理速度,同时缓存命中率也大大降低,故第一段代码的速度更快一些

 

三、课程总结

非常感谢孟老师和李老师的认真授课,让我从只会linux的一些基本操作到理解了linux基本内核架构思想,让我对Linux操作系统有了更深的了解,较之前仅会几个Linux命令有了非常大的提高。

 


 
 
posted @ 2021-05-18 19:30  change!!!!!  阅读(196)  评论(0)    收藏  举报