操作系统 - 李治军- 学习笔记与核心问题归纳 -(1)CPU管理

发布于:2022-10-16 ⋅ 阅读:(732) ⋅ 点赞:(0)

1进入内核的手段:系统调用

为什么不能直接进入内核:不安全。例如用户名密码都在内核,如果随意进入,不安全

如何保证不能强制jump:硬件设计,DPL寄存器表示目标内存段的特权级,系统初始化时,DPL初始化为0.CPL表示当前特权级CPL<=DPL时,才能执行当前指令.系统调用通过中断处理,CPL在切换段时会随之修改。

系统调用的过程

int0x80通过不同系统调用号执行不同系统调用

将系统调用号置给eax,然后调用int0x80 30:00

int0x80会查idt表(中段描述表),在系统初始化的过程中将DPL置为3,设置中断处理程序入口地址,设置idt表中的段基址。通过查表,DPL被设为3,而CPL是cs的最后两位,自然是0,然后通过中断处理函数入口进入systemcall程序。

接下来systemcall查system_CALL_TABLE根据exa中的系统调用号*函数指针大小+基址调用对应的系统调用。

2.多进程图像

cpu工作流程:取址执行,更新pc

每个进程有一个保存进程信息的数据结构:PCB

main中的fork()开创了第一个进程

子进程fork()= 0

多进程如何组织

多个PCB分为不同状态,分别组织在就绪/等待队列

多进程如何交替?

队列操作+调度+切换

通过schedule完成进程的切换

进程与线程的区别:

进程是CPU资源分配的最小单位,线程是CPU调度的最小单位。不同进程拥有不同的地址空间,而同一个进程中不同的线程共用一套地址空间,共用一套虚拟内存。

进程切换与线程切换的区别:

进程切换只需要切换指令序列,线程切换需要切换指令序列和地址空间

用户级线程与核心级线程的区别?

核心级线程切换需要进入内核,通过schedule函数调度。用户级线程的切换不需要进入内核,通过yield函数调度。

多线程的实现:

如何切换用户级线程?(L10 23:00)

从目标tcb取出目标esp,通过yield切换栈(每个线程独占一个栈)指针(esp寄存器),原线程TCB修改栈指针记录现场

用户栈与内核栈的关联?11:00

通过中断进入内核时,将用户栈的ss,sp压入内核栈,将cs,ip压入内核栈,保存现场

通过iret返回用户栈时,弹栈从而恢复用户栈的ss,sp,cs,ip

如何切换核心级线程?

1中断引发schedule

2通过next找到目标线程的tcb。

3通过schedule切换内核栈指针(esp寄存器)(注意:用户栈的ss,sp,cs,ip已经被封装在了内核栈,内核栈的cs,ip也被封装在了内核栈)

4修改原线程tcb中的内核栈指针记录现场

为什么线程切换不用切pc?

pc已经被压在了栈里,通过切换栈已经完成等了pc的切换。

如何创建线程?

1如何创建用户级线程

1申请两块内存存放tcb和栈

2设置栈起始地址

3将tcb和栈关联

2如何创建核心级线程?

1申请两块内存存放tcb和内核栈

2初始化内核栈

3tcb关联内核栈

多核与多cpu的区别?

多处理器:每个cpu有独立的一套高速缓存和mmu,不同cpu内存映射相互独立

多核:多个cpu共用一套高速缓存和mmu,用一套内存映射

fork

fork的过程:查idt表,修改CPL和DPL,进入内核,根据系统调用号查系统调用表,调用system_fork,调用copy_process,传参父进程的内核栈内容,接下来:

1为pcb申请一页物理内存(getfreepage)

2创建内核栈(参数根据申请到的物理内存基址和偏移量设定),创建用户栈(参数来自父进程)

3将eax置为0

4创建虚拟地址空间ldt

CPU调度策略

评价CPU调度策略的标准

1周转时间

2响应时间

3吞吐量

调度算法

1先入先出,特点:响应时间短,但周转时间大

2短作业优先,特点:周转时间短,但是长作业响应时间长

3时间片轮转调度:特点:大幅缩短响应时间,但是增加了调度次数,吞吐量低,周转时间长

除此之外,根据任务特点设置不同任务队列(前台/后台),分别使用不同的调度算法。同时,为了提高响应时间,所有任务队列都要引入时间片。

Linux0.11的调度算法

L15

过程:将轮询指针置于数组末尾,轮询数组,next即为counter(优先级)最大的就绪态进程。

如果没有就绪态进程有时间片,那么设所有进程的时间片=时间片/2+priority

特点:counter既表示优先级又表示时间片

设置时钟中断,每次counter--。当counter=0时,schedule被调用。

这种算法的优点?

保证了阻塞态进程优先级较高,一旦其进入就绪态,可以较快被调度。

3进程同步与信号量

1为什么是信号量而不是信号?

L16 16:00

当有多个生产者睡眠时,消费者只能唤醒一个生产者(一个信号只能记录缓冲区大小,无法记录多个睡眠中的生产者)

信号量

信号量>0,表示还有多少空闲缓冲区。信号量<0,表示有多少进程被阻塞

信号量的数据结构包含一个阻塞队列和一个value。

P系统调用:如果value<0,表示--之前的value<=0.阻塞。

V系统调用:如果value<=0,表示++之前value<0.唤醒。

40:00

其中,empty表示空闲缓冲区大小,full表示生产出的产品数量

对于生产者,当缓冲区满的时候(空闲缓冲区大小empty=0时),生产者阻塞。

当消费者消费了一次之后(V(empty)),空闲缓冲区增加,生产者wake。

对于消费者,当缓冲区空的时候(生产出的产品数量full=0时),消费者阻塞。

当生产者生产了一件产品后(V(full)),产品数量增加,消费者wake

除此之外,互斥信号量mutex保证了文件同时只有一个进程能访问

为什么要用互斥信号量保护信号量?

  不合适的cpu调度会导致指令执行序列不合适,从而导致共享数据(比如信号量)错误

临界区的设计准则?

1基本原则:互斥性

2有空让进:如果没有进程进入临界区,应尽快安排等待进入临界区的进程进入

3有限等待:不能让等待进入临界区的进程无限等待

进入临界区的尝试

1轮换法 缺点:不满足有空让进

2标记法 缺点:可能造成两个进程相互等待,出现死锁场景

3非对称标记(标记+轮转) 缺点:只支持两个进程

4面包店算法

choosing表示有进程正在取号

5硬件手段阻止调度

缺点:多核情况下影响其它cpu

6硬件原子指令法

testandset是原子的,中途不会检查中断寄存器,不会在执行过程中切出去

缺点:多cpu情况下影响其它cpu

信号量的代码实现(linux0.11)

死锁

死锁产生的原因

1互斥性 2占有且等待 3不可抢占 4循环等待

死锁的处理

1死锁预防:破坏死锁出现的条件

手段:1资源按顺序申请(破坏了循环等待的条件,从而避免死锁)

2一次性申请所有资源(破坏了占有且等待的条件,从而避免死锁)

缺点:造成了资源浪费

2死锁避免:检测每个资源请求,如果造成死锁就拒绝

手段:银行家算法

缺点:时间复杂度高

3死锁检测+恢复:回滚部分进程(优先级低的,占用资源多的),然后调用银行家算法

缺点:回滚比较麻烦

4死锁忽略

最常用

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到