操作系统 | 抽象:进程
程序是静态的,没有生命周期的存在磁盘上面的一些指令。操作系统可以让这些字节运行起来,发挥作用。进程非正式定义即运行中的程序。
既然说一个进程是一个正在运行的程序,那么我们需要了解机器的哪些部分对执行程序很重要。
1)指令可访问的内存是进程的一部分。指令、正在运行的程序读取和写入的数据也在内存中。
2)寄存器。许多指令明确地读取或更新寄存器。
3)持久存储设备。
操作系统提供了创建、销毁、等待、其他控制、状态这些进程API。
创建进程
操作系统将代码和静态数据加载到内存、进程地地址空间中。早期操作系统会在运行程序之前全部完成加载过程,而现代操作系统惰性(lazily)执行该过程,仅在程序执行期间需要加载的代码或数据片段才会加载(涉及分页和交换的机制)。
之后,操作系统将为程序的运行时栈(run-time stack)分配一些内存、为程序的堆(heap)分配一些内存、执行I/O任务。最后,OS将CPU控制权转移到新创建的进程中开始执行程序。
进程状态
- 运行:进程正在处理机上运行
- 就绪:进程已准备好运行,但由于某种原因操作系统选择不再此时运行
- 阻塞:一个进程执行了某种操作,直到发生其他事件时才会准备运行。
进程所需的数据结构
进程列表、进程控制块PCB
模拟程序
参数说明:
-l PROCESS_LIST
:PROCESS_LIST由逗号分隔,形如X1:Y1,X2:Y2
,其中X是进程中包含的指令数,Y是进程为CPU指令的概率(非CPU指令则为I/O指令)。
模拟程序可模拟 进程使用CPU、进程提出IO请求并等待I/O过程完成。
在这里5:100意为程序由5条指令组成,每条指令为CPU概率是100%。加上-c参数,可以看到每个时刻的状态:
复杂一点,如果运行2个包含5条100%CPU指令的进程,则可以观察到:
在这里可以看到,首先运行PID为0的程序,process 1处于就绪状态,可以运行,但直至process 0处于结束态时才会被处理。
在下面的模拟片段中,将只产生I/O请求的指令(即CPU指令概率为0),并用-L
参数指定IO操作的时间为5个时间片长度。
执行过程如下所示,可以看到程序发出I/O时,进程进入WAITING状态(阻塞态),IO设备忙,CPU处于空闲。
使用-p命令可以得到相应的统计结果:
练习
-
使用
./process-run.py -l 4:100,1:0
发出4条CPU指令和一条简单的IO请求并等待完成。这两个进程需要多少时间?——第0个进程的4条CPU指令先被执行,第1个进程处于就绪态,IO空闲CPU忙。之后第二个进程等待IO处理,IO忙CPU空闲。由于IO请求时间长默认为5,因此({4+2+5=11}).
-
交换进程执行顺序:
./process-run.py -l 1:0,4:100
.——第0个进程发起IO请求后进入阻塞态(开始用时1,IO处理时间5,结束用时1),第1个进程被CPU处理(时间4,在第0个进程处于阻塞态时即被处理),总共用时7。
-
现在探索另一些标志。一个重要的标志是-S,它决定了当进程发出 I/O 时系统如何反应。将标志设置为 SWITCH_ON_END,在进程进行 I/O 操作时,系统将不会切换到另一个进程,而是等待进程完成。
——可以观察到如果阻止了系统切换,时间开销变为11。IO处理期间,第1个进程也无法被CPU处理。
-
另一个重要的行为是 I/O 完成时要做什么。利用-I IO_RUN_LATER,当 I/O 完成时,发出IO请求的进程不一定马上运行。相反,当时运行的进程一直运行。当你运行这个进程组合时会发生什么(./process-run.py -l 3:0,5:100,5:100,5:100 -S SWITCH_ON_IO -I IO_RUN_LATER -c -p)系统资源是否被有效利用?
——资源利用效率并不高。process 0运行时请求IO,然后process 1运行5条CPU指令后,process 2、process 3分别运行5条IO指令,之后process 0结束IO,并发起第2条、第3条IO请求。总共耗时({1+5+5+5+1+7+7=31})
-
更改命令行参数为
-I IO_RUN_IMMEDIATE
,观察运行情况:——可以看到执行的时间片为21,CPU利用率达到100%。这是因为对于多条IO请求的情况,IO设备忙时IO请求的进程处于阻塞态,其他进程可以被CPU处理,因此IO、CPU均忙的情况显著增加,从而提高了效率。