精简Linux系统概念模型
Linux操作系统主要涉及系统调用、文件管理、内存管理、进程管理、设备驱动程序以及网络管理等。
对于我们的课程,主要还是讲了进程管理、系统调用和文件管理等。对于模型我们也从这几个方面入手。
进程管理
进程结构
想要理解进程管理,首先要清楚操作系统是如何定义和描述一个进程的。在Linux内核中用一个数据结构来代表处理不同的实体,这个数据结构就是通常所说的进程描述符或进程控制块(PCB)。在linux操作系统下这个数据结构就是task_struct结构体,头文件为:
-
#include <sched.h>
每个进程都会被分配一个task_struct结构体,它包含了这个进程的所有信息。这个结构体代码很长很复杂,几个常用的属性如下:
-
thread_info 进程的底层信息
-
mm_struct 指向内存区域描述符的指针
-
fs_struct 当前目录
-
files_struct 指向文件描述符的指针
想了解更多信息,可以去include/linux/sched.h看看代码。
进程状态
操作系统原理中的进程有就绪态、运行态、阻塞态这3种基本状态,在五态模型中,进程分为新建态、终止态,运行态,就绪态,阻塞态。Linux中的实现与上面的状态有一些出入。
Linux中标记进程状态有TASK_RUNNING(对应就绪态和运行态)、tsk->exit_state(对应终止状态)、TASK_INTERRUPTIBLE(对应阻塞态)和TASK_UNINTERRUPTIBLE(对应阻塞态)。
这是因为所谓的就绪态和运行态在Linux中都表现为TASK_RUNNING,而是否在运行取决于它有没有获得CPU的控制权,也就是说这个进程有没有在CPU中实际执行。
对于tsk->exit_state状态的进程,我们称之为僵尸进程。内核会在适当时候处理它,然后释放进程描述符,这样这个进程才彻底消失在系统中。
进程调度
Linux内核通过schedule函数实现进程调度,它会在运行队列中选择一个进程切到CPU上运行。这里有两个重要的问题,即
-
进程调度的时机——也即内核调用schedule函数的时机
-
进程调度的算法——经典的几个算法
文件管理
Linux中一切都是文件,包括硬件设备,这样用户可以采用读写文件的方式实现访问硬件。
为了对各类文件系统进行统一管理,Linux引入了虚拟文件系统,即VFS。为各类文件系统提供了一个统一的操作界面和应用编程接口。
原理也很简单,用户态下有几个系统调用函数,包括read()、write()、open()、close()等,作用类似于字面意义,就是读写打开关闭文件。
然后调用内核中的sys_open()、sys_read()等函数,对包括FAT、设备文件等在内的文件系统操作。可以说VFS是一个软件层,用来处理与Unix标准文件系统相关的所有系统调用。
系统调用
首先讨论一下为什么会有系统调用。
首先Linux操作系统分为用户态和内核态,因为硬件资源有限,为了更好利用这些资源,所以有了这样的划分。内核态下cpu可以访问内存的所有数据,包括外围设备,例如硬盘,网卡,cpu也可以将自己从一个程序切换到另一个程序。用户态下只能受限的访问内存,且不允许访问外围设备,占用cpu的能力被剥夺,cpu资源可以被其他程序获取。系统调用的意义即是为用户态下的进程与硬件设备交互提供了一组接口。
要实现系统调用,我们首先要编写各种各样的系统调用内核处理函数,然后给他们编号。
一般在用户态应用程序执行一个库函数,这个库函数封装了系统调用。库函数向内核传递参数,触发中断int 0x80进入内核态。之后中断处理函数会根据系统调用号调用对应的系统调用内核处理函数。
举例:Linux写文件
1.首先进程调用库函数发起读写文件请求,然后内核检查进程的文件描述符,看看是否在已打开文件列表中。
2.中断处理函数会调用执行sys_open()和sys_read()函数,这样找到该文件的inode。
3.在inode中,通过文件内容偏移量计算出要读取的页。然后找到文件对应的地址空间,在地址空间中查询对应的页缓存是否存在。
4.如果页缓存命中,直接把内容修改更新在页缓存中。如果缺失,产生缺页异常进行处理。
5.一个页缓存的页被修改后标记为脏页,需要回写到磁盘中的文件快。有两种方式:手动调用sync()/fsync()系统调用或者依赖pdflush进程定时把脏页回写到磁盘。
应用程序放入系统模型影响性能的因素
Linux编译程序的速度
程序在Linux系统中要经过预处理、编译、汇编和链接。系统编译程序的速度往往差异较大。
一般我们用make编译程序,但是实际上make并没有将多核处理器的性能发挥到极限。
Linux系统要提供并行编译的功能。
进程调度算法是否合理
程序在系统中表现为进程,系统怎么调度进程同样是影响速度的一大因素。
我们设计调度算法时要考虑其合理性,假设我放入的应用程序是I/O繁忙型,那么一种不利于I/O繁忙型作业的调度算法(如先来先服务)肯定是会影响进程调度的。
比如CFQ算法。Completely Fair Queuing (cfq, 完全公平队列) 在 2.6.18 取代了 Anticipatory scheduler 成为 Linux Kernel 默认的 IO scheduler 。cfq 对每个进程维护一个 IO 队列,各个进程发来的 IO 请求会被 cfq 以轮循方式处理。也就是对每一个 IO 请求都是公平的。这使得 cfq 很适合离散读的应用。
文件系统性能
linux标准文件系统呈现出多种风格。如果我们没有特殊的需要,就可以直接使用普通的Ext2文件系统。如果我们想避免系统崩溃后冗长的文件系统检测,就可以切换到Ext3文件系统。如果我们不得不处理许多的小文件,ReiserFS是最好的选择。
不同的文件系统同样会影响程序的性能。
内存限制
Linux系统采用了物理内存和虚拟内存两种方式,虚拟内存虽然可以缓解物理内存的不足,但是占用过多的虚拟内存,应用程序的性能将明显下降,要保证应用程序的高性能运行,要保证足够的物理内存,避免占用虚拟内存过多的情况。
系统缓存提高程序运行效率
Buffer和Cache的设计目的,是为了提升系统的I/O性能。它们利用内存,充当起慢速磁盘与快速CPU之间的桥梁,可以加速 I/O的访问速度。Buffer和Cache 分别缓存的是对磁盘和文件系统的读写数据。
从写的角度来说,不仅可以优化磁盘和文件的写入,对应用程序也有好处,应用程序可以在数据真正落盘前,就返回去做其他工作。
从读的角度来说,不仅可以提高那些频繁访问数据的读取速度,也降低了频繁I/O对磁盘的压力。