基于本人观看学习 哈工大李治军老师主讲的操作系统课程 所做的笔记,仅进行交流分享
特此鸣谢李治军老师,操作系统的神作!
如果本篇笔记帮助到了你,还请点赞 关注 支持一下 ♡><)!!
主页专栏有更多,如有疑问欢迎大家指正讨论,共同进步!
给大家跳段街舞感谢支持!ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ
更多操作系统笔记:【哈工大李治军老师】操作系统笔记专栏汇总
如果有需要markdown或者PDF可以私信我,可以在此基础上自己添加补充笔记(●’◡’●)
b站: 【哈工大】操作系统 李治军(全32讲)
大学MOOC:大学慕课—操作系统—主讲:哈工大李治军
操作系统在管理CPU时引入了一个多进程图像,通过多进程图像操作系统管理CPU
多进程图像是操作系统的核心图像。
- 取指:CPU 从内存中读取指令,并解析指令中的操作码和操作数等信息。
- 执行:根据指令中的操作码和操作数,CPU 进行相应的逻辑或算术运算,并将结果保存到寄存器或内存中。
- 写回:如果结果需要保存到内存中,则将结果写入内存,或者将结果保存到寄存器中供后续指令使用。
- 重复:不断重复上述步骤,直到程序执行结束。
取指:CPU 从内存中读取指令,并解析指令中包含的操作码和操作数等信息。
执行:根据指令中的操作码和操作数,CPU 进行相应的逻辑或算术运算,并将结果保存到寄存器或内存中。
写回:如果结果需要保存到内存中,则将结果写入内存;否则,将结果保存到寄存器中供后续指令使用。
重复:不断重复上述步骤,直到程序执行结束。
设置PC初始值,操作系统会自动取指执行
有IO指令和没有IO指令执行时间之比:
可以看出IO执行特别慢,CPU利用率非常低
如何解决这个问题?
在等待IO过程中,CPU切出去执行其他程序,让CPU充分忙碌起来,提高了利用率
举例单道程序和多道程序CPU利用率对比:
这种多道程序同时出发,交替执行就是并发。
CPU应该工作的样子:
对于切换程序执行,因为ax,bx改变了,只修改寄存器PC不能找到原来程序执行到哪里。
在切出程序后,原来的程序执行到哪里,执行的样子 需要记录下来,切回时才能继续执行
由此看出 运行的程序和静态程序不一样:运行的程序需要记录运行后的时刻
进程:刻画运行中的程序 一个运行中的程序就是一个进程
进程有开始、有结束,程序没有
进程会走走停停,走停对程序无意义
进程需要记录ax,bx,…,程序不用
记录这些信息的数据结构就是:PCB
多个程序同时运行就是多进程,CPU进行管理。
PCB中记录了操作系统所需的,用于描述进程的当前情况以及控制进程运行的全部信息。
操作系统根据PCB来对并发执行的进程进行控制和管理。
从上层用户的角度看:共有三个进程PID:1 PID:2 PID:3
操作系统根据PCB合理推进执行多个进程
启动了的程序就是进程,所以是多个进程推进操作系统只需要把这些进程记录好、要按照合理的次序推进(分配资源、进行调度)这就是多进程图像。
我们每要解决一个任务,计算机都会创建一个进程来执行这个任务。 多进程图像是操作系统的核心图像
windows任务管理器中可以查看多个进程同时执行,操作系统通过管理进程实现对计算机的管理
通过PCB形成一系列数据结构,操作系统推进进程的执行
多进程的组织:PCB+状态+队列
运行->等待; 运行->就绪; 就绪->运行…
进程状态图能给出进程生存期的清晰描述,是认识操作系统进程管理的一个窗口。
挂起:挂起是指操作系统将某个正在运行的进程暂时停止执行,并将其状态保存到外部存储器或内存中,直到以后再次恢复该进程执行。是一种重要的管理和优化技术,它可以减少系统资源的浪费,提高系统的响应速度和可用性。
在进程挂起期间,进程的状态信息被保存到外部存储器或内存中,当操作系统需要重新启动该进程时,可以从保存的状态信息中还原该进程的上下文,继续执行。
主动挂起:是指进程自己向操作系统申请挂起自己的状态,通常是因为当前执行的任务已经完成,但该进程还需要保留下来,以便稍后根据需要进行继续执行。例如暂停操作、文件下载中暂停/继续操作等
被动挂起:是指操作系统强制挂起某个正在执行的进程,因为系统资源已经不足,或者该进程所请求的资源无法获得,此时操作系统会暂时停止该进程执行,以便其他进程可以更好地利用系统资源。
多个进程同时在存在于内存会出现下面的问题
进程1中地址100访问到了同时运行的进程2中的地址100:
解决的办法:
限制对地址100的读写
多进程的地址空间分离: 内存管理的主要内容
为什么说进程管理连带内存管理形成多进程图像?
队列中的进程放入和取出 多个进程交替执行
生产者—消费者实例
共享缓冲区:
两个合作的进程都要修改counter:
counter语义错误:初始counter=5,进行加一减一应该还是五,但经过执行序列得到的counter为4,进程必须合理推进
核心在于进程同步:
读写PCB,OS中最重要的结构,贯穿始终
如何将资源不动而切换指令序列?
线程切换分为指令切换 和 资源切换 分治
进程 = 资源 + 指令执行序列
将资源和指令执行分开一个资源 + 多个指令执行序列
既有多个指令在同时触发交替执行,切换也不用那么复杂:
线程: 保留了并发的优点,避免了进程切换代价
实质就是内存映射表不变而PC指针改变
一个网页浏览器: 同时触发,交替执行:
一个线程用来从服务器接收数据
一个线程用来显示文本
一个线程用来处理图片(如解压缩)
一个线程用来显示图片
下载一段网页——显示文本——再回来下载图片…
这些线程要共享资源吗?
接收数据放在100处,显示时要读… 写入读出缓冲区
所有的文本、图片都显示在一个屏幕上
多线程表达的切换指令序列对进程切换很有价值。
启动多个线程,每个线程执行函数
核心是Yield…
能切换了就知道切换时需要是个什么样子
Create就是要制造出第一次切换时应该的样子
如果继续往下执行跳转到另一个线程,因为两个线程共用了一个栈
每个指令序列中,函数调用应该用自己的栈!!!
TCB任务控制块,包含了线程的信息,如寄存器状态、堆栈指针、优先级、资源占用等
TCB 在任务/线程切换时用于保存当前任务/线程的状态信息,以便后续再次恢复执行
(jmp 204;应该去掉)
ThreadCreate的核心就是用程序做出三样东西:
多个程序出现在内存中执行,在程序执行的过程中调用Yield(),首先切换栈然后再弹栈,实现切换到另外一个线程执行,在Yield()…
如果进程的某个线程进入内核并阻塞
一旦一个线程阻塞,其他线程也将被阻塞,会切换到另一个进程执行
虽然通过用户级线程启动多个序列,但是CPU发生阻塞等待
核心级线程ThreadCreate是系统调用,会进入内核
TCB在内核中,内核中一个线程阻塞,将会切换到另一个线程执行
因此内核级线程的并发性更好
核心级线程完全由操作系统在内核中决定,比用户级线程复杂
和用户级相比,核心级线程有什么不同?
ThreadCreate是系统调用,内核管理TCB,内核负责切换线程
如何让切换成型? ——内核栈,TCB
用户栈是否还要用?执行的代码仍然在用户态,还要进行函数调用
一个栈到一套栈:两个栈到两套栈
用户栈和内核栈是操作系统中两个重要的栈空间,它们分别用于存储用户态和内核态的执行环境
当一个进程从用户态切换到内核态时,需要使用到内核栈。进程从内核态返回用户态时,需要使用到用户栈
·所有中断(时钟、外设、INT指令)都引起上述切换
·中断(硬件)又一次帮助了操作系统…
用户栈和内核栈是操作系统中两个不同的栈区域,它们各自负责存储不同执行环境的栈帧信息
在处理器发生中断时,或者进程主动发起系统调用时,CPU会切换到内核态,并将当前进程的用户态上下文信息保存到它的用户栈中,然后切换到内核栈中继续执行相应的内核代码;当内核完成相应的处理逻辑后,再将内核栈中保存的内容弹出,恢复用户栈中保存的用户态上下文信息,最后切换回用户态继续执行用户进程。
switch to:仍然是通过TCB找到内核栈指针;然后通过ret切到某个内核程序;最后再用CS:PC切到用户程序
switch to也称为上下文切换,当多个任务或线程同时运行时,操作系统需要在它们之间进行快速的切换,实现并发执行
“五段论”保证了线程之间的正确切换和执行:
用户级线程、核心级线程的对比:
用户级线程是由应用程序开发者创建和管理的线程,运行在用户空间,并通过用户程序调用系统调度库来实现线程的调度和同步
每个用户级线程都对应着一个内核级线程,内核级线程以操作系统内核的身份运行并提供线程与系统资源之间的映射
用户级线程实现简单,调度和同步开销小,缺点是不能利用多处理器环境提高性能
核心级线程是由操作系统内核创建和管理的线程,直接由内核调度器进行调度
与用户级线程相比,内核级线程有更好的并发性和CPU利用率,但比较复杂。
内核级线程需要采用复杂的数据结构来维护线程上下文切换和状态信息,并且需要在内核态进行线程切换
核心是让操作系统内核直接管理线程调度,对于上层应用程序来说,无需关心线程的创建、调度和同步等
先从用户栈到内核栈,再从内核栈到用户栈;核心级线程的两套栈,核心是内核栈。
fork() 系统调用可以让程序生成一个新的独立进程,并在原进程和新进程中进行不同的操作
中断入口: _system_call将用户态信息压栈
任务的运行状态如果不在就绪状态就执行调度,如果在就绪状态但counter等于0,也执行调度程序
中断出口:
sys_fork 函数调用了 fork() 系统调用,它会通过 eax 寄存器返回新创建进程的进程 ID,在此之前会先将该寄存器的值入栈保存
接着通过 cmp 判断是否需要进行进程调度,并将返回地址入栈后通过 jmp 指令跳转到 schedule 函数执行进程切换操作
在进程切换结束后,通过 pop 指令将原 eax 寄存器的值恢复回来fork() 调用的返回值
任务状态段Tss(Task State Segment)表示任务状态段的表项,用于获取相应进程的任务结构体的起始地址
跳转到所切换到的进程的任务结构体中保存的代码起始地址,从而实现进程上下文切换的操作:
Linux 0.11用tss切换,但也可以用栈切换,因为tss中的信息可以写到内核栈中
fork()使用 push 指令将寄存器 %gs 和 %eax 的值分别压入栈中
然后并调用_copy_process 函数创建新进程
·在 _copy_process函数中,使用给定的参数构造一个新进程的状态
这些参数全部传入 _copy_process函数设置上下文信息
然后函数返回到 sys_fork 函数并使用 add 指令来从栈中删除参数,最终 ret 返回
调用 get_free_page 函数来动态申请一页大小的内存空间,并转换为一个指向PCB的指针 p
创建TCB——创建内核栈和用户栈——关联栈和TCB 子进程和父进程共享用户栈
fork() 调用成功时,返回值为子进程的 PID 在父进程中。在子进程中,fork() 返回 0。
exec() 是一个系统调用,会执行 system_call
ex.a_entry是可执行程序入口地址,产生可执行文件时写入
前面知识的串联
更多操作系统笔记:【哈工大李治军老师】操作系统笔记专栏汇总
第二章 下:操作系统第二章(下)学习笔记
大家的点赞、收藏、关注将是我更新的最大动力!欢迎留言或私信建议或问题。 大家的支持和反馈对我来说意义重大,我会继续不断努力提供有价值的内容! |