【李治军操作系统笔记】L8-L20
L8 CPU管理的直观想法
多进程图像由操作系统管理CPU引入
CPU怎么工作?
比如说设置PC=50,CPU会根据50发出取指的命令,把这个地址放到地址总线上,内存把内存中50的这个指令再放到地址总线上,传给CPU,之后就会自动的取指执行,给一个初始地址,就会自动累加。管理CPU直接的想法
:设置好PC的初值- IO特别慢,机械设备比电子设备慢,CPU遇到费时的IO操作可以切到别的程序,多道程序交替执行。
并发
:一个CPU上交替的执行多个程序,从而可以提高利用率。不止修改PC的值那么简单,还要记录程序执行到哪里的样子,记录信息。进程
:运行中的程序,所有的不一样都表现在PCB中。进程有开始有结束,程序没有;进程会走走停停,需要记录ax,bx。
L9 多进程图像
-
操作系统只需要把进程记录好、按照合理的次序推进(分配资源、进行调度)
-
多进程图像一直存在于启动开始到关机结束,main中的fork()创建了第一个进程,init()执行了shell(Windows桌面),shell再启动其他进程。
-
多进程如何组织?
PCB+状态+队列
用PCB放到不同队列中,用状态转移推进。
阻塞态:打饭但是饭卡丢了 -
多进程如何交替?
-
交替的三个部分
:队列操作+调度+切换
FIFO:公平但没有考虑进程执行的任务的区别
多进程如何影响?
此时不能用DPL=0,因为只有在操作系统中DPL=0,此时大家都是用户态程序,所以DPL=3,执行的时候CPL也是3,所以可以去访问。但存在问题,多个进程同时存在于内存会错乱地址。所以需要进程管理连带内存形成多进程图像,限制对地址的读写。
多进程如何合作?
核心在于进程同步,合理的推进顺序。
L10 用户级线程
出发点:在进程切换的时候可不可以不切换映射表(程序运行所需的资源,也就是内存),而是切换指令。
线程:保留了并发的优点,避免了进程切换代价。
- 指令的切换
- 映射表上资源的切换
核心是Yield来切换。
每个线程要有自己的线程栈,但在上述yield函数中有个问题,因为jmp 204,然后到204执行,然后B函数调用反弹栈,又到了204执行,204是因为调用yield才压栈。所以要把jmp 204去掉。
问题:用户级线程进入不了内核,如果程序要通过操作系统访问硬件,那么传入到操作系统就要切换进程,从而会发生阻塞,还是实现不了我们的功能,所以就有了核心级线程。
L11 内核级线程
进程必须在内核中,进程要访问内存,分配资源。
MMU:内存管理单元,映射。
并行才能最大发挥多核优势,多进程因为只有一个mmu所以不能并行,但多线程在只有一个mmu的情况下也能并行。
目前用的是多核。多核一定要支持核心级线程。多个线程可以分配到多个核上(访问硬件),而且用的是同一个MMU。多进程并行不起来。
只有用户级线程是不能分配硬件。
用户级线程,每一个程序都有自己的栈,从一个栈到两个栈(TCB切,然后根据TCB切换用户栈)。而核心级线程就是一套栈,从一套栈到两套栈(TCB切换,根据TCB切换一套栈,内核栈和用户栈)。
进入内核的唯一途径是中断
阻塞就意味着要换别的线程去执行, ????为一段能完成第二级返回的代码,一段包含iret即中断返回的代码。
- 线程+进程切换
(对应第4个作业)
申请一段内存作为tcb(Thread Control Block),再申请一段内存作为内核栈,内核栈的东西初始化,然后tcb关联内核栈。
L12 内核级线程实现
在INT 0x80中断执行完毕之后,才进入内核,ss和sp指向用户态栈,把当前的cs和ip压进来。然后执行system_call。
- 切换五段论中的中断入口和中断出口
在sys_fork,sys_write的时候可能会发现已经启动磁盘读写必须等待,可能会引起切换。比较current是否非0,也就是是否阻塞。如果时间片用光了,也要切换。
-
切换五段论中的schedule和中断出口
schedule中next就是找下一个核心级线程的tcb。 -
切换五段论中的switch_to
TSS(task struct segment,任务结构段)的切换方式比较慢
eip ret=??1是int 0x80指令执行完下一句要执行的地方。 -
创建栈
p是这一页的初始地址,这一页内存用来做tcb,0x10是内核数据段(这里用的是内核堆栈段),前两句把tcb和内核栈都创建好了,用户栈可以共享,但是内核不可以共享栈。esp和ss是传下来的参数。
子进程eax=0,而根据该值就可以知道是否为子进程。
if(!fork()) {子进程执行代码}
L13 操作系统的那棵“树”
只有进入内核才能进行内核调度,进入内核的唯一方法就是中断
时钟中断
L14 CPU调度策略
- 将cpu调度分配给哪个进程,要让进程满意:
- 尽快结束任务:任务进入到任务结束周转时间短
- 用户操作尽快响应:响应时间短
- 系统内耗时间少:吞吐量
- FCFS:first come first served
- SJF:短作业优先
- RR:按时间片来轮转调度 round robin。时间片大:响应时间太长;时间片小:吞吐量小
L15 一个实际的schedule函数
-
就绪态进程counter会设为初值,而如果是阻塞态的进程counter一定会变大,执行io完之后进程的优先级上来。
-
counter的作用:
- 时间片。当前进程的counter等于0就切换。
- 优先级。
-
counter对于所有未执行的任务来说一直是增加的,而只有运行的任务的counter才会减少的。
-
counter保证了响应时间的界:假设counter初始值是p,counter最大的界是2p c ( t ) = c ( t − 1 ) / 2 + c 0 c(t)=c(t-1)/2+c_0 c(t)=c(t−1)/2+c0,总会收敛。
-
经过IO以后,counter就会变大;io时间越长,counter越大,照顾了IO进程,变相的照顾了前台进程
-
后台进程一直按照counter轮转,近似了SJF调度
-
每个进程只用维护一个counter变量,简单并且高效
L16 进程同步与信号量
进程同步:各个进程走走停停,配合向前推进。
生产者进程:缓存区满,生产者停
消费者进程:缓存区空,消费者要停
counter代表缓冲区的大小
counter无法体现等待中的量,所以需要信号量来记录更全面的信息,来执行等待和唤醒。
信号量:通过对这个量的访问和修改,让大家有序推进。
P代表需不需要阻塞
V代表消费过程需不需要唤醒
empty代表空闲缓冲区的容量,full代表已经生产的内容个数有多少个。mutex代表互斥量,避免共同去操作共享缓冲区。对于生产者来说,先测试空闲缓冲区是不是为0(是不是满了)。对于消费者来说,先测试一下有没有内容也就是full(如果没有就阻塞)。
L17 信号量临界区保护
问题:当empty=-1代表有一个进程在睡眠,但是如果有两个进程睡眠,但是empty=-1,会出问题。并且同时修改信号量可能造成empty的含义不正确。
- 临界区(critical section)一次只允许一个进程进入的该进程的那一段代码。
保护算法是否合理:
- 互斥进入:如果一个进程在临界区中执行,则其他进程不允许进入
- 有空让进:若干进程要求进入空闲临界区时,应尽快使一进程进入临界区
- 有限等待
进入临界区Peterson算法
多个进程:临界区保护的硬件原子指令法
L18 信号量的代码实现
信号量有两种:
1)有正有负:-n 表示有n个进程在等待这个资源,欠了n个;+n 表示该资源有n个空余;可以用 if来判断是否睡眠,一次只唤醒一个。
2)只有0,和1两种状态:1表示锁住;0表示解锁;用while检查状态,当锁住则睡眠;当被唤醒(唤醒是将整个等待此资源的队列里的所有进程都唤醒,根据schedule去处理优先级),要检查下状态是否被锁住,