diff --git a/notes/计算机操作系统.md b/notes/计算机操作系统.md index 3af51ab4..da19cbe3 100644 --- a/notes/计算机操作系统.md +++ b/notes/计算机操作系统.md @@ -9,6 +9,7 @@ * [进程与线程](#进程与线程) * [进程状态的切换](#进程状态的切换) * [进程调度算法](#进程调度算法) + * [进程调度算法实现](#进程调度算法实现) * [进程同步](#进程同步) * [经典同步问题](#经典同步问题) * [进程通信](#进程通信) @@ -205,34 +206,338 @@ Linux 的系统调用主要有以下这些: ### 2. 交互式系统 -**优先级调度** +交互式系统有大量的用户交互操作,在该系统中调度算法的目标是快速地进行响应。 -除了可以手动赋予优先权之外,还可以把响应比作为优先权,这种调度方式叫做高响应比优先调度算法。 - -响应比 = (等待时间 + 要求服务时间) / 要求服务时间 = 响应时间 / 要求服务时间 - -这种调度算法主要是为了解决短作业优先调度算法长作业可能会饿死的问题,因为随着等待时间的增长,响应比也会越来越高。 - -**时间片轮转** +**2.1 时间片轮转** 将所有就绪进程按 FCFS 的原则排成一个队列,每次调度时,把 CPU 时间分配给队首进程,该进程可以执行一个时间片。当时间片用完时,由计时器发出时钟中断,调度程序便停止该进程的执行,并将它送往就绪队列的末尾,同时继续把 CPU 时间分配给队首的进程。 时间片轮转算法的效率和时间片的大小有很大关系。因为进程切换都要保存进程的信息并且载入新进程的信息,如果时间片太小,会导致进程切换得太频繁,在进程切换上就会花过多时间。 -**多级反馈队列** +

-

+**2.2 优先级调度** -如果一个进程需要执行 100 个时间片,如果采用轮转调度算法,那么需要交换 100 次。多级队列是为这种需要连续执行多个时间片的进程考虑,它设置了多个队列,每个队列时间片大小都不同,例如 1,2,4,8,..。进程在第一个队列没执行完,就会被移到下一个队列。这种方式下,之前的进程只需要交换 7 次。 +为每个进程分配一个优先级,按优先级进行调度。 + +为了防止低优先级的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级。 + +**2.3 多级反馈队列** + +如果一个进程需要执行 100 个时间片,如果采用时间片轮转调度算法,那么需要交换 100 次。 + +多级队列是为这种需要连续执行多个时间片的进程考虑,它设置了多个队列,每个队列时间片大小都不同,例如 1,2,4,8,..。进程在第一个队列没执行完,就会被移到下一个队列。这种方式下,之前的进程只需要交换 7 次。 每个队列优先权也不同,最上面的优先权最高。因此只有上一个队列没有进程在排队,才能调度当前队列上的进程。 +可以将这种调度算法看成是时间片轮转调度算法和优先级调度算法的结合。 + +

+ ### 3. 实时系统 -实时系统要求一个服务请求在一个确定时间内得到响应。 +实时系统要求一个请求在一个确定时间内得到响应。 分为硬实时和软实时,前者必须满足绝对的截止时间,后者可以容忍一定的超时。 +## 进程调度算法实现 + +以下只是假象系统上的调度算法实现。源代码:[Process-Scheduling](https://github.com/CyC2018/Algorithm/tree/master/Process-Scheduling/src) + +### 1. FCFS + +首先创建进程数据结构: + +```java +public class Process { + private String name; + private long totalTime; + private long remainTime; + private long comeInTime; + + public Process(String name, long totalTime, long comeInTime) { + this.name = name; + this.totalTime = totalTime; + this.remainTime = totalTime; + this.comeInTime = comeInTime; + } + + public void run(long runTime) { + System.out.println("process " + name + " is running..."); + System.out.println("come in time : " + comeInTime); + System.out.println("total time : " + totalTime); + System.out.println("remain time : " + remainTime); + System.out.println(); + remainTime -= runTime; + try { + Thread.sleep(runTime); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public long getTotalTime() { + return totalTime; + } + + public long getRemainTime() { + return remainTime; + } + + public long getComeInTime() { + return comeInTime; + } +} +``` + +接着创建一个进程等待队列数据结构: + +```java +public interface ProcessQueue { + + void add(Process process); + + Process get(); + + boolean isEmpty(); +} +``` + +FCFS 算法使用普通的先进先出队列即可实现该进程等待队列: + +```java +public class FCFSProcessQueue implements ProcessQueue { + + private Queue queue = new LinkedList<>(); + + @Override + public void add(Process process) { + queue.add(process); + } + + @Override + public Process get() { + return queue.poll(); + } + + @Override + public boolean isEmpty() { + return queue.isEmpty(); + } +} +``` + +接下来是调度器的实现,把它设计成可独立运行的 Thread,它对进程等待队列进行轮询,如果有进程的话就从队列中取出一个任务来执行。批处理系统中,调度器调度一个进程都直接让进程执行完毕,也就是给进程运行时间为进程的总时间。 + +```java +public class Scheduler extends Thread { + + protected ProcessQueue processQueue; + + public Scheduler(ProcessQueue processQueue) { + this.processQueue = processQueue; + } +} +``` + +```java +public class BatchScheduler extends Scheduler { + public BatchScheduler(ProcessQueue processQueue) { + super(processQueue); + } + + @Override + public void run() { + while (true) { + if (!processQueue.isEmpty()) { + Process process = processQueue.get(); + process.run(process.getTotalTime()); + } + try { + Thread.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } +} +``` + +使用一个模拟器来模拟实际场景下进程的不定时到达,每 1s 到达一个进程,并且进程的运行时间再 0s \~ 3s 之间,使得多个进程会发生竞争关系。 + +```java +public class ProcessComeEmulator extends Thread { + + private ProcessQueue processQueue; + + public ProcessComeEmulator(ProcessQueue processQueue) { + this.processQueue = processQueue; + } + + @Override + public void run() { + int processId = 0; + while (true) { + System.out.println("process " + processId + " is coming..."); + System.out.println(); + Process process = new Process((processId++) + "", getRandomTime(), System.currentTimeMillis()); + processQueue.add(process); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + private long getRandomTime() { + return (long) (Math.random() * 3000); + } +} +``` + +最后创建一个客户端来运行整个调度程序: + +```java +public class FCFSClient { + public static void main(String[] args) { + ProcessQueue processQueue = new FCFSProcessQueue(); + ProcessComeEmulator processComeEmulator = new ProcessComeEmulator(processQueue); + processComeEmulator.start(); + Scheduler scheduler = new FCFSScheduler(processQueue); + scheduler.start(); + } +} +``` + +执行结果如下: + +```html +process 0 is coming... + +process 0 is running... +come in time : 1528964425691 +total time : 1807 +remain time : 1807 + +process 1 is coming... + +process 1 is running... +come in time : 1528964426691 +total time : 2311 +remain time : 2311 + +process 2 is coming... + +process 3 is coming... + +process 4 is coming... + +process 2 is running... +come in time : 1528964427692 +total time : 2651 +remain time : 2651 + +process 5 is coming... + +process 6 is coming... + +process 3 is running... +come in time : 1528964428692 +total time : 137 +remain time : 137 + +process 4 is running... +come in time : 1528964429692 +total time : 2513 +remain time : 2513 +``` + +### 2. SJF + +与 FCFS 不同的是,SJF 的进程等待队列需要使用优先级队列来实现: + +```java +public class SJFProcessQueue implements ProcessQueue { + + private PriorityQueue processesQueue = new PriorityQueue<>( + (o1, o2) -> (int) (o1.getTotalTime() - o2.getTotalTime())); + + @Override + public void add(Process process) { + processesQueue.add(process); + } + + @Override + public Process get() { + return processesQueue.poll(); + } + + @Override + public boolean isEmpty() { + return processesQueue.isEmpty(); + } +} +``` + +运行客户端: + +```java +public class SJFClient { + public static void main(String[] args) { + ProcessQueue processQueue = new SJFProcessQueue(); + ProcessComeEmulator processComeEmulator = new ProcessComeEmulator(processQueue); + processComeEmulator.start(); + Scheduler scheduler = new BatchScheduler(processQueue); + scheduler.start(); + } +} +``` + +```java +process 0 is coming... + +process 0 is running... +come in time : 1528964250005 +total time : 2496 +remain time : 2496 + +process 1 is coming... + +process 2 is coming... + +process 1 is running... +come in time : 1528964251006 +total time : 903 +remain time : 903 + +process 3 is coming... + +process 2 is running... +come in time : 1528964252006 +total time : 1641 +remain time : 1641 + +process 4 is coming... + +process 5 is coming... + +process 4 is running... +come in time : 1528964254007 +total time : 243 +remain time : 243 + +process 5 is running... +come in time : 1528964255007 +total time : 646 +remain time : 646 + +process 3 is running... +come in time : 1528964253006 +total time : 2772 +remain time : 2772 +``` + ## 进程同步 ### 1. 临界区 diff --git a/pics/8c662999-c16c-481c-9f40-1fdba5bc9167.png b/pics/8c662999-c16c-481c-9f40-1fdba5bc9167.png new file mode 100644 index 00000000..ff810e91 Binary files /dev/null and b/pics/8c662999-c16c-481c-9f40-1fdba5bc9167.png differ