在进入正题之前,先问一句,什么是操作系统,它和普通的应用程序有什么区别?这个问题虽然不难,但是它和我今天想要讲的内容密切相关。下面我就说说我的理解吧,我认为操作系统是一种抽象,这种抽象是人们经过长时间的实践和归纳提炼出来的,在没有操作系统的时候,人们仍然有办法制作程序并在机器上运行,但是人们必须针对硬件编程,这种方式十分繁琐而且有大量的重复劳动。人们慢慢发现,可以把程序对硬件资源的使用抽象出来,并提供相应的编程接口,这样程序就不需要直接面向硬件编写。这样不仅简化了程序的编写,而且使程序移植性变得更好。这种抽象的的结果就是出现了操作系统,它提供了对处理器的抽象、对存储器的抽象等等。这样,我们的应用程序就可以只面向操作系统来编写,应用程序运行于操作系统上,操作系统便成了承载这些应用程序的平台。可是随着计算机的发展,这个平台也出现了许多种类,面向一种平台编写的程序便很难在其他的平台上运行,这也变成了程序可移植性的巨大阻碍,于是又产生了类似Java虚拟机(JVM)这种更高级的抽象,程序只需要面向虚拟机编写,而不需要考虑具体运行的平台,Java语言也因此流行。如果能够理解操作系统对硬件进行抽象的原理,我想即使不需要我们自己实现一个操作系统,这也会使我们对应用程序的编写产生莫大的启发。接下来我就聊聊操作系统中几个基本的概念和原理吧。
进程和线程
进程也是一种抽象,它是操作系统对程序执行实例的抽象。进程是操作系统进行资源分配的基本单位,一个程序至少会有一个进程,操作系统会为每一个进程分配独立的内存空间,而且一个进程无法访问另一个进程的地址空间,如果需要获取其他进程中的资源,就要通过进程间通信来完成,比如通过管道、文件、套接字等。每一个进程都含有一个进程控制块(PCB),它可以唯一标识一个进程,操作系统也根据PCB来对进程进行调度。相比于进程,线程就要轻量很多了,进程可以说是线程的容器,线程是进程中的执行实体,一个进程中至少包含一个线程,CPU资源最终还是分配到了线程上。在同一个进程中的线程共享分配给该进程的资源,但每个线程都有自己的寄存器和栈。举个例子来说,要完成一个做饭任务,首先要分配一个厨房,这就好比创建了一个进程并为之分配了资源,厨房中有的人管洗菜切菜、有的人管烧水、有的人管炒菜,有的人管煮饭,其中每一个具体的工作就好比是一个工作线程,这些线程共享厨房这个资源,当切菜的人把菜切好后,炒菜的人就能看到,因为他们在同一间屋子里。但是每个线程也都有自己的一小块资源(寄存器和栈),这就好比切菜的人使用的切菜板不能让烧水的人占用。但是这些还不能保证做饭这个任务能正常的完成,比如厨房中每个人做的具体任务是有顺序要求的,炒菜的人必须等洗菜的人把菜洗好才能继续,他不能把还没洗的菜给炒了,所以线程之间需要进行同步。同时,有些必要的资源也不能多人共享,比如厕所,炒菜的人去厕所的时候,其他人都要等他出来才能进去,所以线程之间还需要进行互斥。当满足这些规则的时候,做饭这个任务才能有条不紊的完成,而不会出什么差错。其实很多时候把抽象的东西和现实世界中的现象进行类比,很多让人困惑的东西也就容易理解了。
进程调度算法
上文中也提到了进程控制块(PCB),其中存储了关于该进程的信息,其中就包括进程的调度信息。进程被提交给操作系统之后并不会直接就进入运行状态,它们首先会被放入一个叫做就绪队列的数据结构中,表示这些进程已经准备好了,可以运行它们。进程调度就是从就绪队列中选择合适的进程分配处理机,使它进入运行状态。下面就介绍几种常见的进程调度算法。
- 先来先服务调度算法:顾名思义,先进入就绪队列的进程先得到处理机,进入运行状态,直到运行结束或阻塞,才放弃处理机。
- 短作业优先调度算法:在就绪队列中选择一个估计运行时间最短的进程,分配处理机使之进入运行状态,直到运行完成或阻塞,才放弃处理机器。
- 基于优先级的非抢占式调度算法:在就绪队列中选择优先级最高的进程,为之分配处理机,然后让该进程运行直到结束或阻塞,才放弃处理机。
- 基于优先级的抢占式调度算法:在就绪队列中选择优先级最高的进程,分配处理机使之执行,但是在它运行过程中,就绪队列中出现了优先级更高的进程,该调度算法会将当前进程停止,并把处理机分配给更高优先权的进程。因此该算法能更好的满足紧急作业的需求,常用于实时系统中。
- 高响应比优先调度算法:响应比=响应时间/要求服务时间=(等待时间+要求服务时间)/要求服务时间。所以,进程等待的时间越长,要求服务的时间越短,它的响应比就越高。这样既考虑了进程等待的时间,又照顾了运行时间短的进程,是较为公平的调度算法。但是在调度之前需要先对进程的响应比进行计算,这会增加系统的开销。
- 时间片轮转调度算法:每次调度时,把处理机分配给队首进程,并令其执行一个时间片,当时间片用完时,调度程序便停止该进程的执行,并将它加入就绪队列的末尾。然后把处理机分配给就绪队列中新的队首进程。
- 多级反馈队列调度算法:设置多个就绪队列,为每个队列赋予不同的优先级,并且优先级越高的队列中,为每个进程分配的时间片越小。当一个进程进入内存后,首先将它放入优先级最高的队列末尾,按照先来先服务算法等待调度,当轮到它执行时,如果它在规定的一个时间片内还没有运行结束,就把它加入第二级的就绪队列末尾,同样按照FCFS算法等待调度,以此类推。仅当高优先级的队列为空时,低优先级队列中的进程才被调度执行。并且高优先级队列中的进程会抢占低优先级队列中正在执行进程的处理机。
分页与页面置换算法
我们知道通过分页,可以使得一个数据结构一部分存储在内存中,另一部分则保存在磁盘中。这样当内存中没有需要的页面时,就需要把磁盘中的页面放入内存并在内存中选择一个页面淘汰掉。而页面置换算法就是要确定淘汰掉哪一个页面的算法。
1. 最佳置换算法:内存中的每个页面都可以用在该页面被首次访问前需要执行的指令数进行标记,把标记最大的页面置换出去。但是由于当发生缺页时,操作系统无法知道各个页面下一次是在什么时候被访问,所以该算法是无法实现的,不过它可以作为衡量其他算法的标准。
2. 先进先出置换算法:每次发生缺页时,总是淘汰最早进入内存的页面。
3. 最近最久未使用(LRU)置换算法:当发生缺页时,淘汰掉最近一段时间中最长时间没有被使用过的页面。
4. 最近最少使用置换算法:缺页时,淘汰掉最近一段时间使用次数最少的页面。
总结
学操作系统的意义是什么,我觉得操作系统是一种方法学,我们在写程序时遇到的很多问题,操作系统都给出了可供参考的很优秀的解决方案。同时,理解操作系统对硬件、对应用程序进行抽象的思想,我想也会对自身大有益处。然而事实总是你学的越多,就越发现自己懂的越少,所以学无止境,加油吧,少年!