Linux系统概念模型——课程总结
一、Linux操作系统
进程
谈到进程,即程序的一次动态运行,Linux内核需要一种数据结构来管理其运行,方便任务的识别与切换。task_struct是Linux对进程的描述,其内部数据结构非常庞大,其中state是进程状态,stack是堆栈等,具体如图。
其次,任务的运行可能与其他任务协作,或者需要等待外部事件,因此在不同时刻需要不同状态,由此便引入了进程的状态。当使用fork()系统调用来创建一个新进程时,新进程的状态是TASK_RUNNING(就绪态),当调度器选择这个新创建的进程运行时,新创建的进程就切换到运行态,它也是TASK_RUNNING(运行态);调用用户态库函数exit()会陷入内核执行内核函数do_exit(),也就是终止进程,那么会进入TASK_ZOMBIE状态;等待外部事件,即TASK_INTERUPTIBLE。
最后,时间片到时或者任务需要协调,需要进程之间切换。进程的切换则涉及进程上下文,即CPU的一个快照,切换前需要保存,切换回需要重新写入来确保程序的正确运行。进程上下文包含了进程的一些执行信息,比如用户地址空间(程序代码,数据,堆栈信息),控制信息(进程描述符,内核堆栈),相关寄存器的值。最核心即几个关键寄存器的切换,CR3寄存器(保存地址空间 ,数据),ip与sp寄存器,这几个信息会被保存至进程描述符的thread成员变量和内核堆栈中去。
虚拟地址
程序的运行被虚拟成一个个的进程,而进程又需要运行在内存中,而这个内存沙盘即是虚拟地址空间。在32位模式下它是一个4GB的内存地址块。在Linux系统中, 内核进程和用户进程所占的虚拟内存比例是1:3,而Windows系统为2:2。这并不意味着内核使用那么多物理内存,仅表示它可支配这部分地址空间,根据需要将其映射到物理内存。
虚拟地址最终要落盘到物理地址,一般通过页表。页表由操作系统维护并被处理器引用,内核空间在页表中拥有较高特权级,因此用户态程序试图访问这些页时会导致一个缺页。在Linux中,内核空间是持续存在的,并且在所有进程中都映射到同样的物理内存。内核代码和数据总是可寻址,随时准备处理中断和系统调用。与此相反,用户模式地址空间的映射随进程切换的发生而不断变化。
Linux进程在虚拟内存中的标准内存段布局如下图所示:
简单讲,用户进程部分分段存储内容可由栈、堆、BSS段、数据段、代码段组成。 在将应用程序加载到内存空间执行时,操作系统负责代码段、数据段和BSS段的加载,并在内存中为这些段分配空间。栈也由操作系统分配和管理;堆由程序员自己管理,即显式地申请和释放空间。
文件系统
大量信息的存储与处理是计算机应当解决的一个重要问题,信息的组织、存取和保管便成为了文件系统产生的源头。管理文件通常会设置一个数据结构,即文件控制块FCB,在Linux中为inode(包含文件处理的所有信息)。文件控制块的有序集合构成文件目录,每个目录项即是一个文件控制块。给定一个文件名,通过查找文件目录便可找到该文件对应的目录项(即FCB)。为了实现文件目录的管理,通常将文件目录以文件的形式保存在外存空间,这个文件就被称为目录文件,Linux中主要分为以下几类:
要实现操作系统对其它各种不同文件系统的支持,就要将对各种不同文件系统的操作和管理纳入到一个统一的框架中。对用户程序隐去各种不同文件系统的实现细节,为用户程序提供一个统一的、抽象的、虚拟的文件系统界面,这就是所谓的虚拟文件系统VFS。例如,在Linux操作系统中,可以将DOS格式的磁盘或分区,即文件系统,“安装”到系统中,然后用户程序就可以按完全相同的方式访问这些文件,就好像它们也是Ext2格式的文件一样,一般分为三个层次:
VFS的基本思想:引入一个通用文件模型,这个模型能够表示所有支持的文件系统。通用文件模型有下列对象类型组成:
-
超级块对象( superblock object):存放文件系统相关信息:例如文件系统控制块。
-
索引节点对象( inode object):存放具体文件的一般信息:文件控制块/inode〉。
-
文件对象( file object):存放已打开的文件和进程之间交互的信息。
-
目录项对象( dentry object):存放目录项与文件的链接信息。
大部分操作只需要与通用文件模型 中的一些对象打交道,而不需要真正操 作具体的文件系统和文件,因此可以把 VFS看成是一个“通用”的文件系统,在必要时依赖某种具体的文件系统。
中断机制
内核的一个主要功能就是处理硬件外设I/O,因此IO就绪后需要打断CPU此刻运行的任务。而内核的目标就是让中断尽可能快的处理完,尽其所能把更多的处理向后推迟。其中中断又可分为两类,即同步与异步。
同步中断由CPU本身产生,又称为内部中断。这里同步是指中断请求信号与代码指令之间的同步执行,在一条指令执行完毕后,CPU才能进行中断,不能在执行期间。所以也称为异常。异步中断是由外部硬件设备产生,又称为外部中断,与同步中断相反,异步中断可在任何时间产生,包括指令执行期间,所以也被称为中断。
由于中断会打断内核中进程的正常调度运行,所以要求中断服务程序尽可能的短小精悍;但是在实际系统中,当中断到来时,要完成工作往往进行大量的耗时处理。因此期望让中断处理程序运行得快,并想让它完成的工作量多,这两个目标相互制约,诞生——顶/底半部机制。 中断处理程序是顶半部——接受中断,它就立即开始执行,但只有做严格时限的工作。能够被允许稍后完成的工作会推迟到底半部去,此后,在合适的时机,底半部会被开终端执行。顶半部简单快速,执行时禁止一些或者全部中断。 底半部稍后执行,而且执行期间可以响应所有的中断。这种设计可以使系统处于中断屏蔽状态的时间尽可能的短,以此来提高系统的响应能力。顶半部只有中断处理程序机制,而底半部的实现有软中断,tasklet和工作队列实现。
二、具体实例
文件的读与写,首先文件的读/写主要是通过系统调用read ( )和write( )完成的。read( )和write( )的服务例程分别是sys_read ( )和sys_write ( )函数。它们都需要三个参数:一个文件描述符fd; 一个包含要传送数据的内存缓冲区地址buf;一个指定应该传送多 少字节数的count。read ( )把数据从文件传送到缓冲区,而 write ( )执行相反的操作;两个系统调用都返回所成功传送的字节数,或者发一个错误信号 并返回-1。读或写操作总是发生在由当前文件指针所指定的文件偏移量处。两个系统调用都通过把所传送 的字节数加到文件指针来更新文件指针。
三、影响应用程序性能表现的因素
1.内存:Linux 使用伙伴系统管理内存分配,以页为单位,如果遇到比页小的对象(如不到 1K),对于用户空间,malloc 通过 brk() 分配;对于内核空间,通过 slab 分配器,可将 slab 看作构建在伙伴系统上的一个缓存。当内存紧张时,系统会通过一系列机制回收内存:回收缓存,如 LRU 算法;回收不常访问的内存,把不常使用的内存通过交换分区直接写到磁盘;杀死进程,通过 OOM(Out of Memory)直接杀死占用大量内存的进程。(内存不足或有大量垃圾回收时影响特别严重)
查看内存:
$ free total used free shared buff/cache available Mem: 4039436 570832 2195068 11972 1273536 3108340 Swap: 1533948 0 1533948
2.CPU:CPU 使用率过高也会影响程序的性能表现
perf top 可实时显示占用 CPU 时钟最多的函数或指令,因此可用来查找热点函数进行优化。
$ perf top Samples: 833 of event 'cpu-clock', Event count (approx.): 97742399 Overhead Shared Object Symbol 7.28% perf [.] 0x00000000001f78a4 4.72% [kernel] [k] vsnprintf 4.32% [kernel] [k] module_get_kallsym 3.65% [kernel] [k] _raw_spin_unlock_irqrestore ...
3.缓存命中率:
安装 cachestat、cachetop工具进行查询优化:
$ cachetop 20:52:44 Buffers MB: 191 / Cached MB: 1009 / Sort: HITS / Order: ascending PID UID CMD HITS MISSES DIRTIES READ_HIT% WRITE_HIT% 1534 root vminfo 1 0 0 100.0% 0.0% 986 messageb dbus-daemon 1 0 0 100.0% 0.0% 5956 root cachetop 3 0 0 100.0% 0.0%
四、结语