操作系统学习笔记

名词总结

名词概念
PCB进程控制块(PCB Process Control Block),系统中存放、管理和控制进程信息的数据结构称为
PC程序计数器 也就是下一条指令的地址
PID进程ID(Process ID) 进程标识符
TCB线程控制块
FCB文件控制块
PSW程序状态字寄存器,用于存放PC、IR等的信息
IR指令寄存器,存放到当前进行的指令
PID系统进程
POSIX可移植操作系统接口
IPC进程间的通信机制
系统调用指的就是引起内核态和用户态切换的一种方式
半双工半双工和全双工是计算机网络中的概念,意思是通讯同一时间只允许一方发送数据(对讲机)
全双工通信允许两方向上同时传输数据(电话)
P操作属于系统调用,来自荷兰语proveren,即test,检测,代表wait原语,通常使用P(S)代替wait(S)
V操作属于系统调用,来自荷兰语verhogen,即increment,增加,代表原语signal,通常使用V(S)代替signal(S)
用户态一般的操作系统对执行权限进行分级,分别为用保护态和内核态。用户态相较于内核态有较低的执行权限,
很多操作是不被操作系统允许的,从而保证操作系统和计算机的安全。
内核态内核态相当于一个介于硬件与应用之间的层,可以进行硬件的调度、使用,可以执行任何cpu指令,也可以引用任何内存地址,
包括外围设备, 例如硬盘, 网卡,权限等级最高。
用户态内核态切换三种情况下,用户态会转换到内核态,系统调用、程序异常(例如/0,内存资源耗尽等)、来自外围设备的中断
系统调用/程序接口用户程序通过系统调用的方式才能对硬件进行使用,或者说操作系统将使用硬件的接口提供给用户程序
中断中断是操作系统内核程序夺取cpu的唯一途径,或者说用户程序调用内核代码的唯一途径,因为在一般情况下,操作系统会将cpu使用权交给应用程序。
GDT操作系统对应的的段表
LDT每个进程对应的段表,在PCB中的一个表,LDT放在寄存器 LDTR 中 REGISTER
TLB快表寄存器
JVMJava Virtual Machine Java虚拟机 的缩写
secondary缓冲区
CHS磁盘块封装
CPUCPU,Central Processing Unit ,也叫中央处理器

李志军老师网课

P4 操作系统的接口

什么是操作系统的接口?

系统调用。 操作系统提供了一些重要的函数,这就是操作系统的接口,接口表现为函数调用,有由于是系统提供的,所以称为系统调用。

什么是操作系统?

操作系统是执行系统调用的代码

POSIX ( IEEE制定的一个标准族 )

表头POSIX定义表头
任务管理fork创建一个进程
execl运行一个可执行程序
pthread_create创建一个进程
文件系统open打开一个文件或目录
EACCES返回值,表示没有权限
mode_t_st_mode文件头部结构:文件属性

P5 系统调用的实现

CPL (CS)当前的特权级
RPL (DS)
DPL目标特权级
GDP表存放 CS 与 DS
IDP表中断向量表

Q1:用户程序不能随意 jum

用户程序 调用 放在操作系统内存中的东西 时,因为安全性问题,是不能直接访问的,

Q2:分为 内核 / 用户 态,内核 / 用户 段

​ 将内核程序和用户程序隔离,核心态也就是内核态为0,0S服务为 1和2,用户态为 3

每一条指令执行的时候,都有PC,PC由 CS 和 iP,CS包括的有本段程序处于的特权级信息,只有当前特权级大于等于目标特权级,才能执行这条指令,注意数值越小表示特权级越高。

Q3:硬件提供了 主动进入内核的方法

这是用户程序发起调用内核代码的唯一方式, int 中断指令

P6 操作系统的历史

第一个历史:计算机的发展

第一阶段:批处理 无法实现切换与调度。

第二阶段:多道程序交替执行,也就是多进程了,各个作业直接的切换和调度称为核心,既有 IO 调用,又有计算任务。

第三阶段:每个人启动一个作业,分时系统,各个作业之间快速切换,与前一阶段不同的是,定时的来切换,将每个作业占用 OS 的时间相等,核心仍然是任务切换

第四阶段:UNIX 出现了

第五阶段:Linux 出现了,Linux 由 UNIX 改造而来。

第一个历史总结:

  • 多进程结构是操作系统基本图谱
  • 用户通过执行程序使用计算机(吻合冯诺依曼的思想)
  • 作为管理者,操作系统要让多个进程合理推进,就是进程管理
  • 多进程(用户)推进时会有内存复用等问题。

第二段历史:操作系统的发展

第二段历史主要就是 基于文件的图像。

DOS就是命令行窗口

通过文件来使用计算,非常方便,又加上了一些图形化界面,文件就是第二个重要的图像

第二个历史,对用户的使用感觉加倍重视,各种文件、编程环境、图形界面等

如何通过文件存储代码,执行代码,操作屏幕

如何让文件和操作变成图标,点击,或者触碰

任务:

1)掌握、实现操作系统的多进程图谱,多进程图像。覆盖两个部分

  • 1、CPU,怎么从 CPU 出来内存。CPU 管理
  • 2、内存也是一大块,还讲了进程。内存管理

2)掌握、实现操作系统的文件操作视图,文件操作图像

  • 文件是什么?其实文件就是 IO 设备
  • IO 设备主要讲磁盘文件,IO 怎么驱动,磁盘怎么工作,

P7 我们的学习任务

操作系统要管理硬件——CPU管理、内存管理,这两个合起来也就是 进程视图

操作系统套管理设备管理——中断设备管理、磁盘管理,这两个合起来就是 文件视图

用户通过接口进入操作系统,进程的管理,就要用 fork,用 fork 管理CPU进行

内存是怎么管理呢?要通过地址,使用内存与 多进程图像是接在一起的

操作设备为什么就是操作文件呢?设备 对应的是 设备的文件,dev, 操作某个文件,就会操作对应的设备。

操作文件,open 一个普通文件与 open 一个设备文件 分别是怎么展开的

P8 CPU管理的直观想法

IO 工作的特别慢,一个IO指令执行起来非常慢的

P9 多进程图像

  • 1、操作寄存器如何完成切换:
    • 操作系统组织多个进程,用PCB放到不同队列中,用状态转移推进。
  • 2、写调度程序
    • DPL和CPL 是用来保护操作系统的一种机制,只有OS的DPL才等于0,
  • 3、进程同步与合作
  • 4、地址映射:逻辑内存与物理内存不同,
    • 通过映射表来实现,
    • 多进程的地址空间分离,这是内存管理的主要内容。
    • 进程管理和内存管理共同构成了多进程图像

P10 用户级线程

1、进程 = 资源 + 指令序列

  • 资源 :包括寄存器值,PCB,内存映射表

  • 线程: 指令序列

  • 线程保留了并发和交替执行的优点,同时避免了切换进程的代价

  • 实质就是映射表不变,而PC指针改变,体现了分治的思想

2、线程的实用性

  • 多线程的用户交互性好
  • 多线程的资源共享

3、总结:用户级线程切换的核心

  • 一个栈变成两个栈
  • 每个线程拥有自己的栈,自己的TCB
  • 在切换的时候先切换TCB,在切换栈
  • 在创建的时候就是把要切换的PC指针先放到自己的栈中,然后再创建TCB,在将来切回来的时候,首先通过TCB切换到对应的栈中,然后在栈中弹出对应的地址,也就是PC指针。

4、Yield 函数

  • 要求能切出去再切回来
  • esp
  • 线程切换先切TCP,然后再切PC指针,
  • 一个线程一个栈
  • Yield 切出去的时候,需要先保存需要下一步执行的地址,也就是下一跳,为了返回时可以接着往下执行,
  • Yield 切换回来的时候,不需要 jum 到下一跳,也就是就是不用切换PC,因为下一跳的地址已经在切出去的时刻压栈了

5、Create 函数

  • 制造出第一次切换时应该的样子

6、用户级线程

  • 调用Yield函数,自己主动让出cpu,内核看不见用户级线程的TCP,内核只能看见所属进程而看不见用户级线程,
  • 所以一个用户级线程需要等待,内核会切到别的进程上,不会切到该进程下的其他用户级线程!!!

7、内核级线程

  • 访问硬件(比如IO网卡等)相关的,一定要用到内核。
  • 此时TCP在内核中,内核是可以看到TCP的
  • 内核能看见内核级线程,一个线程需要等待,内核会切到所属进程下的其他内核级线程。
  • 内核及线程就叫 Schedule,为了区别与用户级线程的Yield

P11 内核级线程

1、compare

  • 切换进程实际上是切换内核级线程 + 切换资源
  • 核 是由操作系统管理的,只有操作系统才能分配硬件资源,用户级线程不涉及资源的访问
  • 一个系统中这三个都有:用户级线程,核心级线程,进程,这样才能充分分配

2、MMU

  • memory management uint 内存映射表

3、多处理器 VS 多核

SS stack segment register栈段暂存器
SP stack pointer栈指针
ESP extra segment point附加段寄存器指针
EFLAGS你可以当作是保存当前运行状态
  • 只有多核才能用多线程

  • 这里按老师的意思的话 多核CPU是没法让多进程并行执行的… 但是实际上好像并不是这样,我上SO上搜了一下,发现在多核CPU中,应该每个核都有一个MMU(至少在core i7中是这样)

  • 并行才能最大发挥多核优势,多进程因为只有一个mmu的原因不能并发只能并行,但多线程在只有一个mmu的情况下也能并行

  • 几个核执行这个内存单元里的进程里的多线程,再用另外几个核执行另一个内存单元里的多线程

  • 因为多进程MMU不是用的一套

  • 通过查阅资料,老师此处说的多进程不能发挥多核价值,或许有误

  • 1.linux下并未对进程线程分别做抽象,都是利用task_struct来描述具体调度的一个单元

  • 2.也就是说创建进程、线程的时候其实都是调用的clone

  • 3.如果clone传参共享资源则为创建线程反之则为进程

  • 4.所以“多进程在多核上的情况”,其关键就在于MMU是否共享

  • 5.直接给出回答:MMU通常不是共享的。

  • 6.具体参考i7存储系统框图,每个core都有一个MMU

  • 7.也就是说如果有两个进程A B,每个进程下有两个线程A:T1T2 B:T3T4

  • 8.两core单CPU情况下,可以是core1:T1 core2:T3

4、用户级线程到核心级线程的区别

  • 一个栈到一套栈,两个栈到两套栈
  • 用户级线程切换是:先TCB切,然后根据TCB再切换用户栈
  • 核心级线程切换是:先TCB切,然后根据TCB再切换一套栈,这一套栈包括用户栈和内核栈。

5、中断会自动转到内核栈

  • INT 访管中断,对应一个 IRET 刚好是反着的一个过程,弹栈返回到中断前的状态。

  • 按下鼠标,启动磁盘等硬件中断

  • 时钟中断

  • IRET :中断返回,根据中断返回切换到用户栈

  • read 是一个库函数,调用库函数会展开成一段 int 指令

  • 使用call和ret才能自动压弹栈,这里糸统终端就需要手动记录cs,ip

  • 内核栈就是记录糸统中断后恢复状态必须的cs,ip,及用户栈的信息

  • switch_to 仍然是通过 TCB 找到内核栈指针,然后通过 ret 切到内核栈指针,然后通过 ret切到某个内核程序,最后再用 CS : PC 切到用户程序

  • TCB在内核,从线程1中断进内核,在内核从TCB1切换到TCB2,然后进线程2用户态接着执行,所以要先用中断进入内核,才能进场TCB的切换

P12 内核级线程的实现

image-20210524161512913
  • 1、线程1中 从用户栈到内核栈
    • 靠中断,现在以 系统中断为例,也就是 fork,fork本身是创建进程系统调用
  • 2、线程1中 从内核栈切换到 TCB
  • 3、TCB 调度完成从线程1到线程2的切换
  • 4、线程2中从 TCB 切到内核栈
  • 5、线程2中 从内核栈到用户栈

P15 schedule 函数 *

count 保证了响应时间的上界为 2p p为初始值

经过 IO 之后,count就会变大, IO 师姐越长,counter 越大,照顾了IO进程,也就是照顾了前台进程

后台进程一直按照counter轮转,近似了SJF

每个进程只用维护一个 counter 变量,简单高效

COUNT的复用,既作为优先级,又作为时间片,count会改变,优先级是动态的

首先是找到所有就绪态任务的最大counter,大于零则切过去,否则更新所有任务的counter,即右移一位再加priority

然后进入下一次的找最大counter大于零则切否则更新counter,

所以说那些没在就绪态的counter就一直在更新,数学证明出等的时间越长counter越大

等他们变成就绪态了,由于counter大,也就可以优先切过去了

P16 进程同步与信号量

程序和进程关系:

一个进程代表程序的一次执行,程序可以多次执行,也就对应了多个进程

等待是进程同步的核心,需要让进程 走走停停 来保证多进程合作的合理有序

等信号量期间是阻塞的

请求的信号量的时候会把自己阻塞起来,直到获取到信号后再激活变成就绪状态

信号不能满足要求,因为信号只是有或者没有,所以要引入信号量

信号量就是用来记录一些信息量,并根据这个信息来总结是 sleep还是 weakup,其实就是一个整形变量

信号只有两种状态,适用于单进程

信号量可以涵盖更多地信息,适用于多进程

信号量的 P操作和 V操作

信号量是一个结构体

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6VTKCWT4-1622192224112)(C:\Users\lh\AppData\Roaming\Typora\typora-user-images\image-20210526142514074.png)]

P17 信号量临界区保护

验证保护算法是否合理的标准:

  • 互斥进入
  • 有空让进
  • 有限等待

进入临界区的保护方法

  • 1、软件方法

    • 1)轮换法 有空让进效果不好!!!
    • 2)标记法 可能会造成无限制空转等待
    • 3)非对称标记 结合了标记和轮转两种思想
      • 两个进程:Peterson算法
      • 多个进程:面包店算法
  • 2、 硬件方法

    • 关闭中断来关闭调度即可
    • 但是注意,多CPU的时候不好使。这里涉及到多cpu如何schedule
  • 3、 硬件原子指令

    • 其实是用 mutex锁信号量 来保护信号量,为了解决mutex仍然需要保护的问题
      使用硬件级原子操作,不能被打断不能切出去进行调度

应用:

  • 单核系统开关中断
  • 多核系统原子变量自旋锁。

总结:

  • 硬件原子指令法,不须换保护变量,
  • 软件的锁,需要设置一个保护变量,会出现无休止的套娃。

P18 信号量的代码实现 *

P19 死锁处理

死锁即是形成了资源的等待环路,死锁的4个必要条件

  • 1、互斥使用
  • 2、不可抢占
  • 3、请求和保持
  • 4、循环等待

死锁处理方法:

  • 1、死锁预防:破坏死锁出现的条件
    • 1.1 在进程执行之前,一次性申请所有需要的资源,不会占用资源再去申请其他资源
    • 1.2 对资源类型进行排序,自愿申请必须按顺序进行,不会出现环路等待。
    • 总结:引入太多不合理因素
  • 2、死锁避免:当请求出现时,先假装分配,然后用银行家算法,也就是在执行前检测每个资源的请求,如果会造成死锁就拒绝
    • 2.1 银行家算法,缺点是复杂度太高,效率低
    • 总结:每次申请都执行银行家算法 O(mn),效率太低
  • 3、死锁检测+恢复
    • 等出现问题了,有一些进程因为死锁而停住了,再处理,选择一个进程进行回滚,然后再用银行家算法来算是否能找到安全序列,如果不行,再回滚,直到所有程序都能执行。
      但是回滚是个大问题!!!已经写入磁盘,还得退回来,那就很麻烦了。
    • 恢复很不容易,进城造成的改变很难恢复
  • 4、死锁忽略
    • 也就是重启
    • windows,linux个人版 都不做死锁处理,直接忽略,大不了重启就好了,小概率事件,代价可以接受
    • 死锁出现是不确定的低概率事件

P20 内存的使用与分段

一段内存,一段程序,这个程序被分为多个段,有代码段,变量段等,每个段在内存中找到一块空闲区域,每个段将找到地址的初始值记录在对应段的LDT中,然后将LDT表放在PCB中,又因为编译、链接后的装入模块的地址都是从0开始的,即可执行文件指令地址不变,也就是PC指针的初始值都是从0地址开始取值执行,在每次取址的时候都会查PCB中的LDT表,根据LDT表中对应某个段的基址来找对应的物理地址,也就是从程序中的逻辑地址找到对应在内存中的物理地址,然后进行相应的操作。

段可以有:

主程序段、变量集段、函数库段、动态数组(堆)段、栈段

通过 段号+段内偏移 进行取址

不是将整个程序,而是将各个段分别放入内存中,每个段都需要记录其基地址,存在一个表中,该表称为段表,段表保存到PCB中

每个进程对应一个PCB,PCB中有一个与之对应的 LDT段表,某个进程在运行时,会首先把自己的LDT表在寄存器 ldtr (register) 中更新,这个寄存器暂且理解成只有一个,所以每次取址时都是与 ldtr寄存器 中的基址进行相加的运算,所以当需要切换到另外一个进程时,ldtr寄存器也会跟着切换,变成另外一个进程的 LDT段表。

与之前讲过的地址映射表联想起来,这个LDT段表 就是 地址映射表

P21 内存的分区与分页

三步:

  • 第一步:分段 (LDT段表),将一个程序分成多个段落,编译干的事儿
  • 第二步:找到一段空闲分区,需要再内存中划分空闲区
  • 第三步:通过一个表,将映射关系做好,需要通过磁盘读写,把程序载入进来,这是设备驱动的事儿,
  • 这三步完成后就建立了一张映射表,和PCB关联在一起,这是前面讲的进程管理的事情。

这三步完成以后,在以后的运行过程中,就可以通过运行时重定位取址执行,程序执行起来,内存也跟着使用起来了。

物理内存需要分页

程序、用户是需要分段

32位操作系统,也就是有32位地址总线,32位地址总线可以有 2^32,也就是4G的内存空间,

就需要对这4G的内存空间来建立一个虚拟页到物理页的映射转换,

4G的内存空间,没一页是 4kB,那么就有 1M ( 也就是2^20 ) 的页,这个映射表就是这 1M 个页的映射关系

页表的存储也是有一个寄存器,叫 C23 寄存器,这个与段表非常类似,也是存在PCB中的。

通过页来找物理地址这件事,是通过硬件做的,CPU都有一套叫 MMU 的内存管理单元,里面有一套管理分页的电路

只要你把页表的起始地址给 C23 寄存器,寄存器就是硬件,MMU 就可以根据偏移地址等找出对应的物理地址,

一个程序是由多个段组成的,为了避免内存碎片带来的浪费,这每个段不能直接放在内存中,而是要先把内存分页,把这个段打散成多个页来放在内存中

MMU memory manager unit 内存管理单元

P22 多级页表与快表

一个常识:CPU在执行指令的时候,如果是做一些加减乘除类似的运算,那些都是在CPU内部做的,其速度是非常快的,CPU执行指令主要花费的时间不是在运算上,而是花费在通过地址总线来访问内存,这个速度是很慢的,也就是取址是慢的,

问题:

  • 需要页表项 连续
  • 也需要降低也变的存储空间

方法一:只存放用到的页

  • 缺点:页表编号不连续

方法二:多级页表

  • 相当于书的目录的作用。既减轻了存储压力,每个还都连续,空间效率比较高,但是浪费了时间

方法三:多级页表 + 快表

  • 快表相当于一个cache,把频繁使用的地址映射记录下来
  • TLB 是一组相连快速存储,是寄存器
  • 在时间可空间上做到最优化。

目录表项和页号表项都是2^10个

offset是一共有2^12个么,每个offset的大小是4KB么?这块没理解,求解答

页面的大小是有offset所占位数决定,而2的12次方等于4KB

P23 段页结合的实际内存管理

程序分段

  • 分配端、建段表 LDT,段不能分开,是需要合在一起的。

物理内存分页

  • 分配页、建页表

基地址在PCB中,要把程序放入内存,才能开始使用内存

只要做好段表和页表,执行指令时MMU就能自动完成,MMU是用来计算逻辑地址到虚拟地址再到物理地址的转换的,这个过程是由硬件来完成,移位的操作很快的。所以需要先把段表和页表做好。

页段同时存在是重定位

逻辑地址虚拟内存虚拟地址物理地址
cs : ip 段号 : 段内偏移

根据段表,看段号找到基址,再根据偏移,产生出的地址就是虚拟内存的虚拟地址

这个虚拟地址不是真的地址,需要再次经过一个映射,这次的映射支持分页机制,这次根据页号和偏移找到对应的物理地址,

1)子进程的数据段和代码段映射到不同的虚拟内存地址,每个进程分配 64M 虚拟内存地址
2)父进程和子进程共用一套页目录表,因为虚拟地址不同,页目录号号不同,不打架
3)在页目录表中增加对应页框号表,即父进程和子进程中对应的虚拟地址不同,但是不同地址映射到了同一块页框,也即映射到同一块物理内存。
4)然后对子进程设置共享内存段属性为只读,这样在子进程往里写的时候,会发现只读,会修改映射,映射到其他空间!!!
达到对于用户来讲虽然是操作的同一个地址,但其实对应的是物理内存的两块区域,即子进程和父进程的内存区域是分隔的!!!

P24 内存换入-请求调页 swap in

段页同时存在,就需要用到虚拟内存

上层用户是看不到物理地址的,他们看到的就是0-4G的一个空间,(这个是在32位操作系统上虚拟内存的大小)

每个进程可以单独使用这4G内存,就像是单独拥有这4G内存一样,操作系统就是给用户提供了这样一个假象,好像用户可以在0-4G的内存上随意使用这个内存

后面的从虚拟内存到物理地址的映射,用户是全然不知的,操作系统是实现了从虚拟内存到物理内存的映射

物理内存一般是比较小的,有1G,有2G,但是在用户看来都是4G,

用户执行的程序都在磁盘上,需要将磁盘上的指令放在内存上,才能完成执行指令

所有指令都是一句一句执行的,所以当执行完一句后,再次换入换出到内存上,这样就实现了大的虚拟内存映射到了小的物理内存上。

请求的时候才从虚拟内存到物理内存映射,才换入到物理内存

虚拟内存相当于一个大仓库,

P25 内存换出

访问地址发现缺页,产生缺页中断,从磁盘中读取,换入

当从磁盘读入时,发现物理内存不足,也就是页框不足,就要用 clock 算法产生时钟中断,淘汰掉一个页面,写进磁盘

这个过程就是 swap in 和swap out 的swap分区管理

这样通过 内存,时钟中断,缺页中断,磁盘,磁盘管理,磁盘驱动,这样就形成了swap换入换出的一个样子,

而实现换入换出,是为了实现虚拟内存,这个就是实现虚拟内存的核心

实现虚拟内存,是为了实现段页

实现段页,是为了实现操作系统管理内存的思想

管理内存,是为了实现让程序能够载入内存,也就是让程序能够执行起来

让程序能够执行起来,这就是进程

内存管理和进程管理这两个部分是操作系统的核心,

在加上一些设备驱动,文件系统,系统初始化,系统引导,这些加起来就是一套完整的操作系统

P26 IO与显示器

一台计算机,有CPU,有内存,有显示器,有键盘,即使在没有磁盘的情况下,也就可以开始使用了,

I/O就是输入输出,I/O设备就是可以将数据输入到计算机或将计算机数据输出的设备,常见的:鼠标、键盘、音响、显示器、打印机、话筒、摄像头等等。

**I/O控制器:**CPU无法直接控制I/O设备,需要一个电子部件去充当中间人,这个部件就是I/O控制器,CPU控制I/O控制器,I/O控制器控制I/O设备。

假如我们的CPU能够控制I/O设备,那不同的厂商、不同型号的设备,都要对应进行编码,显然是不切实际的,所以CPU要采用通用调度方式调度I/O设备从而需要I/O控制器。

外设的控制器:

显示器的 I/O 控制器:对应显卡

键盘:输入 对应

磁盘:读写

核心是就三个:

  • 1、CPU向外设发出一条 读或写 的指令,也就是
  • 2、外设工作完成的时候,向CPU发出中断
  • 3、CPU处理中断

但是OS为了统一,要想让外设实现驱动需要做三件事:

  • 1、向外设的核心控制器的某些寄存器端口或者是某些存储端口,发出读写指令,所以第一个部分就是 通过 out 向外设发出指令,这个是计算机/CPU使用外设最核心的东西

  • 2、但是要想写出上面的几条核心指令,必须要对硬件非常了解,要知道硬件对应的端口,硬件指令的格式等等,这些细节非常麻烦,因为不一样的外设制造厂商通常造出的设备也不一样,操作系统为了隐蔽这些细节,就做了一个统一的视图,也就是将所有的外设都做成文件,根据文件所对应的文件名,根据文件描述的结构中的信息,决定了走那条路,最终到达某个设备对应的 out 代码,

    所以第二个部分就是把不同设备对应的 out 代码,采用文件的方式,向上封装起来,

  • 3、进行中断处理

  • 1、CPU 通过 out 指令向 外设 发出命令

  • 2、通过文件形成统一的文件视图

  • 3、进行中断处理

对于显示器来说:

从CPU开始out,也就是从用户指令开始,

P27 键盘

按下键盘就相当于中断,

对于键盘来说:

从键盘按下开始讲故事,

将键盘键入的字符 放入 缓冲队列

设备驱动的核心内容:

无非就是 out 、read 等这样的指令

要么是CPU 发出指令,要么是设备发出中断,

无非是最终和文件接在一起

其他的内容就是一些包装。

键盘的回显,和显示器显示一样

键盘将键入的字符放入缓冲区,显示器从缓冲区中读取

一个是 write_q, 显示器

一个read_q,键盘

P28 生磁盘的使用

怎么让磁盘驱动起来,也就是用起来

CPU中的磁盘控制器通过out写一些指令,磁盘收到指令后进行读或者写

磁盘工作完成之后,向CPU发出中断,做一些后续处理

part1:

三步:

移动磁头到相应的磁道上

旋转磁盘到相应的扇区上

最后与内存进行相应的读(磁生电)或者写(电生磁)

磁盘的IO过程:控制器 寻道 旋转 传输

控制器中要包含几个信息

柱面、磁头、扇区、缓存位置

out指令中要指定上面几个信息的具体数值,然后将 out 指令发出去

操作系统为了将整个磁盘抽象起来,把上面的几个信息整合封装起来,用唯一的表示 也就是盘块号 来标识,

这样就可以只需要知道 block号,操作系统就可以自己通过这个 block号 找到具体的位置

这就是使用操作系统的系统,可以将使用硬件变得简单且高效

应用程序通过盘块号来访问磁盘,效率得到了很大的提升

操作系统在使用抽象的时候,也要想办法将使用的过程变得高效,磁盘的寻道时间是相对来说很慢的,寻道时间也就是移动磁头的时间,而其他的启动时间、传输时间都很迅速,不是最主要的时间消耗。

因为相邻盘块通过是连续使用的,为了减少寻道时间,就把相邻盘块放在同一磁道上。

part2 四个抽象

第一个抽象

  • CHS 磁盘块封装

  • 就是抽象出 block, chs 抽象为盘块 block

第二个抽象 核心就是多个磁盘访问请求出现在请求队列中时该怎么办? 其实就是调度问题

  • 也就是 把 block 放入请求队列时产生了冲突该怎么办,也就是磁盘调度算法,实际中通常使用电梯算法

第三个抽象

  • block的集合抽象为文件,根据 FCB 找到 inode ,根据 inode 再找到对应的 block

第四个抽象

  • 文件的组织形式抽象为目录,根据给出的路径 找到 FCB

给我一个路径名,这个路径名就是一个树状结构,我能够得到这个文件的 FCB

根据这个 FCB ,我能得到 inode

根据 inode 和对应的映射,就能找到盘块号

根据盘块就能放到电梯队列上,

根据电梯队列,就能在磁盘中断的时候把对应的 盘块 读出来,算出 CHS,并把 CHS 发给控制器

控制器根据 CHS 就可以驱动马达,控制磁盘读写相应的盘块

最终就是有了路径,就找到了磁盘的某个盘块 的映射。

磁盘调度算法

1、先来先服务 FCFS, FCFS-First Come First Serve

​ 根据进程请求房屋内磁盘的现后顺序进行调度。符合惯性思维,但在很多时候,效果很差。

2、最短寻找时间优先 SSTF,SSTF-Shortest Seek Time First

​ 学过数据结构与算法的话,核心思想就是贪心算法,该算法会优先处理与当前磁头最近的磁道的需求。

3、扫描算法 SCAN

​ 核心思想,只有磁头移动到最外侧磁道的时候才能往内侧移动,移动到最内侧的时候才能向外侧移动。

4、电梯算法 C-SCAN

总结

1、进程得到 盘块号,算出扇区号,这里埋下一个文件管理的伏笔

2、通过扇区号,得到缓冲内存,使用电梯算法,把缓冲内存放到请求队列中,

3、接下来的工作就是硬件干的,就不用占用CPU了,进程就可以 sleep_on 了

4、然后磁盘工作完成后,产生中断,将进程唤醒 weak_up

P29 从生磁盘到文件

实际上我们使用生磁盘,并不是直接通过盘块号,而是通过文件,也就是字符流,文件就是由字符流来标识的

本章节就是讲,从盘块号怎么抽象出 文件,反过来怎么从文件找到盘块号进行使用,进行映射的抽象。操作系统封装了这个映射,并进行维护。

盘块是逻辑的,实际上读取的是扇区,其读取效率交由下层解决,即电梯算法

引入文件后,那就不叫生磁盘了,那就是 cook_disk ,也就是熟磁盘 ,哈哈哈哈好有趣

FCB 记录起始块,文件控制块

映射有很多种:

顺序存储:类似于数组

链式存储:类似于链表

索引结构:inode ( i 对应的就是 index , node 就是相当于节点,一个结构体嘛,比如 FCB 就是一个结构体,通过结构体来查询,所以叫他 inode)

索引就是文件分成不同的物理块存入磁盘,对每个物理块都有一个索引与之对应,需要读写时就通过索引表查询其物理地址进行相关操作。

总结:

从文件找到盘块号,怎么找呢?通过 文件控制块TCB的映射关系,再加上一些小小的计算

P30 文件使用磁盘的实现 *

image-20210528130842851

根据文件名找到 inode·,这个文件名不是但指文件的名字,还包括整个路径,所以是根据 路径名找到 inode

根据 inode 找到盘块号

根据盘块号往电梯队列中放,

根据电梯队列中的盘块号,算出 chs cfs

最后把 是 out 发在磁盘控制器中

最后磁盘控制器驱动马达,电生磁、磁生电,对磁盘产生读或写

P31 目录与文件系统

整个磁盘变成一棵 目录树

chs 抽象为盘块 block,block的集合抽象为文件,文件的组织形式抽象为目录

第四层抽象:整个磁盘被抽象为一个文件系统

给我一个路径名,这个路径名就是一个树状结构,我能够得到这个文件的 FCB

根据这个 FCB ,我能得到 inode

根据 inode 和对应的映射,就能找到盘块号

根据盘块就能放到电梯队列上,

根据电梯队列,就能在磁盘中断的时候把对应的 盘块 读出来,算出 CHS,并把 CHS 发给控制器

控制器根据 CHS 就可以驱动马达,控制磁盘读写相应的盘块

最终就是有了路径,就找到了磁盘的某个盘块 的映射。

P32 目录解析代码的实现 *

操作系统的全图:

操作系统是一个什么东西呢?

是一个管理硬件的软件,硬件比如 CPU、内存,外设等

OS 首先是管理 CPU,这就引入了OS 第一个重要的视图——多进程图像

在程序执行的过程中,为了让程序执行,需要从内存中不断地取址执行取址执行,这就需要把内存使用起来

内存就引入了 分段分页,段页合作,虚拟内存,还有内存的换入换出,这样就可以把程序放进内存中的某个位置,将指令运行起来,内存也使用起来了,

在程序执行时,对于一些 open,read 等文件指令,就是根据文件视图与外设 如磁盘、显示器、键盘等也联系起来了,将各种设备就业使用起来了

OS 的视图有两大视图

多进程视图是核心,多个进程转来转去,实现了 CPU、内存的运转,

然后再加上 文件的open、read等接口,就通过 IO 把各个外设也使用起来了,

多进程视图往往又是和 IO 、内存等并行的往前推进

第二大视图是 文件视图,用来访问 IO 端口,

将两大视图,再加上系统接口,系统启动,这个系统就能自己从磁盘上载入进来,打出 logol,然后在内存中展开,创建多个进程

用户敲命令来启动进程,用户就把 PPT,word、MP3 、后台编译程序 等应用程序工作起来,计算机就解决了各种有待解决的各种问题

完结撒花~

实验没有做,还是很空的。

leetbook 硬核操作系统指南

进程的系统调用

下面是 UNIX 操作系统和 Windows 操作系统系统调用的对比

UNIXWin32说明
forkCreateProcess创建一个新进程
waitpidWaitForSingleObject等待一个进程退出
exitExitProcess终止执行
openCreateFile创建一个文件或打开一个已有的文件
closeCloseHandle关闭文件

线程的系统调用

调用函数功能
thread_create创建新的线程
thread_exit退出线程
thread_join表示一个线程可以等待另一个线程退出
thread_yiel表示允许线程自动放弃 CPU 从而让另一个线程运行

为了使编写可移植线程程序,IEEE 在 IEEE 标准 1003.1c 中定义了线程标准。线程包被定义为 Pthreads

POSIX线程(通常称为pthreads)是一种独立于语言而存在的执行模型,以及并行执行模型

每个工作流程都称为一个线程,可以通过调用POSIX Threads API来实现对这些流程的创建和控制。可以把它理解为线程的标准。

POSIX Threads 的实现在许多类似且符合POSIX的操作系统上可用,例如 FreeBSD、NetBSD、OpenBSD、Linux、macOS、Android、Solaris,它在现有 Windows API 之上实现了pthread。

线程调用描述
pthread_create创建一个新线程
pthread_exit结束调用的线程
pthread_join等待一个特定的线程退出
pthread_yield释放 CPU 来运行另外一个线程
pthread_attr_init创建并初始化一个线程的属性结构
pthread_attr_destory删除一个线程的属性结构
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值