进程调度模拟
进程调度就是在所有可运行的进程之间分配CPU资源,它使得在单个CPU上并发执行多个进程成为可能。本文通过java模拟时间片轮转算法,以具象化进程调度。进程是操作系统中一个重要的抽象,通过进程调度和虚拟内存机制实现了CPU和内存的虚拟化。在每个进程看来,自己是独占CPU和内存的。 下面看一下进程调度的大致原理,每个进程是程序的执行实体,相当于程序执行的容器。在进程执行时,进程有自己
进程调度就是在所有可运行的进程之间分配CPU资源,它使得在单个CPU上并发执行多个进程成为可能。本文通过java模拟时间片轮转算法,以具象化进程调度。进程是操作系统中一个重要的抽象,通过进程调度和虚拟内存机制实现了CPU和内存的虚拟化。在每个进程看来,自己是独占CPU和内存的。
下面看一下进程调度的大致原理,每个进程是程序的执行实体,相当于程序执行的容器。在进程执行时,进程有自己的执行环境,包括进程本身的状态和CPU状态,进程本身状态有代码、数据以及堆栈等,CPU状态就是相关寄存器的状态。在进程调度时,需要完成进程执行环境的切换,也就是上下文切换。
时间片就是进程在被抢占之前持续运行的时间。当一个进程的时间片耗尽后,调度程序会暂停该程序的执行,然后选择其他的进程抢占CPU。由于大部分OS的时间片都很短(基本上10ms),所以在人们看来这些进程是同时执行的。
调度程序维护者一个运行队列(Run Queue),包含所有处于可运行状态(TASK_RUNNING)的进程(在Linux中的CFS调度器,运行队列被组织成红黑树,按照进程的已运行时间排序)。调度程序就是在运行队列中,选择合适的进程抢占CPU。
当进程等待某个事件发生时会被阻塞,比如等待IO操作完成或是获取锁失败时,此时进程处于阻塞状态(TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE)不可能被调度占有CPU。进程的阻塞是通过等待队列(Wait Queue)实现的。每个被等待的事件都有自己的等待队列,比如一个锁或是文件描述符。进程执行阻塞操作时:
1. 将自己添加到相应事件的等待队列,当该事件发生时,队列上的所有进程会被唤醒,这个唤醒操作在其他地方执行。
2. 将进程的状态更新为阻塞状态(TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE)。
3. 调用schedule函数以调度其他进程执行。
唤醒操作很简单,当事件发生时,会将等待队列上的进程的状态修改为运行态(TASK_RUNNING),以便调度程序可以调度它执行。
还有一个重要的问题就是调度的时机,如果紧靠进程自己去调用schedule函数完成调度,那么用户进程会一直执行下去,这样显然对其他进程是不公平的。调度程序需要注册一个定时器任务,它周期性的去检查当前运行的进程是否已经用尽时间片,如果已用尽那么会调用schedule函数调度其他进程占用CPU。
下面看看如何通过java模拟:
CPU:单例,用于模拟CPU,由一个线程驱动,默认时间片是5s。
Task:模拟进程,被调度的实体。
Job:进程中执行的任务,这个类是本模拟程序中抽象出来的,主要通过计数来模拟程序的运行。
Scheduler:调度器,具有一个运行队列。schedule方法可以完成进程的调度。
Timer:模拟定时器,用于完成定时任务,会周期性的调用Scheduler.schedule()检查当前运行的进程的时间片是否用尽,然后调度其他进程占用CPU。
SleepSysCall:模拟慢系统调用,就是会导致进程阻塞的系统调用。具有一个等待队列。
ReadFS:是SleepSysCall的一个实现,用于模拟读文件,会阻塞10s。
CounterJob、CounterForkJob和SleepCallJob是Job的实现,模拟具体的任务,其中CounterForkJob会fork一个子进程,而SleepCallJob会调用阻塞的系统调用,就是用来模拟进程的阻塞和唤醒。
下面看一下,程序的输出:
2012-8-28 16:29:10 com.chosen0ne.taskschedule.arch.CPU run
信息: CPU runs
2012-8-28 16:29:10 com.chosen0ne.taskschedule.arch.Timer run
信息: Scheduler ticks //================// 定时器触发Scheduler
2012-8-28 16:29:10 com.chosen0ne.taskschedule.os.Task run
信息: Task JOB A switch in
2012-8-28 16:29:10 com.chosen0ne.taskschedule.job.CounterForkJob jobRun
信息: JOB A : 1
2012-8-28 16:29:11 com.chosen0ne.taskschedule.job.CounterForkJob jobRun
信息: JOB A : 2
2012-8-28 16:29:12 com.chosen0ne.taskschedule.job.CounterForkJob jobRun
信息: JOB A : 3
2012-8-28 16:29:13 com.chosen0ne.taskschedule.arch.Timer run
信息: Scheduler ticks
2012-8-28 16:29:13 com.chosen0ne.taskschedule.job.CounterForkJob jobRun
信息: JOB A : 4
2012-8-28 16:29:13 com.chosen0ne.taskschedule.os.Task fork
信息: fork task 'JOB C' //=============// 创建子进程
2012-8-28 16:29:14 com.chosen0ne.taskschedule.job.CounterForkJob jobRun
信息: JOB A : 5
2012-8-28 16:29:15 com.chosen0ne.taskschedule.job.CounterForkJob jobRun
信息: JOB A : 6
2012-8-28 16:29:16 com.chosen0ne.taskschedule.arch.Timer run
信息: Scheduler ticks
2012-8-28 16:29:16 com.chosen0ne.taskschedule.os.Scheduler schedule
信息: Task JOB B is selected to run, Task JOB A switch out //====// 上下文切换
2012-8-28 16:29:16 com.chosen0ne.taskschedule.os.Task run
信息: Task JOB B switch in
2012-8-28 16:29:16 com.chosen0ne.taskschedule.job.CounterJob jobRun
信息: JOB B : 100
2012-8-28 16:29:17 com.chosen0ne.taskschedule.job.CounterJob jobRun
信息: JOB B : 101
2012-8-28 16:29:18 com.chosen0ne.taskschedule.job.CounterJob jobRun
信息: JOB B : 102
2012-8-28 16:29:19 com.chosen0ne.taskschedule.arch.Timer run
信息: Scheduler ticks
2012-8-28 16:29:19 com.chosen0ne.taskschedule.job.CounterJob jobRun
信息: JOB B : 103
2012-8-28 16:29:20 com.chosen0ne.taskschedule.job.CounterJob jobRun
信息: JOB B : 104
2012-8-28 16:29:21 com.chosen0ne.taskschedule.job.CounterJob jobRun
信息: JOB B : 105
2012-8-28 16:29:22 com.chosen0ne.taskschedule.arch.Timer run
信息: Scheduler ticks
2012-8-28 16:29:22 com.chosen0ne.taskschedule.os.Scheduler schedule
信息: Task JOB D is selected to run, Task JOB B switch out
2012-8-28 16:29:22 com.chosen0ne.taskschedule.os.Task run
信息: Task JOB D switch in
2012-8-28 16:29:22 com.chosen0ne.taskschedule.job.SleepCallJob jobRun
信息: JOB D : 200
2012-8-28 16:29:23 com.chosen0ne.taskschedule.job.SleepCallJob jobRun
信息: JOB D : 201
2012-8-28 16:29:24 com.chosen0ne.taskschedule.job.SleepCallJob jobRun
信息: JOB D : 202
2012-8-28 16:29:25 com.chosen0ne.taskschedule.arch.Timer run
信息: Scheduler ticks
2012-8-28 16:29:25 com.chosen0ne.taskschedule.job.SleepCallJob jobRun
信息: JOB D : 203
2012-8-28 16:29:26 com.chosen0ne.taskschedule.job.SleepCallJob jobRun
信息: JOB D : 204
2012-8-28 16:29:26 com.chosen0ne.taskschedule.lib.SleepSysCall call
信息: JOB D sleeps //===========================// 调用阻塞系统调用,进入阻塞状态
2012-8-28 16:29:27 com.chosen0ne.taskschedule.os.Task run
信息: Task JOB E switch in
2012-8-28 16:29:27 com.chosen0ne.taskschedule.job.SleepCallJob jobRun
信息: JOB E : 200
2012-8-28 16:29:28 com.chosen0ne.taskschedule.arch.Timer run
信息: Scheduler ticks
2012-8-28 16:29:28 com.chosen0ne.taskschedule.job.SleepCallJob jobRun
信息: JOB E : 201
2012-8-28 16:29:28 com.chosen0ne.taskschedule.lib.SleepSysCall call
信息: JOB E sleeps
2012-8-28 16:29:29 com.chosen0ne.taskschedule.os.Task run
信息: Task JOB C switch in
2012-8-28 16:29:29 com.chosen0ne.taskschedule.job.CounterJob jobRun
信息: JOB C : 1000
2012-8-28 16:29:30 com.chosen0ne.taskschedule.job.CounterJob jobRun
信息: JOB C : 1001
2012-8-28 16:29:31 com.chosen0ne.taskschedule.arch.Timer run
信息: Scheduler ticks
2012-8-28 16:29:31 com.chosen0ne.taskschedule.job.CounterJob jobRun
信息: JOB C : 1002
2012-8-28 16:29:32 com.chosen0ne.taskschedule.job.CounterJob jobRun
信息: JOB C : 1003
2012-8-28 16:29:33 com.chosen0ne.taskschedule.job.CounterJob jobRun
信息: JOB C : 1004
2012-8-28 16:29:34 com.chosen0ne.taskschedule.arch.Timer run
信息: Scheduler ticks
2012-8-28 16:29:34 com.chosen0ne.taskschedule.os.Scheduler schedule
信息: Task JOB C is selected to run, Task JOB C switch out
2012-8-28 16:29:34 com.chosen0ne.taskschedule.os.Task run
信息: Task JOB C switch in
2012-8-28 16:29:34 com.chosen0ne.taskschedule.job.CounterJob jobRun
信息: JOB C : 1005
2012-8-28 16:29:35 com.chosen0ne.taskschedule.job.CounterJob jobRun
信息: JOB C : 1006
2012-8-28 16:29:36 com.chosen0ne.taskschedule.lib.ReadFS proc
信息: #### JOB D read data from FS //==========// 从阻塞系统调用返回,进入可运行状态
2012-8-28 16:29:36 com.chosen0ne.taskschedule.lib.SleepSysCall$1 run
信息: JOB D wakes up
2012-8-28 16:29:36 com.chosen0ne.taskschedule.job.CounterJob jobRun
信息: JOB C : 1007
2012-8-28 16:29:37 com.chosen0ne.taskschedule.arch.Timer run
信息: Scheduler ticks
2012-8-28 16:29:37 com.chosen0ne.taskschedule.job.CounterJob jobRun
信息: JOB C : 1008
2012-8-28 16:29:38 com.chosen0ne.taskschedule.lib.ReadFS proc
信息: #### JOB E read data from FS
2012-8-28 16:29:38 com.chosen0ne.taskschedule.lib.SleepSysCall$1 run
信息: JOB E wakes up
2012-8-28 16:29:38 com.chosen0ne.taskschedule.job.CounterJob jobRun
信息: JOB C : 1009
2012-8-28 16:29:39 com.chosen0ne.taskschedule.job.CounterJob jobRun
信息: JOB C : 1010
源码见githup: https://github.com/chosen0ne/task-schedule-simulate
更多推荐
所有评论(0)