一、操作系统的历史和发展
1、多进程操作系统的出现
-
IBM7094 殿堂级别;
-
IBM OS/360——多道程序,多进程结构出现,切换和调度出现,IO和计算任务能够相互交替切换- ,防io;
-
MULTICS——使用人数增加,多个人控制任务,分时系统的出现(对服务器而言尤其重要);
-
UNIX;
-
LINUX;
2、用户使用的便利,图形化界面的出现
在IBM推出PC机后,Dos操作系统也诞生了,然后就是windows的诞生,出现了各种文件、编程环境、图形界面,相应的软件上也有了相应的技术实现
3、为什么要回顾历史:历史使人明智
所有东西都不是一蹴而就的,计算机的诞生和成长的过程能对后来的学习产生启迪
二、学习任务
1、多进程图谱:掌握和实现:CPU是怎么做的,怎么处理内存的
2、文件操作视图:掌握和实现:IO、磁盘和文件
三、CPU管理–多进程综述
1、CPU工作原理,取指执行,而管理就是让CPU做这件事
2、执行一条指令IO指令:计算指令的时间是1000000:1,可以看出IO指令执行特别慢
- 执行1000000条计算指令一条IO指令时又会发生什么?
- 在执行完计算指令后等待IO指令完成再执行下面的指令,从开始执行计算到执行完IO,理论上可以看出一半时间CPU在计算,一半时间是在等待IO执行完,CPU的利用率为50%
这还是执行1000000的利用率 - 所以让CPU多道程序、交替执行——并发,多个程序都放入内存
- 怎么切回去
- 控制切换后,寄存器的值可能被另外一个程序改变,所以要记录,不是改变PC就能够简单地完成切换。
而每个程序有一个存放信息的结构:PCB
3、多进程的组织——PCB+状态+队列
PCB放入不同队列,用状态推进进程
4、多进程如何交替
- 设为阻塞态,放入阻塞队列,切换程序(schedule()切换)
- schedule()函数:找到就绪队列中的PCB(getNext()调度)
- 进程调度的话题比较深刻,在此不作讨论,涉及各种复杂算法
5、多个进程运行时相互影响
多个进程同时放在内存中时,代码段所用的内存地址(虚拟地址?)存在交集,所以要限制相同地址的读写,这里要通过映射表来实现地址空间分离(内存管理的主要内容)
6、多进程如何合作
比如打印任务里有多个程序
- 应用程序提交打印任务
- 打印任务被放进打印队列
- 打印进程从队列中取出任务
- 控制打印机打印
但是在合作的过程中也同样会出现相互影响的问题
核心在于进程同步(合理的推进顺序)
四、线程的出现和用户级线程
1、分治思想:将资源(内存)和指令执行分开——线程的出现:保留了并发的优点,避免了进程切换代价
- 通过指令切换实现线程
一个网页浏览器
一个线程用来从服务器接收数据
一个线程用来显示文本
一个线程用来处理图片
一个线程用来显示图片 - 线程是并发进行的,而不是依次进行,所以在网页缓慢显示的时候可以看到一段文本出来后逐渐显示出了图片,而不是等大型图片加载完了,接连着把其他全部文本图片加载出来
- 看c语言代码的意思是创建了两个函数,这两个函数之间能够进行不断切换,执行后释放内存,而且共用了一个缓存区
- Create()函数和yield函数,这两个函数实现了线程之间的并行调度
2、yield函数详细相关笔记:两个执行序列与一个栈的例子讲解
- 两个线程共用了一个栈时的情况会导致最后弹栈返回另一个线程
- 个人理解是栈是先进后出原则,esp永远指向的是栈顶,所以只要在一个栈中弹栈就是从栈顶开始读取数据,得到的是栈顶的404
- 而实际上需求是返回最初的执行的地方而不是最后一个.
- 根据老师的例子,回顾一下栈的基本知识:
栈遵循先进后出原则
栈的两个重要结论:
(1)栈是从高地址向低地址增长。
(2)栈是自顶向下增长。
这里的自顶向下不是指上下方向,而是指的栈顶,esp指向的永远是栈顶,在操作系统内存中存放顺序应该如下:
1003H 104
1002H 204
1001H 304
1000H 404(栈顶<-esp)
所以两个程序需要两个栈,要不然pop的时候弹出的永远是404
3、yield函数详细相关笔记:从一个栈到两个栈
- 这一次我们吸取教训准备了两个栈来
1001H 104 2001H 304
1000H 204 2000H 404
- 显然要实现栈的切换我们需要将esp的值保存在一个中间数,这样才能进行切换
- TCB结构就是用来保存esp的
- 这个时候执行了两个弹栈,最后返回到了我们想要去的104
- 切换前要把栈保存在TCB内,想要回来的时候,再将保存的栈地址赋值给esp寄存器
3、create函数详细相关笔记:两个线程的样子:两个TCB、两个栈、切换的PC在栈中
- 首先申请一段内存作为TCB
- 再申请一段内存作为栈
- 再在栈中存放初始地址
- 最后栈和TCB管理
4、最后,为什么说到现在都是用户级线程——yield是一个用户程序
如果某个线程进入内核执行后阻塞了,那么可以想象之后内核会去执行其他进程,也不会回到之前的地方执行其他线程