操作系统的启动
刚上电时,操作系统在硬盘中。计算机工作原理是不断地取值执行,要取值执行,就要将代码放到内存中。一开始操作系统在硬盘上,无法取值执行。所以第一部分工作就是将操作系统从硬盘上载入到内存里。这些工作由操作系统的引导扇区来完成,也就是bootsect.s,这是一段汇编程序。
bootsect.s:
- 读入setup。
- 在屏幕上打出logo。
- 调用13号中断,读入system。
setup.s:完成OS启动前的设置。获得计算机的硬件信息,形成数据结构来管理这些设备。
int 0x15中断,获取内存大小。为了让操作系统知道内存有多大,才可以去管理。
操作系统挪动到从0地址开始的一系列内存。
mov cr0,ax
,进入保护模式,即从16位(实模式)切换到32位的寻址方式。cr0这个寄存器的最后一位PE如果是0则为16位模式,是1则为32位模式。在保护模式下的地址翻译:
地址翻译:全局描述符表GDT(Global Descriptor Table),在硬件中完成,速度快。
CS不再是左移,而是变为了选择子,存放的是表的下标,表的索引。内存地址=根据CS查表+ip。
jmpi 0,8
,CS=8,ip=0。跳到了head.s
接下来要跳到system模块中执行了,system由许多文件编译而成。第一部分是head.s。
关于汇编:
- as86汇编:能产生16位代码的Intel 8086(386)汇编。
- GNU as汇编:产生32 位代码,使用AT&T系统V语法。
- 内嵌汇编,gcc编译x.c会产生中间结果as汇编文件x.s。
head.s初始化一些表,跳出来到main.c。main.c中引用了mem_init函数,这个函数是用来初始化内存。
以上步骤完成了两件事情:将操作系统读进内存和初始化(读取硬件信息等)操作系统。
操作系统的接口
信号转换,屏蔽细节。
命令行是怎么回事?命令输入后发生了什么?
- 命令是一个用C语言写的程序。
图形按钮是怎么回事?
- 消息框架程序 + 消息处理程序。
操作系统接口:通过c程序连接操作系统和应用软件。例如:fork。
操作系统提供这样的重要函数。这种重要函数就是操作系统接口:接口表现为函数调用,又由系统提供,所以称为系统调用(system_call)。
系统调用的实现
如果可以随意调用的话计算机会很不安全,所以要将内核程序和用户隔离。内存分为内核段和用户段。
硬件提供了主动进入内核的方法:int 0x80中断。
(待补完)
操作系统历史
IBM7094:
- 计算机使用原则:只专注于计算。
- 批处理操作系统(Batch system):一个作业完成,自动读入下一个作业。
- 典型代表:IBSYS监控系统。
OS/360:计算机进入各种行业,一台计算机要应对不同的使用场景。
- 多道程序(multiprogramming):多个任务同时出现,交替进行。
- 作业之间的千幻和调度成为核心。
- 多进程结构和进程管理概念萌芽。
MUTICS:使用人数增加。
- 每人启动一个作业,作业之间快速切换。
- 分时系统(timesharing):分时切换。
- 核心仍然是任务切换,但是资源复用的思想会操作系统影响很大,虚拟内存就是一种复用。
UNIX:简化的MUTICS,核心概念差不多,但更加灵活和成功。1969年贝尔实验室的两个人开发了UNIX。
Linux:1981年推出的IBM PC。开源。
多进程结构是操作系统的基本图谱。
DOS:
- 1975年出现了CP/M。写命令让用户用,执行对应的程序,单任务。
- 1980,在CP/M的基础上开发了QDOS。
MS-DOS:1981,MS-DOS。
Windows:文件,开发环境,图形界面对于OS的重要性。
对用户的使用感受更加重视了。
cpu管理的直观想法
- cpu的工作原理:自动的取值执行。
因为IO指令非常耗时(与计算指令相比),在一个程序执行IO指令时,可以执行其他程序。就形成了多道程序交替执行。
- 一个cpu上交替执行多个程序:并发。
怎么做到并发呢?
- 需要记住切换执行时,程序原来的样子。用PCB存储。
进程:进行中的程序。
- 进程有开始,有结束,程序没有。
- 进程会走走停停,走停对程序无意义。
- 进程要记录离开进程时进程的样子,ax,bx。。
多进程图像
操作系统只需要把这些资源记录好、要按照合理的次序推进(分配资源、进行调度),这就是多进程图像。
用户使用计算机就是启动了一堆进程,用户管理计算机就是管理进程。
多进程如何组织和存放进程?
- Process Control Block:用来记录进程信息的数据结构。
- 运行→等待;运行→就绪;就绪→运行。。。
多进程如何交替?
- 启动进程,启动磁盘读写,将进程设为阻塞态,放入等待队列中。启动schedule()切换函数。
- getNext函数(调度函数)在就绪队列中找到下一个启动的线程,switch_to函数切换进程,保存当前线程,执行下一个线程。
多个进程如何相互影响?
- 不同进程有可能会使用同一个内存。
- 解决方法:映射表。这是一种内存管理的方法。不同进程如果要访问同一个地址,可以用映射表把这个地址变成不同的物理地址。
多个进程之间如何合作?
- 在打印工作中,多个进程想要打印,如果不做处理多个进程交替进行打印任务,就会出现问题。
- 解决方法:进程同步。给多个进程需要操作的公共资源上锁。第一个进程上锁,第二个进程想要执行的时候先检查锁,解锁后再执行。
用户级线程
线程:保留了并发的优点,避免了简称切换的代价。一个进程中有多个线程,而这些进程的资源是公用的,使切换速度更快。现在讨论的切换都是线程的切换而不是进程的切换。
create函数同时启动多个线程,线程可以通过yield函数主动切换到第二个线程。
两个线程公用一个栈的话,我们会发现程序的运行顺序会出现问题。为此我们需要给一个线程用一个栈,在调用yield时会切换esp寄存器(用来存放栈顶指针的寄存器,记录在tcb中,一个线程一个tcb)的值。
内核级线程
多核cpu如果想发挥作用,必须支持核心级线程。
这里是多对多的:多个线程和多个核,由操作系统调配。
核心级线程与用户级有什么不同?
- 切换时切换的是两套站栈。包括用户栈和内核栈。
- 这时tcp会记录用户栈与内核栈。
用户栈和内核栈之间的关联:
通过中断进入内核栈,通过IRET返回用户栈。
进入内核时,内核栈中压入对应用户栈的SS和SP(栈地址)、CS和IP(指令地址)。
内核栈中记录了如何找到用户栈的信息。
内核switch_to的五段论:
- 进入中断。
- 中断处理:启动磁盘读或时钟中断,引发切换。
- 找到下个要执行的tcb。
- 根据tcb完成内核栈的切换。
- IRET切换到用户栈。
所以在创建线程时,就需要创建用户栈、内核栈、tcb。