- 计算机系统工作的基本原理
- 计算机的三大法宝:存储程序计算机、函数调用堆栈、中断
- 1. 存储程序计算机
- 2. 函数调用堆栈
- 基本汇编指令
- 基本汇编的语法规则:
- 对内存堆栈的操作:
- 函数调用与返回指令:
- 第一步:call XXX
- 第二步:enter XXX
- 第三步:leave XXX
- 3. 中断
- 计算机的三大法宝:存储程序计算机、函数调用堆栈、中断
- Linux内核的启动过程
- Linux系统的一般执行过程
- 进程管理
- 进程概念
- 进程的组成
- 进程控制
- 进程链表
- 进程切换
- 进程创建
- 进程撤销
- 进程调度
- 进程调度算法:
- 调度时机
- Linux进程调度策略
- 内存管理
- Linux文件系统
- Linux中的文件类型
- 虚拟文件系统VFS
- 总结:
Linux系统一般有4个主要部分: 内核、 shell、 文件系统和 应用程序。内核、shell和文件系统一起形成了基本的操作系统结构,它们使得用户可以运行程序、管理文件并使用系统。
内核是操作系统的核心,具有很多最基本功能,它负责管理系统的进程、内存、设备驱动程序、文件和网络系统,决定着系统的性能和稳定性。
Linux 内核由如下几部分组成:内存管理、进程管理、设备驱动程序、文件系统和网络管理等。如图:
- 进程管理:管理最核心的CPU资源,保证各个进程能够合理地利用CPU资源完成自身任务。
- 内存管理:管理内存资源,使得内存被进程安全使用。
- 虚拟文件系统:为方便对外设进行管理,将如硬盘、光盘等外设均抽象为虚拟文件,Linux内核提供诸如open、write、read等接口函数对其统一管理。
- 网络系统:管理涉及网络操作的资源。
- 进程间通信:为进程间交流资源提供渠道。
其中,进程管理为Linux内核最重要的部分,因为在操作系统中,进程是程序运行的动态实体,所有进程的工作开展都要依托于Linux内核的进程调度,其相互关系如下图所示。
最终程序以进程的形式得到CPU资源而运行,当其需要调动下层资源时,会向Linux操作系统内核发出系统调用请求,运行环境从用户态转向内核态,在某个时间节点,完成预期功能,再返回用户态继续运行。
- 用户态:即上层应用程序的活动空间,应用程序的执行必须依托于内核提供的资源,包括CPU资源、存储资源、I/O资源等。在⽤户态,代码能够掌控的范围会受到限制。
- 内核态:所有的指令包括特权指令都可以执⾏。
计算机系统工作的基本原理
计算机的三大法宝:存储程序计算机、函数调用堆栈、中断
1. 存储程序计算机
在这里,我们可以把CPU抽象成⼀个for循环,因为它总是在执⾏next instruction(下⼀条指令),然后从内存⾥取下⼀条指令来执⾏。从这个⻆度来看,内存保存指令和数据,CPU负责解释和执⾏这些指令,它们通过总线连接起来。这⾥揭示了计算机可以⾃动化执⾏程序的原理。
计算机的存储体系中最关键的是内存(Memory),是存储程序计算机模型中两个关键部件之⼀。每个内存单元有⼀个地址(Address),内存地址是从0开始编号的整数,CPU通过地址找到相应的内存单元,取其中的指令或者读写其中的数据。⼀个地址所对应的内存单元不能存很多东⻄,只能存⼀个字节,指令或int、float等多字节的数据保存在内存中要占⽤连续的多个字节的地址,这种情况下指令或数据的地址是它所占内存单元的起始字节的地址。
2. 函数调用堆栈
堆栈是C语言程序运行时必须的一个记录调用路径和参数的空间, 在这个堆栈上保存了函数调用框架, 传递的参数, 保存的返回地址, 函数内部的局部变量等等参数.
基本汇编指令
为了与Linux内核的汇编形式一致,这里采用AT&T汇编。
基本汇编的语法规则:
movl %eax, %edx
把eax的内容放到edx中
movl $0x123, %edx
把0x123的十六进制数值放到edx中,开头有$符号表示这是一个立即数
movl 0x123, %edx
把十六进制0x123内存地址指向的内存中存储的数据放到edx中,开头无$符号表示这是一个地址
movl (%ebx), %edx
把ebx寄存器中存储的数据当作内存地址,把这个内存地址存储的数据放到edx中
movl 4(%ebx), %edx
在上一条内存地址的基础上加立即数4,取出对应地址中存储的数据放入edx中
对内存堆栈的操作:
pushl、popl、subl等。
函数调用与返回指令:
call指令实际做的就是将eip寄存器的当前值压栈并将要调用函数的地址赋给eip。
出于安全方面的原因,eip寄存器不能被程序员直接使用。
ret指令实际做的事就是出栈并将值赋给eip。
一个函数调用动作是通过call
指令来完成的.一个完成的函数调用过程主要分为以下三步:
第一步:call XXX
- 执行call之前
- 执行call时, cs:eip的原来的值指向call指令的下一条指令, 该值被保存到栈顶, 然后cs:eip的值指向XXX的入口地址, 即函数返回时是返回到call指令的下一条指令, 在C语言代码中代表着函数调用代码的 下一条代码的起始.
第二步:enter XXX
- pushl %ebp
- movl %esp, %ebp
- XXX的函数体
第三步:leave XXX
- movel %ebp, %esp
- popl %ebp
- ret
3. 中断
中断最初⽤于避免CPU轮询I/O设备,就绪状态发⽣时让I/O设备主动通过中断信号通知CPU,⼤⼤提⾼了CPU在输⼊输出上的⼯作效率,这就是硬件中断(外部中断)。后来随着中断适⽤范围扩⼤,⽐如解决机器运⾏过程出现的异常情况以及系统调⽤的实现等,这就产⽣了软件中断(内部中断),也称为异常,软件中断⼜分为故障(fault)和陷阱(trap)。
简而言之,在没有中断机制之前,计算机只能⼀个程序⼀个程序地执⾏,也就是批处理,⽽⽆法多个程序并发⼯作。有了中断机制,CPU帮我们做了⼀件事情,就是当⼀个中断信号发⽣时,CPU把当前正在执⾏的进程X的CS:RIP寄存器和RSP寄存器等都压栈到了⼀个叫内核堆栈的地⽅,然后把CS:RIP指向⼀个中断处理程序的⼊⼝,做保存现场的⼯作,然后去执⾏其他进程⽐如Y,等重新回来时再恢复现场,即恢复CS:RIP寄存器和RSP寄存器等到CPU上继续执⾏原进程X。显然中断机制在计算机系统中发挥着关键作⽤。
中断在定义上可以分为传统意义上的中断(硬件中断)和异常, 而中断又分为:
- 可屏蔽中断(Maskableinterrupt)
- 非屏蔽中断(Nonmaskableinterrupt)
异常又分为:
-
处理器探测异常
由CPU执行指令时探测到一个反常条件时产生,如溢出、除0错等.根据发生异常时保存在内核堆栈中的eip的值可以进一步分为:
-
故障(fault)
eip=引起故障的指令的地址. 这种异常通常可以纠正,处理完异常时,该指令被重新执行. 例如缺页异常
-
陷阱(trap)
eip=随后要执行的指令的地址
-
异常终止
eip=???.发生严重的错误。eip值无效,只有强制终止受影响的进程
-
-
编程异常
这类异常通常由编程者发出的特定请求产生,通常由int类指令触发, 例如系统调用
关于中断和异常的处理流程, 又可以分为硬件处理流程以及软件处理流程
Linux内核的启动过程
-
存放Linux内核启动时的初始化代码放在init⽬录中。init⽬录中的main.c源⽂件负责整个Linux内核启动,⽽main.c源⽂件中的start_kernel函数是Linux内核启动过程的起点。此函数会初始化高层内核功能,安装根文件系统,以及启动 init 进程
-
Init 进程:启动各种应用程序(根据相关启动脚本设置)。Init 进程首先进行一系列的硬件初始化,并挂载根文件系统。最后 init 进程会执行用户传递过来的“init=”启动参数执行用户指定的命令,或者执行以下几个进程之一,由内核态变为用户态: execve("/sbin/init",argv_init,envp_init);
Linux系统的一般执行过程
1、正在运行的用户态进程X
2、发生中断,保存当前的CPU上下文到进程X的内核堆栈。加载当前进程内核堆栈的相关信息,跳转到中断处理程序。
3、保存现场,此时完成了中断上下文的切换。进程X从用户态进入了内核态。
4、中断处理过程中调用了schedule函数,其中的switch_to做了关键的进程上下文切换,将当前进程X的内核堆栈切换到进程调度算法选出的Y进程的内核堆栈。并完成EIP等寄存器状态的切换。
5、从进程Y继续执行。
6、恢复现场,要注意这里现在是进程Y的中断处理过程中。
7、从Y的进程内核堆栈中弹出之前完成的压栈内容,此时完成了中断上下文的切换。从进程Y的内核态返回了用户态。
8、继续运行用户态进程Y。
进程管理
进程概念
进程是进程实体 的运行过程,是系统进行资源分配和调度的一 个独立单位
特征:
- 进程控制块(PCB)+程序段+相关的数据段=进程实体
- 动态性
- 并发性
- 独立性
- 异步性
进程的组成
- 进程控制块:进程控制块PCB是名字为task_struct的数据结构,它 称为任务结构体。当一个进程被创建时,系统就为该进程建立一个 task_struct任务结构体。当进程运行结束时,系统撤 消该进程的任务结构体。进程的任务结构体是进程存在的唯一标志。
- 进程上下文:把系统提供给进程的处于动态变化的运行环境总和称为进程上下文。
- 进程栈:linux系统为每个用户进程分配了两个栈:用户栈和内核栈。内核进程只有内核栈,没有用户栈。
- 进程的状态:运行态、可运行态、等待态、暂停态、僵死态。
进程控制
进程链表
为了对给定类型的进程进行有效的搜索,内核维护了几个进程链表。一般进程链表是双向链表。
进程切换
进程切换本质上由两步完成:
- 切换页全局目录以安装一个新的地址空间
- 切换内核态堆栈和硬件上下文,例如:CPU寄存器等
进程创建
linux提供了几个系统调用来创建和终止进程:
fork,vfork,clone来创建新进程
exec执行一个新程序
exit来终止进程
进程撤销
撤销过程分为:
- 进程终止:释放进程占有的大部分资源
- 进程删除:彻底删除进程的所有数据结构
进程调度
进程调度算法:
- 先进先出:总把处理机分配给最先进入就绪队列的进程,一个进程一旦分得处理机便一直执行下去,直到进程完成或阻塞时,才释放处理机。
- 最短CPU运行期优先:从就绪队列中选出一个执行时间最短的进程,分配处理机。
- 最高优先权优先调度算法:总把处理机分配给就绪队列中最高优先级的进程。
- 时间片轮转:进程按照FIFO规则排队,按一定的时间间隔分配处理机。
调度时机
- 进程状态发生变化
- 当前进程时间片用完
- 进程从系统调用返回到用户态
- 中断处理后,进程返回到用户态
Linux进程调度策略
- linux的进程调度是基于优先级的调度。进程的优先级是动态的,避免了进程饥饿。
- linux进程分为普通进程和实时进程,实时进程的优先级高于普通进程。实时进程的优先级范围为1~99
- 普通进程采用普通进程的时间片轮转算法
- 实时进程采用实时进程的先进先出或实时进程的时间片轮转算法
内存管理
在讲进程地址空间时涉及到了内存管理。Linux把进程地址空间分成内核区和用户区两部分。当在内核态申请内存时直接给分配,而进程在用户态申请内存时,只是给了一个新的线性地址空间的一个使用权(打白条),真的要用的时候会产生缺页异常,然后再真的分配。请求调页是一种动态内存分配技术,它把页框的分配推迟到不能再推迟为止。与之相关的分配页框的方式为写时复制:父子进程是共享页框的,当读的时候,不分配新的页框。当写的时候,谁写就给谁分配,两者各自过运行一段时间都就都有了自己的空间。
与进程地址空间有关的全部信息都包含在一个叫做内存描述符的数据结构中,进程只能访问某个有效的线性区,进程的地址空间是线性区链表上所有线性区描述符所规定的集合(通过链表来管理,头部由mmap指向)
Linux文件系统
在LINUX系统中有一个重要的概念:一切都是文件。 其实这是UNIX哲学的一个体现,而Linux是重写UNIX而来,所以这个概念也就传承了下来。在UNIX系统中,把一切资源都看作是文件,包括硬件设备。UNIX系统把每个硬件都看成是一个文件,通常称为设备文件,这样用户就可以用读写文件的方式实现对硬件的访问。这样带来优势也是显而易见的:
UNIX 权限模型也是围绕文件的概念来建立的,所以对设备也就可以同样处理了。
Linux中的文件类型
- 正规文件:指系统所规定的普通格式的文件,包括系统文件、用户文件和库文件等。
- 目录文件:由文件目录构成的一类文件,用来维护文件系统结构和管理普通文件。
- 符号链接文件:是一个短文件,包含了另一个文件的任意路径名。
- 设备文件:包括块设备文件和字符设备文件。在Linux系统中,所有的输入输出设备都被看作文件。
- 管道文件:进行进程间通信的文件。
- 套接字文件:在发送方和接收方各创建一个套接字通信端点以获得tcp服务。
虚拟文件系统VFS
虚拟文件系统(VirtualFileSystem,VFS):隐藏了各种硬件的具体细节,把文件系统操作和不同文件系统的具体实现细节分离了开来,为所有的设备提供了统一的接口,VFS提供了多达数十种不同的文件系统。虚拟文件系统可以分为逻辑文件系统和设备驱动程序。逻辑文件系统指Linux所支持的文件系统,如ext2,fat等,设备驱动程序指为每一种硬件控制器所编写的设备驱动程序模块。
虚拟文件系统(VFS)是 Linux 内核中非常有用的一个方面,因为它为文件系统提供了一个通用的接口抽象。VFS 在 SCI 和内核所支持的文件系统之间提供了一个交换层。即VFS 在用户和文件系统之间提供了一个交换层。
- 是 Linux 内核中的一个软件层,用于给用户空间的程序提供文件系统接口;
- 同时,它也提供了内核中的一个抽象功能,允许不同的文件系统共存。
- 系统中所有的文件系统不但依赖 VFS 共存,而且也依靠 VFS 协同工作。
- 为了能够支持各种实际文件系统,VFS 定义了所有文件系统都支持的基本的、概念上的接口和数据 结构;
- 同时实际文件系统也提供 VFS 所期望的抽象接口和数据结构,将自身的诸如文件、目录等概念在形式 上与VFS的定义保持一致。一个实际的文件系统想要被 Linux 支持,就必须提供一个符合VFS标准 的接口,才能与 VFS 协同工作。实际文件系统在统一的接口和数据结构下隐藏了具体的实现细节,所以在VFS 层和内核的其他部分看来,所有文件系统都是相同的。
总结:
通过学习《Linux操作系统分析》课程,自底向上,深入浅出,并通过结合具体例子进行原理分析,系统的了解了Linux操作系统的基本原理,包括Linux的初始化过程、零号进程的创建、系统调用的原理、进程的创建与切换、中断的原理、设备驱动等等。几乎涵盖了Linux操作系统最主要的功能,但各个部分并非完全独立工作而是互有交织,通过整理归纳所学知识构造概念模型,可以更加系统的认识Linux系统。