Linux课程总结报告
Linux系统概念模型
从宏观上来看, Linux系统概念模型由内到外分为四部分:内核,shell,系统调用,用户程序。
Linux操作系统内核主要涉及系统调用、文件管理、内存管理、进程管理、设备驱动程序以及网络管理等。而我们的课程主要讲了进程管理、系统调用和文件管理等。我们重点梳理和总结这几部分。
进程管理
进程概念与结构
进程就是程序执行的过程。数据结构struct task_struct是描述进程的(就是通常所说的进程描述符或进程控制块PCB),保存了程序执行过程中的一些必要信息,如进程的id,进程的状态,堆栈等。几个常用的属性如下:
- thread_info 进程的底层信息
- mm_struct 指向内存区域描述符的指针
- fs_struct 当前目录
- files_struct 指向文件描述符的指针
详见include/linux/sched.h。
进程状态
我们可以通过PS命令插眼进程状态。该命令可以确定有哪些进程正在运行和运行的状态、进程是否结束、进程有没有僵死、哪些进程占用了过多的资源等等。总之大部分信息都是可以通过执行该命令得到的。
Linux上进程有5种状态:

他们之间的转换关系如图所示:

进程调度
Linux内核通过schedule函数实现进程调度,它会在运行队列中选择一个进程切到CPU上运行。
其中进程调度的时机可以总结如下:
- 用户进程上下文中主动调用系统调用,进入中断上下文,在系统调用返回用户态前进行进程调度。
- 内核进程或可中断的中断处理程序,执行过程中发生中断进入中断上下文,在中断返回前进行进程调度。
- 内核进程主动调用schedule函数。
进程调度需要一定的调度策略:
- 实时进程:FIFO、RR
- 普通进程:CFS(完全公平调度):基本原理是基于权重的动态优先级调度算法。
系统调用
系统调用概念
计算机的各种硬件资源是有限的,为了更好的管理这些资源,用户进程是不允许直接操作的,所有对这些资源的访问都必须由操作系统控制。为此操作系统为用户态运行的进程与硬件设备之间进行交互提供了一组接口,这组接口就是所谓的系统调用。
系统调用实质上就是函数调用,只不过调用的是系统函数,处于内核态而已。 用户在调用系统调用时会向内核传递一个系统调用号,然后系统调用处理程序通过此号从系统调用表中找到相应的内核函数执行,最后返回。
系统调用过程
应用程序在用户态执行时可能会请求系统调用,比如读写磁盘的I/O操作,这时就需要int 0x80指令。int 0x80指令是一条硬件中断指令,该指令的执行意味着程序将从用户态进入内核态,同时由硬件负责保存用户态的执行现场,并把程序的用户态堆栈转换为内核态堆栈。
Int0x80之后就进入到内核代码了,在内核中首先执行到system_call的位置,pushl %eax的意思是把eax寄存器的值压入程序内核态堆栈,eax中存放着系统调用号,系统调用号标识了不同的系统调用。用户态程序传递给系统调用的参数存放在ebx、ecx、edx等等寄存器中,而ebx、ecx、edx等寄存器的值通过SAVE_ALL宏压入内核堆栈。
系统调用处理完之后由RESTORE_ALL宏把SAVE_ALL压入的寄存器反向弹出。
Iret指令与int 0x80相反,iret从内核态返回到用户态,一次系统调用完成。
整个过程如图:

文件系统
Linux采用了虚拟文件系统(VFS)的形式,实现“一切皆文件”。 向上,对应用层提供一个标准的文件操作接口 (如read()、write()、open()、close()等); 对下,对文件系统提供一个标准的接口,兼容多种文件系统可以方便的移植到Linux上。
VFS主要数据结构如下:
- 超级块(Super Block):超级块对象表示一个文件系统。它存储一个已安装的文件系统的控制信息,包括文件系统名称(比如Ext2)、文件系统的大小和状态、块设备的引用和元数据信息(比如空闲列表等等)。
- 索引结点(Inode):索引结点对象存储了文件的相关元数据信息,例如:文件大小、设备标识符、用户标识符、用户组标识符等等。Inode分为两种:一种是VFS的Inode,一种是具体文件系统的Inode。前者在内存中,后者在磁盘中。
- 目录项(Dentry):引入目录项对象的概念主要是出于方便查找文件的目的。不同于前面的两个对象,目录项对象只存在于内存中,实际对应的是磁盘的目录innode对象。VFS在查找的时候,根据一层一层的目录项找到对应的每个目录项的Inode,那么沿着目录项进行操作就可以找到最终的文件。
- 文件对象(File):文件对象描述的是进程已经打开的文件。因为一个文件可以被多个进程打开,所以一个文件可以存在多个文件对象。
影响应用程序性能表现的因素
程序编译
程序被装载到内存前经过预处理、编译、汇编和链接。系统编译程序的速度往往差异较大。并行编译可以大大提高大型程序的编译速度。
装载和进程创建
可执行文件首先被装载到内存中,然后分配内存资源完成进程的创建,这个过程中影响性能表现的因素有:
-
应用程序本身的规模大小,这直接影响到程序装载的速度;
-
进程创建的方式,写时复制(cow)技术在 fork 子进程的时候不直接复制父进程的所有资源,而是允许其读相同的物理页,只有一个进程写物理页时才拷贝内容并分配给子进程,大大提高了效率。
进程调度
程序的动态表现是进程,因此进程调度的效率会影响程序性能表现。
设计调度算法时要考虑其合理性,Linux 的进程调度策略进过一系列演变, 目前采用 CFS(完全公平调度)算法。
进程执行时
进程开始执行时,需要考虑的因素有如下:
- 内存大小。Linux的虚拟内存虽然可以缓解物理内存的不足,但是占用过多的虚拟内存,应用程序的性能将明显下降;
- 缓存。应用程序如果能充分利用局部性原理,充分利用Cache缓存来读取数据,不仅可以提高那些频繁访问数据的读取速度,也降低了频繁读写对内存的压力。
- 文件系统。如果我们没有特殊的需要,就可以直接使用普通的Ext2文件系统。如果我们想避免系统崩溃后冗长的文件系统检测,就可以切换到Ext3文件系统。如果我们不得不处理许多的小文件,ReiserFS是最好的选择。不同的文件系统同样会影响程序的性能。
- 程序访问系统资源的频率也会影响程序性能,内核态和用户态的频繁切换开销非常大。
- 函数调用。当发生函数调用时,系统要维护每个函数的函数调用堆栈,这也需要性能开销,所以大量的函数调用以及太深的函数嵌套都会影响程序性能。

浙公网安备 33010602011771号