一、单项选择题 7. 某进程在运行过程中需要等待从磁盘上读入数据,此时进程的状态将______。
A.从就绪变为运行 B.从运行变为就绪 C.从运行变为阻塞 D.从阻塞变为就绪
A B C D
C
[解析] 在操作系统中,进程的基本状态有就绪状态、运行状态和阻塞状态三种。以下将分别对这三种状态进行分析。
(1)就绪(Ready)状态
进程已经具备运行条件,但是CPU还没有得到分配。也就是说,当进程已分配到除CPU以外的所有必要资源后,只要再获得CPU,便可立即执行,此时进程的状态称为就绪状态。在一个系统中,处于就绪状态的进程可能有多个,通常将这些处于就绪状态的进程排成一个队列,称为就绪队列。
(2)运行状态
进程已获得CPU,其程序正在执行。在单处理机系统中,只有一个进程处于运行状态,在多处理机系统中,则有多个进程处于运行状态。
(3)阻塞状态
当正在运行的进程由于发生某事件而暂时无法继续执行时,便放弃处理机而处于暂停状态,亦即程序的执行受到阻塞,把这种暂停状态称为阻塞状态,有时也称为等待状态或封锁状态。
三种进程之间的转换图如图所示。
三种进程之间的转换图
以下将针对这个状态转换图的条件进行讨论与分析:
(1)就绪→运行
对于就绪状态的进程,当进程调度程序按一种选定的策略从中选中一个就绪进程,并为之分配了处理机后,该进程便由就绪状态变为运行状态。
(2)运行→阻塞
如果正在运行的进程因发生某等待事件而无法执行,则进程由执行状态变为阻塞状态,例如进程提出输入/输出请求而变成等待外部设备传输信息的状态,进程申请资源(主存空间或外部设备)得不到满足时变成等待资源状态,进程运行中出现了故障(程序出错或主存储器读写错等)变成等待干预状态等。
(3)阻塞→就绪
处于阻塞状态的进程,当其等待的事件已经发生,例如输入/输出完成,资源得到满足或错误处理完毕时,处于阻塞状态的进程并不会马上转入运行状态,而是先转入就绪状态,然后再由系统进程调度程序在适当的时候将该进程转为运行状态。
(4)运行→就绪
正在运行的进程,因为时间片用完而被暂停执行,或在采用抢先式优先级调度算法的系统中,当有更高优先级的进程要运行而被迫让出处理机时,该进程便由运行状态转变为就绪状态。
以上4种情况可以相互正常转换,那么为什么阻塞状态无法直接转换为运行状态呢?为什么就绪状态无法直接转换为阻塞状态呢?其实,即使给阻塞进程分配CPU,也无法执行,因为操作系统在进行调度时,不会在阻塞队列中进行挑选,其调度的选择对象为就绪队列,而就绪状态根本就没有执行,是进入不了阻塞状态的。
本题中,进程在运行过程中,进入I/O操作,则处理阻塞。所以,此时进程的状态将从运行变为阻塞。所以,选项C正确。
二、多项选择题 3. 在竞态条件(Race condition)的情况下,两个线程执行如下代码段,其中count为共享变量,线程1执行代码段A,线程2指向代码段B,那么变量count的值可能为______。
int count=10;
代码段A:
Thread_1()
{
//do something
count++;
}
代码段B:
Thread_2()
{
//do something
count--;
}
A B C D
ABC
[解析] 如果两个或两个以上的线程同时访问相同的对象,或者访问不同步的共享状态,就会出现竞态条件。竞态条件是一个在设备或者系统试图同时执行两个操作的时候出现的不希望的状况,但是由于设备和系统的自然特性,为了正确地执行,操作必须按照合适顺序进行。 本题中,线程Thread_1读取count(10),进行递增操作,还未写回新值1(11)时,线程Thread_2读取count旧值(10),进行递减操作得到新值2(9),当写回时,若新值1覆盖新值2,则得到count=11,若新值2覆盖新值1,则得到count=9。若count++和count--顺序执行,则得到正常值10。 需要注意的是,竞态条件的出错概率非常小,只有非常快速或者非常运气不好时才会出现,在几百万次运行中也很少遇到一次,所以,很难调试出来。 所以,本题的答案为ABC。
7. 进程内的线程可以共享以下哪些资源______。
A.stack B.data section C.register set D.file fd
A B C D
BD
[解析] 线程是指程序在执行过程中,能够执行程序代码的一个执行单元。
进程是指一段正在执行的程序。而线程有时候也被称为轻量级进程,是程序执行的最小单元,一个进程可以拥有多个线程,各个线程之间共享程序的内存空间(代码段、数据段和堆空间)及一些进程级的资源(例如打开的文件),但是各个线程拥有自己的栈空间,进程与线程的关系如图所示。
进程与线程的关系
具体而言,线程共享的内容包括代码段、数据段、堆空间、进程打开的文件描述符、进程的当前目录以及进程的用户ID和组ID。
线程独占的资源包括栈、线程ID、寄存器的值、错误返回码以及线程的信号屏蔽码。具体内容如下:
(1)线程ID
每个线程都有自己的线程ID,这个ID在本进程中是唯一的。进程用此来标识线程。
(2)线程的栈
栈是保证线程独立运行所必需的。线程函数可以调用函数,而被调用函数中又是可以层层嵌套的,所以,线程必须拥有自己的函数栈,使得函数调用可以正常执行,不受其他线程的影响。
(3)错误返回码
不同的线程应该拥有自己的错误返回码变量。
(4)线程的信号屏蔽码
由于每个线程所感兴趣的信号不同,所以,线程的信号屏蔽码应该由线程自己管理,但所有的线程都共享同样的信号处理器。
(5)线程的优先级
由于线程需要像进程那样能够被调度,那么就必须要有可供调度使用的参数,这个参数就是线程。
所以,选项B与选项D正确,选项A与选项C错误。
三、论述题 1. 轮询任务调度和可抢占式调度有什么区别?
在多任务系统中,在同一时刻通常会有多个任务处于活动状态,操作系统此时就需要对资源进行管理,在任务间实现资源(CPU、内存等)的共享。任务调度是指基于给定时间点、给定时间间隔或者给定执行次数自动执行任务。轮询任务调度与抢占式任务调度的区别在于抢占式调度中优先级高的任务可以抢占CPU,而轮询的不能。 具体而言,轮询调度的原理是每一次把来自用户的请求轮流分配给内部服务器,从1开始,直到N(内部服务器个数),然后重新开始循环。只有在当前任务主动放弃CPU控制权的情况下(比如任务挂起),才允许其他任务(包括高优先级的任务)控制CPU。其优点是简洁性,它无须记录当前所有连接的状态,所以,它是一种无状态调度,但缺点是不利于后面的请求及时得到响应。抢占式调度允许高优先级的任务打断当前执行的任务,抢占CPU的控制权。这有利于后面的高优先级的任务也能及时得到响应。但实现相对较复杂,并且可能出现低优先级的任务长期得不到调度。
2. 在MMO游戏中,服务器采用Linux操作系统,网络通信与游戏逻辑处理进程一般是分离的。例如,GameSvr进程处理游戏逻辑,TCPSvr进程处理网络通信。Linux操作系统提供了很多机制可以实现GameSvr和TCPSvr进程之间的数据通信。请列出两种你认为最好的机制,并为主(最好)次(次佳)描述它们实现的框架、优缺点对比和应用中的注意事项。
系统进程之间通信的主要方法有信号、信号量、管道、消息和共享内存。信号量和信号主要用于触发,而不是用于传递数据。所以,数据通信的主要方法是管道、消息和共享内存,以下将分别对这些方式进行具体分析。
(1)管道
管道是由内核管理的一个环形缓冲区,类似于放入内存中的一个纸条,它允许两个进程以生产者/消费者的模型进行通信。如图所示,当两个进程利用管道文件进行通信时,一个进程为写进程,另一个进程为读进程,写进程通过写端(发送端)往管道文件中写入信息,读进程通过读端(接收端)从管道文件中读取信息,两个进程协调不断地进行写、读,便会构成双方通过管道传递信息的流水线。当管道中没有信息时,从管道中读取的进程会等待,直到另一端的进程放入信息。当管道被放满信息时,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也自动消失。
管道通信
管道的优点是不需要加锁,缺点是默认缓冲区太小,只有4K大小,而且它只适合父子进程间通信。由于一个管道只适合单向通信,如果要双向通信,就需要建立两个管道,所以,它不适合多个子进程。除此以外,数据本身没有边界,需要应用程序自己解释,而一般消息大多是一个固定长的消息头,和一个变长的消息体,一个子进程从管道read到消息头后,消息体可能被其他子进程接收到。
(2)消息
消息是有类型的一段文本。为参与消息传递的进程提供msgsnd(用来向消息队列发送消息)和msgrcv(用来从消息队列中读取消息)系统调用。每个进程都有一个与之相关联的消息队列,其功能类似于信箱。消息发送者指定发送的每个消息的类型,类型可以被接收者用作选择的依据。接收者可以按先进先出的顺序接收信息,或者按类型接收。当进程试图给一个满队列发送信息时,它将被阻塞;当进程试图从一个空队列读取时也会被阻塞;如果一个进程试图读取某一特定类型的消息,但由于现在还没有这种类型的消息而失败时,则该进程不会阻塞。
消息队列能适合大部分场景,缺点是默认缓冲比较小,不过这个缓冲区可以调整,前提是具有管理员权限。
(3)共享内存
共享内存是分配一块能被其他进程访问的内存,实现是通过将内存映射到共享它的进程的地址空间,使这些进程间的数据传送不再涉及内核,即进程问通信不需要通过进入内核的系统调用来实现。进程读写共享内存所使用的机器指令与读写虚拟内存空间的其他部分所使用的指令相同。每个进程有一个只读或读写的权限。互斥约束不属于共享内存机制的一部分,但必须由使用共享内存的进程提供。共享内存几乎可以认为没有上限;它也是不局限于父子进程,采用与消息队列类似的定位方式;因为内存是共享的,不存在任何单向的限制;最大的问题就是需要应用程序自己实现互斥。
相比其他的进程间通信方式,共享内存的最大优点是:数据的赋值只有两次,一次是从输入文件到共享内存区,一次是从共享内存区到输出文件,而其他的则需要复制4次:服务器将输入文件读入自己的进程空间,再从自己的进程空间写入管道/消息队列等;客户进程从管道/消息队列中读出数据到自己的进程空间,最后输出到客户指定的文件中。因此,相比管道和消息队列,共享内存是最快的进程问的通信方式,因为它不涉及与内存的交互,所以,其效率更高。
3. 线程与进程的区别和联系分别是什么?线程是否具有相同的堆栈?DLL是否具有独立的堆栈?
进程是死的,只是一些资源的集合,真正的程序执行都是线程来完成的,程序启动的时候操作系统创建了一个主线程,每个线程有自己的堆栈。DLL(Dynamic Link Library,动态链接库)中是否具有独立的堆栈,这个问题不好回答,或者说这个问题本身就有问题。因为DLL中的代码是被某些线程所执行,只有线程拥有堆栈,如果DLL中的代码是由EXE中的线程所调用,那么这个时候是不是说这个DLL没有自己独立的堆栈呢?如果DLL中的代码是由DLL自己创建的线程所执行,那么是不是说DLL有独立的堆栈呢?以上讲的是堆栈,如果对于堆来说,每个DLL有自己的堆,所以,如果是从DLL中动态分配的内存,最好是从DLL中删除;如果是从DLL中分配内存,然后在EXE中,或者另外一个DLL中删除,很有可能导致程序崩溃。
4. 程序什么时候应该使用线程?
程序在以下几种情况下使用线程: 1)耗时的操作使用线程,提高应用程序响应效率。 2)并行操作时使用线程,例如基于C/S架构的服务器端并发线程响应用户的请求。 3)在多CPU系统中,使用线程提高CPU利用率。 4)改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。
5. 请回答以下关于进程、线程以及程序的有关问题。
1)进程和线程的区别是什么?
2)多线程程序有什么优点与缺点?
3)多进程程序有什么优点与缺点?与多线程相比,有什么区别?
1)进程和线程的关系如下: ①一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。 ②资源分配给进程,同一进程的所有线程共享该进程的资源。 ③处理机分给线程,即真正在处理机上运行的是线程。 ④线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。线程指的是进程内的一个执行单元,也是进程内的可调度实体。 进程和线程的相同点如下: ①二者都具有ID、一组寄存器、状态、优先级以及所要遵循的调度策略。 ②每个进程都有一个进程控制块,线程也拥有一个线程控制块。 ③线程和子进程共享父进程中的资源;线程和子进程独立于它们的父进程,竞争使用处理器资源;线程和子进程的创建者可以在线程和子进程上实行某些控制,例如创建者可以取消、挂起、继续和修改线程和子进程的优先级;线程和子进程可以改变其属性并创建新的资源。 进程和线程的不同点如下: ①线程是进程的一部分,一个没有线程的进程是可以被看作单线程的,如果一个进程内拥有多个线程,进程的执行过程不是一条线(线程)的,而是多条线(线程)共同完成的。 ②启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。 ③系统在运行的时候会为每个进程分配不同的内存区域,但是不会为线程分配内存(线程所使用的资源是它所属的进程的资源),同一个进程内的线程可以共享进程的资源。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。而一个线程的数据可以直接为其他线程所用,这不仅快捷,而且方便。 ④与进程的控制表PCB相似,线程也有自己的控制表TCB,但是TCB中所保存的线程状态比PCB表中少多了。 ⑤进程是系统所有资源分配时候的一个基本单位,拥有一个完整的虚拟空间地址,并不依赖线程而独立存在。 2)多线程的优点如下: 无须跨进程边界;程序逻辑和控制方式简单;所有线程可以直接共享内存和变量等;线程方式消耗的总资源比进程方式少。 多线程的缺点如下: 每个线程与主程序共用地址空间,受限于2GB地址空间;线程之间的同步和加锁控制比较麻烦;一个线程的崩溃可能影响到整个程序的稳定性;到达一定的线程数程度后,即使再增加CPU也无法提高性能,例如Windows Server 2003,大约1500个线程数就快到极限了(线程堆栈设定为1M),如果设定线程堆栈为2M,还达不到1500个线程总数;线程能够提高的总性能有限,而且线程多了之后,线程本身的调度也很烦琐,需要消耗较多的CPU。 3)多进程的优点如下: 每个进程互相独立,不影响主程序的稳定性,子进程崩溃也没关系;通过增加CPU,就可以容易扩充性能;可以尽量减少线程加锁/解锁的影响,即使线程运行的模块算法效率低,也可极大提高性能;每个子进程都有2GB地址空间和相关资源,总体能够达到的性能上限非常大。 多线程的缺点如下: 逻辑控制复杂,需要和主程序交互;需要跨进程边界,如果有大数据量传送,就不太适用,适合于小数据量传送、密集运算、多进程调度开销比较大;最好是多进程和多线程结合,即根据实际的需要,每个CPU开启一个子进程,这个子进程开启多线程可以为若干同类型的数据进行处理。当然,也可以利用多线程+多CPU+轮询方式来解决问题。 方法和手段是多样的,关键是自己看起来实现方便又能够满足要求,代价也合适。
6. 程序代码、常量、局部变量和全局变量分别存储在内存中的什么位置?
程序代码存储在代码段,常量分为字符串常量和其他常量,字符串常量存储于字符串常量区,对于整数类型,如果出现在表达式语句中,通常会成为“立即数”,被包含在生成的代码中。局部变量存储于栈上,全局变量(包括静态变量)存储于全局数据区。
7. 请简要介绍Windows内存管理的机制。
内存管理是指软件运行时对计算机内存资源的分配和使用的技术。其最主要目的是如何高效、快速地分配,并且在适当的时候释放和回收内存资源。 在讲解Windows内存管理前,首先介绍几个基本的概念,它们是物理内存、虚拟内存。 物理内存:即插在主板上的内存条。它是固定的,内存条的容量多大,物理内存就有多大(集成显卡系统除外),但是需要注意的是,如果运行很多程序或者程序本身很大,就会导致占用大量的物理内存,甚至导致物理内存被消耗殆尽。 虚拟内存:考虑到代码必须在物理内存中才能被运行,由于现在的操作系统中运行着非常多的应用程序,而内存中不一定能够完全放下,所以,引出了虚拟内存的概念。虚拟内存指在硬盘上划分一块页面文件,充当内存使用,而这块内存却不是实实在在存在的。当程序在运行时,有一部分资源还没有用上或者同时打开几个程序却只操作其中一个程序时,系统没必要将程序所有的资源都塞在物理内存中,于是,系统将这些暂时不用的资源放在虚拟内存上,等到需要时再调出来使用;把那些不常用的程序片断就放入虚拟内存,当需要用到它的时候再载入物理内存中。 除了以上提及的这些内容是内存管理所需要做的事情以外,内存管理还有另外一件事需要做:计算程序片段在主存中的物理位置,以便CPU调度。对于Windows系统而言,其内存管理主要包括页式存储管理、段式存储管理和段页式存储管理等。以下将分别对其进行讲述。 页式存储管理:用户程序的地址空间被划分成若干固定大小的区域,称为“页”,相应地,内存空间分成若干个物理块,页和块的大小相等。可将用户程序的任一页放在内存的任一块中,实现了离散分配。进程空间也被静态地划分为若干个等长的区域,每个区域称为一个逻辑页面,其长度与页框的长度相等。当进程运行时,需要将它的各个逻辑页面保存到存储空间的物理页框中,即需要确定逻辑页面与页框的对应关系,进程的逻辑页面是连续的,但是页框页面却不一定是连续的。允许一个进程占用内存空间中多个连续的区域,而这些区域的长度相等,因而采用静态等长存储分配的方法,不会产生碎片。 段式存储管理:将用户程序地址空间分成若干个大小不等的段,每段可以定义一组相对完整的逻辑信息。存储分配时,以段为单位,段与段在内存中可以不相邻接,也实现了离散分配。 段页式存储管理:分页系统能有效地提高内存的利用率,而分段系统能反映程序的逻辑结构,便于段的共享与保护,将分页与分段两种存储方式结合起来,就形成了段页式存储管理方式。在段页式存储管理系统中,作业的地址空间首先被分成若干个逻辑分段,每段都有自己的段号,然后再将每段分成若干个大小相等的页。对于主存空间也分成大小相等的页,主存的分配以页为单位。段页式系统中,作业的地址结构包含三部分的内容: 段号页号页内位移量 程序员按照分段系统的地址结构将地址分为段号与段内位移量,地址变换机构将段内位移量分解为页号和页内位移量。 为实现段页式存储管理,系统应为每个进程设置一个段表,包括每段的段号、该段的页表始址和页表长度。每个段有自己的页表,记录段中每一页的页号和存放在主存中的物理块号。
8. 段页式虚拟存储管理方案的特点是什么?
页式存储分配是把到来的作业分成相等大小的页,段式存储管理是把一个程序分成若干个段(Segment)进行存储,每个段都是一个逻辑实体(Logical Entity),段页式虚拟存储管理是基本分段存储管理方式和基本分页存储管理方式原理的结合,兼有段式和页式管理的优点,即先将用户程序分成若干个段,再把每个段分成若干个页,并为每一个段赋予一个段名,页间不要求连续(能动态连接),用分段方法分配管理作业,用分页方法分配管理内存。它的特点是空间浪费小、存储共享容易、存储保护容易及能动态连接。 段页式管理采用二维地址空间,例如段号(S)、页号(P)和页内单元号(D)。系统建两张表格,每一作业一张段表,每一段建立一张页表,段表指出该段的页表在内存中的位置,地址变换机构类似页式机制,只是前面增加一项段号。所以,存储共享容易、存储保护容易。