前言
心里一直怀揣着做个自己的操作系统的想法。
操作系统概念
一个操作系统,上能接用户与应用,下能接裸机与虚拟。有下面三大方面的功能:
- 作为系统资源管理者:文件管理、设备管理、存储器管理、处理器管理
- 交互:命令接口、程序接口(系统调用)、GUI
- 虚拟/抽象层
关于系统调用,有设备驱动:设备的请求、释放、启动。
有文件管理:文件的读、写、创建、删除。
有进程控制:进程的创建、撤销、阻塞、唤醒。
有进程通信:进程间的消息传递、信号传递。
有内存管理:完成内存的分配、回收。
简单点讲,操作系统是个将CPU映射成进程、将磁盘映射成文件、将内存映射成地址空间的东西。
Shell
这是层壳,隔离用户与操作系统。作为交互的接口、界面等作用。
最早的操作系统——批处理系统、监控程序
- 单道批处理系统:操作系统本身也是程序,最早的操作系统就是一个监控其他程序运行的程序。
- 多道批处理系统:普通的单道程序处理只允许一个程序从头到尾执行完毕后,才开始另一个程序。而多道批处理运行多道程序交替执行。从而比起单道程序更大程度利用系统资源
中断种类
多道程序并发执行本质:发送中断需要操作系统介入,开展管理工作(用户态转为内核态)
中断分为外中断与内中断。
外中断就是IO信号等。
内中断就是指令中断、强迫中断(软件中断或硬件故障)
内中断或者是陷入(trap)、故障(fault)、终止(abort)。
陷入
陷入trap,或者叫陷阱。陷入指令是汇编指令,在用户程序使用库函数时,库函数内部封装了系统调用System calls
在汇编层面上,在执行系统调用进入内核态前,会执行一种陷入指令(trap指令,访管指令),之后CPU控制权会交给操作系统。然后就会在内核态执行系统调用的特权指令了。
陷入指令是特殊的只能在用户态下执行的指令,并且是特殊的不能再内核态(核心态)下执行的指令。
分时操作系统—— 多用户交互
之前的操作系统不能交互,这就只能写个程序,等执行完才能再操作。
于是分时操作系统CTSS出现了,这个系统会按固定的时间片的分配,循环给不同的用户分配使用时间与系统资源。
用户态与核心态
控制指令中,设计内存、设备、IO等系统资源的操作或是别的特殊操作的时候,这时候使用的就不是普通指令,使用特权指令。特权指令在核心态(kernel mode)又叫管态下使用,普通指令在用户态(User mode)又叫目态下使用。
区分用户态与核心态靠PSW,程序状态字。
内核
内核中执行核心态相关指令。
内核处理的事有
- 时钟管理
- 中断处理
- 原语(不可分割的原子操作,一直执行或者一直不执行):CPU切换、设备驱动
- 系统资源管理:进程管理、存储器管理、设备管理
内核有微内核与大内核。
原语
原语不允许中断,是不可分割的操作,所以叫原子操作。运行在kernel mode下。
进程
在没有线程的情况下,进程是资源分配、调度的基本单位。一个进程控制一个程序,系统可以在不同的进程间切换。
进程有一个头,叫执行上下文(execution context)或进程状态(process state)或进程控制块(process control block)
下面两块中,一个是程序块,存放代码。一个是数据块,存放程序运行的中间数据。
PCB信息
- 进程描述信息:进程标识符PID、用户标识符UID
- 进程控制和管理信息:进程当前状态、进程优先级
- 资源分配清单:程序段指针、数据段指针、键盘、鼠标
- 处理相关信息:各种寄存器值
进程组织方式
进程可以通过链接或者索引组织到一起。
- 链接指的是按进程状态将PCB分为多个队列,OS拥有指向各个队列的指针。
- 索引是建立几张索引表。OS拥有各个索引表的指针。
- 这两个组织方式中的指针都有三种,执行指针(挂一个运行状态的PCB),就绪队列指针(挂一条队列链表,或者挂一张索引表),阻塞队列指针(和就绪的很像)
进程状态转换——五状态模型
进程状态有
运行(Running,处理器√,其他资源√)
就绪(Ready,处理器×,其他资源√)
阻塞(Waiting,处理器×,其他资源×)
创建(New)
终止(Terminated)
- 创建是创建一个PCB,一个PCB就可以当做是一个进程。就绪就是准备了其他资源,然后PCB被放入一个就绪队列。系统从队列中依次执行一个PCB,执行完一次的要么释放掉,要么放入阻塞队列,等待进入下一个就绪。
进程控制
进程控制通过原语/原子操作。因为这时候是不允许中断的。
比如,首先关中断,然后执行原语操作,此时是不允许打断的,之后再开中断。
在这个原语操作中间,就可以执行更新PCB信息:修改进程状态标志、将运行环境保存到PCB中、从PCB中恢复运行环境。
将PCB插入队列和分配/回收资源。
进程通信
进程通信有三个方法,分别是共享存储、消息传递、管道通信。
共享通信
共享通信又有两种,基于数据结构和基于存储区。都是一个道理,是通过共同的存储位置来通信。两个进程对共享空间的访问必须互斥。
消息传递
消息传递分两种,直接和间接。消息包由消息头(发送进程ID,接收进程ID,消息类型)和消息体组成。
直接通信是把自己进程生成一个消息包,然后把这个消息包挂到另一个进程的消息队列上。等待对方读取。
间接通信是把消息包放到一个共享空间中,随便放。然后另一个进程自己去找就行。
管道通信
管道通信是用于连接读写进程的共享文件,是固定大小的缓存区。管道通信和共享存储不同的是,只允许单方向的通信。同样,这个读写也是互斥的。
管道通信有下面三个约定:
- 一边的进程写满了,另一边的进程才能读
- 一边的进程读完了,另一边的进程才能写
- 因为读了的内容会被清除,所以一个管道只能有一个进程读
线程
一个进程可以拥有多个线程,线程可以理解为轻量化进程。线程也有ID,也有线程控制块TCB。
有了线程后,进程成为了资源分配的基本单位,线程是调度的基本单位。传统进程间并发,需要切换进程运行环境时,系统资源开销大。引入线程后,同一进程内线程的切换,不需要切换运行环境,系统资源开销小。
- 用户级线程 User-level Thread
由应用程序通过线程库实现。所有的线程管理工作都由应用程序负责。在用户态下完成,无需操作系统干预。 - 内核级线程 Kernel-level Thread
内核级线程的管理由操作系统的内核完成,线程调度、切换等工作由内核负责,因此内核级线程的切换必然需要在内核态下才能完成。
- 多线程中,往往就是将内核级线程与用户级线程混在一起用的。