天津科技大学操作系统实验15.docx
- 文档编号:6634361
- 上传时间:2023-01-08
- 格式:DOCX
- 页数:52
- 大小:669.02KB
天津科技大学操作系统实验15.docx
《天津科技大学操作系统实验15.docx》由会员分享,可在线阅读,更多相关《天津科技大学操作系统实验15.docx(52页珍藏版)》请在冰豆网上搜索。
天津科技大学操作系统实验15
操作系统实验指导书
课程号:
适用专业:
计算机各专业
制定人:
吴江红
教研室:
计算机科学与技术教研室
计算机科学与信息工程学院
2012年5月
前言
操作系统是计算机的核心和灵魂。
操作系统软件的设计对整个计算机的功能和性能起着至关重要的作用,所以此门课也是必不可少的,是面向计算机科学与技术、网络工程、软件工程等大多数计算机专业本科生和研究生开设的一门计算机专业课程。
操作系统是计算机系统的核心,《操作系统》课程是计算机科学与技术专业的重要必修课。
本课程的目的是使学生掌握现代计算机操作系统的基本原理、基本设计方法及实现技术,具有分析现行操作系统和设计、开发实际操作系统的基本能力。
本课程的理论性强,内容抽象,特别是进程管理,需要通过严密的逻辑思维来想象微观时间世界中的处理机调度与运行。
通过学习使学生掌握计算机操作系统的基本原理及组成;计算机操作系统的基本概念和相关的新概念、名词及术语;了解计算机操作系统的发展特点、设计技巧和方法;对常用计算机操作系统(Dos、Windows和Unix或Linux)会进行基本的操作使用。
实验要求
为了顺利完成编译原理课程实验,学生应做到:
(1)熟练掌握一种高级程序设计语言。
(2)实验前,认真学习教材以及实验指导书的相关内容,提前做好实验准备。
(3)每次实验先分析后编程,在实验报告中应写明自己的编程思路和设计流程。
(4)实验结束一周后提交实验报告。
实验报告内容应包括:
实验目的、实验内容、设计思路和流程框图,源程序(含注释)清单、测试结果以及实验总结。
(5)遵守机房纪律,服从辅导教师指挥,爱护实验设备。
实验的验收将分为两个部分。
第一部分是上机操作,随机抽查程序运行和即时提问;第二部分是提交书面的实验报告。
此外杜绝抄袭现象,一经发现雷同,双方成绩均以0分计算。
实验一Windows多线程..................................................1实验二Windows线程同步机制...........................................5
实验三Windows线程通信................................................9
实验四银行家算法模拟.................................................15
实验五页面置换算法模拟...............................................22
实验一Windows多线程
【开发语言及实现平台或实验环境】
C++/C#
MicrosoftVisualStudio6.0/MicrosoftVisualStudio.NET
【实验目的】
(1)进一步理解操作系统的并发性;
(2)了解Windows线程创建方法,并通过查阅资料理解各参数的含义;
(3)了解多线程程序设计方法,并进行简单应用。
【实验要求】
(1)逐程序进行简要分析、运行各程序并仔细阅读注释;
(2)查阅MSDN或其他资料,掌握相关系统调用使用方法和参数含义;
(3)完成实验报告。
【相关知识】
一、内核对象
(一)内核对象的概念
内核对象是内核分配的一个内存块,这种内存块是一个数据结构,表示内核对象的各种特征。
并且只能由内核来访问。
应用程序若需要访问内核对象,需要通过操作系统提供的函数来进行,不能直接访问内核对象(Windows 从安全性方面来考虑的)。
内核对象通过 Create* 来创建,返回一个用于标识内核对象的句柄,这些句柄(而不是内核对象)可在创建进程范围内使用,不能够被传递到其他进程中被使用。
(二)内核对象使用的计数
因为内核对象的所有者是内核,而不是进程,所以何时撤销内核对象由内核决定,而内核做这个决定的依据就是该内核对象是否仍然被使用。
那么如何判断内核对象是否被使用呢?
可以通过内核对象的“使用计数”属性,一旦这个值变成0了,内核就可以释放该对象了。
(三)创建内核对象
1、进程与句柄表
每个进程在初始化的时候,将被分配一个句柄表,该句柄表中只存储内核对象的句柄,不存储用户对象的句柄。
句柄表的详细结构微软没有公布,但是大致包含三个内容:
内核对象句柄,内核对象地址,访问屏蔽标志。
2、创建内核对象及操作系统内部机制
利用 CreateSomeObject 的函数来创建内核对象。
调用该函数的时候内核就为该对象分配一个内存块,并进行初始化,然后内核再扫描该进程的句柄表,初始化一条记录并放在句柄表中。
3、进程中使用内核对象的内部机制
假设函数 F 使用某个内核对象,其参数为 Handle1,则该函数内部需要查找该进程的句柄表,找出参数句柄对应的记录,然后才能使用该内核对象。
(四)关闭内核对象
无论进程怎样创建内核对象,在不使用该对象的时候都应当通过boolCloseHandle(HANDLEhobj) 来向操作系统声明结束对该对象的访问。
为什么叫声明呢?
是因为此时也许还有其他进程对该对象的访问,操作系统可能并不立即释放该对象。
操作系统需要做的是:
从进程的句柄表中删除该内核对象的记录,另外再考察该内核对象的使用计数以决定是否需要释放该对象。
(五) 内核对象的共享
说到共享,与之孪生的就是共享权限。
Windows 内核对象的共享有三种方式:
1、继承式共享(父子进程间)
只有当进程是父子关系的时候,才能使用此种方式的共享。
特别要注意的是继承的是内核对象的句柄,内核对象本身是不具备继承性。
要达到这种继承的效果需要做以下几件事:
在进程创建内核对象的时候,需要一个安全结构 sa ( SECURITY_ATTRIBUTES 类型,以向 OS 声明对象的访问方式)作为参数。
继承式共享需要将结构的成员 sa.bInheritHandle 设置为 TRUE 。
此时 OS 内部的处理式将进程的句柄表中的该对象的访问屏蔽字段设置成“可继承”。
在创建子进程( CreateProcess 函数)时,设置创建参数 bInheritHandles 为 TRUE 。
表示被创建的子进程可以继承父进程中的所有可继承内核对象。
OS 内部的处理是:
复制父进程句柄表中的记录到子进程的句柄表中,并使用相同的句柄值;为内核对象的使用计数器加 1 。
特别说明:
子进程能够继承的的内核对象仅局限于父进程创建它的时候所拥有的可继承内核对象。
子进程诞生后,父进程再搞出什么可继承的东西,子进程是不能用的。
这就需要在子进程中使用继承的内核对象的时候需要慎重,以确定内核对象是否已被继承了。
利用 SetHandleinformation 方法可以随时修改内核对象句柄的一些属性,目前公开的句柄属性有两种,一种是该句柄是否能被继承,另一种是该句柄是否能被关闭。
2、同名共享
同名共享,不需要共享进程之间存在父子关系。
但局限于内核对象是否支持这种共享方式。
创建内核对象的Create 函数中是否包含 pszName 是该内核对象是否支持同名共享的标志。
方法一:
当 Process1 通过 CreateObject(…”someName”)创建了一个名字为 someName 的内核对象后, Process2 也调用了 CreateObject(…”someName”),此时内核的动作是:
在全局中查询发现已经存在 someName1 的对象;为 Process2 的句柄表添加一条 Ojbect 的记录,使用的句柄不确定;为 someName这个 Object 的引用计数器加 1 。
方法二:
Process2 使用 OpenObject(…”someName” )的方式来获得对名someName的 Object 的句柄。
用这种 Open 方法的时候,需要提供一个参数让 OS 鉴权,以判定是否能够以参数指定的方式来访问内核对象。
3、复制内核对象的句柄的方式共享
跨进程边界的内核对象共享的另外一个方法是通过 DuplicateHandle 来复制内核对象句柄。
如果要将 ProcessS 中的对象拷贝到 ProcessT 中则调用 DuplicateHandle 的进程一定要有对这两个进程的访问权,即句柄表中拥有这两个进程内核对象的句柄记录。
二、线程的一般概念
进程只是线程的容器,从来不执行任何东西;线程总是在某个进程中被创建;线程在进程的地址空间中执行代码;线程们共享进程中的所有内核对象。
三、线程的创建
HANDLECreateThread(
PSECURITY_ATTRIBUTESpsa,
DWORDcbStack,
PTHREAD_START_ROUTINEpfnStartAddr,
PVOIDpvParam,
DWORDfdwCreate,
PDWORDpdwThreadID);
调用CreateThread后,OS 进行如下几个动作:
生成一个线程内核对象;在进程空间内为线程分配堆栈空间。
因为线程的环境同于其所在进程的环境,所以创建的线程可以访问进程中的所有资源,包括线程中所有的内核对象。
四、线程销亡
(一) 终止线程的方式
线程函数返回(最好使用这个方式,可以保证:
线程中创建的 C++ 对象正常析构; OS 释放线程堆栈内存; OS 将线程的退出码设置为线程函数的返回值;系统将递减该线程内核对象的的使用计数器【如果此时还有其他引用 …… ,见下面说明】。
)
调用 ExitThread (不能释放 C++ 对象,所以最好不要使用这个方式。
另外,如果非要调用也应当调用编译器推荐的,如 _endThread 【 Windows 核心编程 P127 】)
同进程内的其他线程(包括主线程)调用 TerminateThread (被撤销线程得不到通知,不能释放资源,尽量避免这种方式。
另外这个函数是个异步函数,返回时,线程不保证已经被撤销,如果要观察线程是否被撤销,应当使用 WaitForSingleObject )包含线程的进程终止(应当避免这种方式)
(二) 线程退出时 OS 的行为
线程内的所有用户对象被释放。
线程的退出码从 STILL_ACTIVE 改为传递给 ExitThread 或 TerminateThread 的代码
线程内核对象的状态改为“已通知”
如果线程为进程中的最后一个线程,则 OS 将进程当作已终止运行
线程内核对象的引用计数器减 1 (一旦线程终止了,其他引用改线程内核对象将不能够处理改线程的句柄,但是可以通过调用 GetExitcodeThread 来检查 hThread 代表的线程是否已经终止运行了。
)
【实验步骤】
(1)阅读和理解1-1.cpp文件中的程序,多次运行1-1.cpp,认真观察结果。
然后将main函数中注释掉的Sleep语句让其可用,即将其前面的注释号删掉,再多次运行,认真观察结果。
比较修改程序前后运行结果发生的变化,并分析其原因。
(2)阅读和理解1-2.cpp文件中的程序,多次运行1-2.cpp,认真观察结果。
思考为什么1-2.cpp中可以不需要Sleep语句就可看到各线程都被调度了。
(3)阅读和理解1-3.cpp文件中的程序,运行1-3.cpp,认真观察结果。
然后将main函数中注释掉的Sleep语句让其可用,即将其前面的注释号删掉,再多次运行,认真观察结果。
再将两个子函数中注释掉的Sleep语句让其可用,再多次运行,认真观察结果,可能会出现销售出0号票的情况。
比较修改程序前后运行结果发生的变化,并分析其原因。
【实验结果与分析】
(1)阅读和理解1-1.cpp文件中的程序
运行一次1-1.cpp的结果如下:
运行多次1-1.cpp的结果如下:
将main函数中注释掉的Sleep语句让其可用,运行结果为:
分析原因:
Sleep(0)的作用为语句可观察线程1和主线程并发执行。
输出结果“mainthreadisrunning/thread1isrunning”。
没有添加的线程1运行结束只输出“mainthreadisrunning”
(2)阅读和理解1-2.cpp文件中的程序
运行一次1-2.cpp文件的结果如下:
运行多次1-2.cpp文件的结果如下:
分析原因:
1-2.cpp文件使用的是时间片轮转方法调度的主线程、线程1、线程2.因此不需要sleep语句就可将主线程调度。
因为在两个线程中存在共享变量,因此执行结果出现不可再现性。
(3)阅读和理解1-3.cpp文件中的程序
运行一次1-3.cpp的结果为:
将主进程的sleep可用,运行结果为:
将两个子函数中注释掉的Sleep语句让其可用,再多次运行:
多次运行修改后的程序结果为:
【程序说明】多线程文件夹下
1-1.cpp文件:
简单的多线程示例;
1-2.cpp文件:
在1.cpp文件中加入循环,使得两个线程交替执行,理解时间片轮转调度,因为在两个线程中存在共享变量,因此执行结果出现不可再现性。
1-3.cpp文件:
编写一个模拟火车站售票系统的程序,多窗口售票可采用多线程技术实现。
主线程创建两个线程(即两个售票窗口)。
实验二Windows线程同步机制
【开发语言及实现平台或实验环境】
C++/C#
MicrosoftVisualStudio6.0/MicrosoftVisualStudio.NET
【实验目的】
(1)了解Windows线程同步机制;
(2)了解互斥体,并通过查阅资料理解互斥体对象的使用方法;
(3)了解事件,并通过查阅资料理解事件对象的使用方法;
(4)了解关键区,并通过查阅资料理解关键区对象的使用方法;
(5)了解信号量,并通过查阅资料理解信号量对象的使用方法;
(6)利用Windows线程同步机制,模拟生产者消费者问题。
【实验要求】
(1)逐程序进行简要分析、运行各程序并仔细阅读注释;
(2)查阅MSDN或其他资料,掌握相关系统调用使用方法和参数含义;
(3)完成实验报告。
【相关知识】
一、Windows线程同步机制
Windows 下提供了多种内核对象实现线程、进程间的同步和互斥,常用的有:
1、关键区(临界区CriticalSection)
关键区不是内核对象,在用户态实现了同一进程中线程的互斥。
由于使用时不需要从用户态切换到核心态,所以速度很快( X86 系统上约为 20 个指令周期),但其缺点是不能跨进程同步,同时不能指定阻塞时的等待时间,只能无限等待。
使用关键区的方法则使同步管理的效率更高。
使用时先定义一个CRITICALSECTION结构的排斥区对象,在进程使用之前调用如下函数对对象进行初始化:
VOIDInitializeCriticalSection(LPCRITICAL_SECTION);
当一个线程使用排斥区时,调用函数:
EnterCriticalSection或者TryEnterCriticalSection;
当要求占用、退出排斥区时,调用函数LeaveCriticalSection,释放对排斥区对象的占用,供其他线程使用。
2、互斥体( Mutex )
互斥体实现了和关键区类似的互斥功能,但区别在于:
互斥体是内核对象,可以实现跨进程互斥,但需要在用户态和核心态之间切换,速度比关键区慢得多(X86 系统上约为 600 个指令周期),同时可以指定阻塞时的等待时间。
Mutex对象的状态在它不被任何线程拥有时才有信号,而当它被拥有时则无信号。
Mutex对象很适合用来协调多个线程对共享资源的互斥访问。
可按下列步骤使用该对象:
首先,建立互斥体对象,得到句柄:
HANDLECreateMutex();
然后,在线程可能产生冲突的区域前(即访问共享资源之前)调用WaitForSingleObject,将句柄传给函数,请求占用互斥对象:
dwWaitResult=WaitForSingleObject(hMutex,5000L);
共享资源访问结束,释放对互斥体对象的占用:
ReleaseMutex(hMutex);
互斥体对象在同一时刻只能被一个线程占用,当互斥体对象被一个线程占用时,若有另一线程想占用它,则必须等到前一线程释放后才能成功。
3、事件(Event)
事件也是内核对象,具有“信号态”和“无信号态”两种状态。
当某一线程等待一个事件时,如果事件为信号态,将继续执行,如果事件为无信号态,那么线程被阻塞。
线程能够指定阻塞时的等待时间。
例如:
只有在通信端口缓冲区收到数据后,监视线程才被激活。
事件对象是用CreateEvent函数建立的。
该函数可以指定事件对象的类和事件的初始状态。
如果是手工重置事件,那么它总是保持有信号状态,直到用ResetEvent函数重置成无信号的事件。
如果是自动重置事件,那么它的状态在单个等待线程释放后会自动变为无信号的。
用SetEvent可以把事件对象设置成有信号状态。
在建立事件时,可以为对象命名,这样其他进程中的线程可以用OpenEvent函数打开指定名字的事件对象句柄。
4、信号量(Semaphore)
信号量是一个资源计数器,当某线程获取某信号量时,信号量计数首先减 1 ,如果计数小于 0 ,那么该线程被阻塞;当某县城释放某信号量时,信号量计数首先加 1 ,如果计数小于或等与 0,那么唤醒某被阻塞的线程并执行之。
对信号量的总结如下:
●如果计数器 m 大于 0 ,表示还有 m 个资源可以访问,此时信号量线程等待队列中没有线程被阻塞,新的线程访问资源也不会被阻塞;
●如果计数器 m 等与 0 ,表示没有资源可以访问,此时信号量线程等待队列中没有线程被阻塞,但新的线程访问资源会被阻塞;
●如果计数器 m 小于 0 ,表示没有资源可以访问,此时信号量线程等待队列中有 abs ( m )个线程被阻塞,新的线程访问资源会被阻塞;
信号量常被用于保证对多个资源进行同步访问。
可按下列步骤使用该对象:
首先,创建信号对象:
HANDLECreateSemaphoreQ;
或者打开一个信号对象:
HANDLEOpenSemaphoreQ;
然后,在线程访问共享资源之前调用:
WaitForSingleObject;
共享资源访问完成后,应释放对信号对象的占用:
ReleaseSemaphoreQ;
二、生产者-消费者模型
生产者-消费者模型是指:
●生产者进行生产将物品放入仓库,同一时间只能有一个生产者将物品放入仓库,如果仓库满,生产者等待。
●消费者从仓库中取出物品,同一时间只能有一个消费者取出物品,如果仓库空,消费者等待;
●生产者将物品放入仓库时消费者不能同时取;
●消费者取物品时生产者不能放入物品;
总之,就是生产者群体或消费者群体内部是互斥的,两个群体之间是同步的。
当只有一个生产者、消费者时,由于同一群体内部不需要互斥,所以只需在群体之间实 现同步即可。
例如可以使用两个 Event/CriticalSection/Mutex/Semaphore 实现同步;
如果有多个生产者和消费者,那么情况会复杂些,需要一个 Event/CriticalSection/Mutex 实现线程之间的互斥,需要两个 Semaphore 实现两个线程群体间的同步。
【实验步骤】
(1)阅读和理解2-1(mutex).cpp文件中的程序,运行2-1(mutex).cpp,认真观察结果。
然后将两个子函数中注释掉的Sleep语句让其可用,再多次运行,认真观察结果,不会出现销售出0号票的情况。
比较修改程序前后运行结果发生的变化,并分析其原因。
(2)2-2(event).cpp、2-3(critical_section).cpp的处理方式同
(1)。
2-2
2-3
分析:
修改之前,在指定暂停的时间sleep(1000)内Sleep(1000);//要保证售完40张票之前线程不退出,thread1和thread2随机售票,出现多种情况;将两个子函数中注释掉的sleep
(1)语句让其可用后,thread1和thread2交替售票,即thread1在其暂停的时间sleep
(1)内,thread2获得了对共享对象hmutex的所有权,开始售票,同理当thread2在其暂停的时间sleep
(1)内,thread1获得了对共享对象hmutex的所有权,开始售票,从而实现了交替售票。
(3)阅读和理解2-4(Producer_Consumer).cpp文件中的程序,运行2-4(Producer_Consumer).cpp,认真观察结果,先生产后消费。
然后将两个子函数中注释掉的while语句让其可用,再多次运行,认真观察结果,生产者和消费者保持同步。
比较修改程序前后运行结果发生的变化,并分析其原因。
分析:
修改之前,在指定暂停时间sleep(20)内,producer和consumer只能执行一次;将两个子函数中注释掉的while语句让其可用后,producer和consumer在指定暂停时间sleep(20)内,随机循环获得共享对象的所有权,进行生产或消费,从而出现多种结果。
(4)阅读和理解2-4(Producer_Consumer)1.cpp文件中的程序,运行2-4(Producer_Consumer)1.cpp,认真观察结果。
【实验思考及总结】
进程同步也包括进程的互斥和进程的同步两个方面,使操作系统管理共享资源的一种手段。
通过这次实验,体会了线程同步机制的重要性,同时,在选用线程同步机制时也应该根据具体的案例的要求选择合适的线程同步机制,对效率要求很高的程序就应该自己编写效率比较高的线程同步机制。
【程序说明】同步文件夹下
2-1(mutex).cpp文件:
对1-3.cpp中的程序,利用互斥对象实现同步。
2-2(event).cpp文件:
对1-3.cpp中的程序,利用事件对象实现同步。
2-3(critical_section).cpp文件:
对1-3.cpp中的程序,利用临界区实现同步。
2-4(Producer_Consumer).cpp文件:
模拟一个生产者一个消费者的同步问题。
2-4(Producer_Consumer)1.cpp文件:
模拟两个生产者两个消费者的同步问题。
实验三Windows线程通信
【开发语言及实现平台或实验环境】
C++/C#
MicrosoftVisualStudio6.0/MicrosoftVisualStudio.NET
【实验目的】
(1)了解Window线程通信方法;
(2)了解匿名管道,并通过查阅资料理解匿名管道的使用方法;
(3)了解命名管道,并通过查阅资料理解命名管道的使用方法;
【实验要求】
(1)逐程序进行简要分析、运行各程序并仔细阅读注释;
(2)查阅MSDN或其他资料,掌握相关系统调用使用方法和参数含义;
(3)完成实验报告。
【相关知识】
Windows应用程序间数据通讯的基本方式有四种。
最简单的是利用剪切板;另一种是DDE(DynamicDataExchange动态数据交换),它利用一种公共的协议实现两个或多个应用程序之间的通讯;再者是通过内存映射文件,内存映射可以将一个进程的一段
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 天津 科技大学 操作系统 实验 15
![提示](https://static.bdocx.com/images/bang_tan.gif)