《合肥工业大学操作系统实验报告(共47页).docx》由会员分享,可在线阅读,更多相关《合肥工业大学操作系统实验报告(共47页).docx(47页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、精选优质文档-倾情为你奉上操作系统实验报告班级:计算机科学与技术姓名:学号: 实验3 进程的创建一、实验目的 练习使用 EOS API 函数 CreateProcess 创建一个进程,掌握创建进程的方法,理解进程和程序的区别。 调试跟踪 CreateProcess 函数的执行过程,了解进程的创建过程,理解进程是资源分配的单位。二、实验过程记录1./*Hello.c*/#include EOSApp.hint main(int argc, char* argv)int i;for (i = 1; i Pas = MmCreateProcessAddressSpace(); 添加一个断点。 2.按
2、 F5 继续调试,到此断点处中断。 3.按 F10 执行此行代码后中断。 4.在“监视”窗口中查看进程控制块的成员变量 Pas 的值已经不再是 0。说明已经初始化了进程的4G 虚拟地址空间。5.使用 F10 一步步调试 PspCreateProcessEnvironment 函数中后面的代码,在调试的过程中根据执行的源代码,查看“监视”窗口中*NewProcess 表达式的值,观察进程控制块中哪些成员变量是被哪些代码初始化的,哪些成员变量还没有被初始化。 6.当从 PspCreateProcessEnvironment 函数返回到 PsCreateProcess 函数后,停止按 F10。此时“
3、监视”窗口中已经不能再显示表达式*NewProcess 的值了,在 PsCreateProcess 函数中是使用ProcessObject 指针指向进程控制块的,所以将表达式*ProcessObject 添加到“监视”窗口中就可以继续观察新建进程控制块中的信息。 7.接下来继续使用 F10 一步步调试 PsCreateProcess 函数中的代码,同样要注意观察执行后的代码修改了进程控制块中的哪些成员变量。当调试到 PsCreateProcess 函数的最后一行代码时,查看进程控制块中的信息,此时所有的成员变量都已经被初始化了(注意观察成员 ImageName 的值)。 8.按 F5 继续执行
4、,EOS 内核会为刚刚初始化完毕的进程控制块新建一个进程。激活虚拟机窗口查看新建进程执行的结果。 9.在 OS Lab 中选择“调试”菜单中的“停止调试”结束此次调试。 10.选择“调试”菜单中的“删除所有断点”。验证:在PsCreateProcess 函数中首先调用了 PspCreateProcessEnvironment 函数来创建进程控制块. ObCreateObject 函数会在由 EOS 内核管理的内存中创建了一个新的进程控制块(也就是分配了一块内存),并由 NewProcess 返回进程控制块的指针(也就是所分配内存的起始地址)数据展示:ObCreateObject 函数创建新的进
5、程控制块时*NewProcess, 还没有初始化其中成员变量,所以值都为 0。初始化进程控制块中各个成员变量的过程中, 进程控制块的成员变量 Pas 的值已经不再是0。3./* NewTwoProc.c*/#include EOSApp.hint main(int argc, char* argv)STARTUPINFO StartupInfo;PROCESS_INFORMATION ProcInfoOne, ProcInfoTwo;ULONG ulExitCode;/ 子进程退出码INT nResult = 0;/ main 函数返回值。0 表示成功,非 0 表示失败。#ifdef _DEB
6、UG_asm(int $3n nop);#endifprintf(Create two processes and wait for the processes exit.nn);/ 使子进程和父进程使用相同的标准句柄。/StartupInfo.StdInput = GetStdHandle(STD_INPUT_HANDLE);StartupInfo.StdOutput = GetStdHandle(STD_OUTPUT_HANDLE);StartupInfo.StdError = GetStdHandle(STD_ERROR_HANDLE);/ 为一个应用程序同时创建两个子进程。/if (C
7、reateProcess(A:Hello.exe, NULL, 0, &StartupInfo, &ProcInfoOne)& CreateProcess(A:Hello.exe, NULL, 0, &StartupInfo, &ProcInfoTwo) / 创建子进程成功,等待子进程运行结束。/WaitForSingleObject(ProcInfoOne.ProcessHandle, INFINITE);WaitForSingleObject(ProcInfoTwo.ProcessHandle, INFINITE);/ 得到并输出子进程的退出码。/GetExitCodeProcess(Pr
8、ocInfoOne.ProcessHandle, &ulExitCode);printf(nThe process one exit with %d.n, ulExitCode);GetExitCodeProcess(ProcInfoTwo.ProcessHandle, &ulExitCode);printf(nThe process two exit with %d.n, ulExitCode);/ 关闭不再使用的句柄。/CloseHandle(ProcInfoOne.ProcessHandle);CloseHandle(ProcInfoOne.ThreadHandle);CloseHand
9、le(ProcInfoTwo.ProcessHandle);CloseHandle(ProcInfoTwo.ThreadHandle); else printf(CreateProcess Failed, Error code: 0x%X.n, GetLastError();nResult = 1;return nResult;作用: 在eosapp.exe父进程下,为hello.exe创建两个子进程,分别等待两个子进程结束,得到退出码后关闭句柄。结果:交替分别显示两遍Hello,world! (15)”以及” Bye-bye!”。三、思考与练习3.在 PsCreateProcess 函数中调
10、用了 PspCreateProcessEnvironment 函数后又先后调用了PspLoadProcessImage 和 PspCreateThread 函数,学习这些函数的主要功能。能够交换这些函数被调用的顺序吗?思考其中的原因。答: PspCreateProcessEnvironment的主要功能是创建进程控制块,并且为进程创建地址空间和分配句柄表。PspLoadProcessImage是将进程的可执行映像加载到进程的地址空间中。PspCreateThread创建了进程的主线程。这三个函数被调用的顺序是不能够改变的。加载可执行映像之前必须已经为进程分配了地址空间,这样才能够确定可执行映像
11、可以被加载到内存的位置;在创建主线程之前必须已经加载过可执行映像,这样主线程才知道指令分工以及开始执行的位置。因此不能交换他们的顺序。四、备注说明在机房完成。实验5 进程的同步一、实验目的 使用 EOS 的信号量,编程解决生产者消费者问题,理解进程同步的意义。 调试跟踪 EOS 信号量的工作过程,理解进程同步的原理。 修改 EOS 的信号量算法,使之支持等待超时唤醒功能(有限等待),加深理解进程同步的原理。二、实验过程记录1./*pc.c*/#include EOSApp.h/ 缓冲池。/#define BUFFER_SIZE10int BufferBUFFER_SIZE;/ 产品数量。/#d
12、efine PRODUCT_COUNT30/ 用于生产者和消费者同步的对象句柄。/HANDLE MutexHandle;HANDLE EmptySemaphoreHandle;HANDLE FullSemaphoreHandle;/ 生产者和消费者的线程函数/ULONG Producer(PVOID Param);ULONG Consumer(PVOID Param);/ main 函数参数的意义:/ argc - argv 数组的长度,大小至少为 1,argc - 1 为命令行参数的数量。/ argv - 字符串指针数组,数组长度为命令行参数个数 + 1。其中 argv0 固定指向当前/ 进
13、程所执行的可执行文件的路径字符串,argv1 及其后面的指针指向各个命令行/ 参数。/ 例如通过命令行内容 a:hello.exe -a -b 启动进程后,hello.exe 的 main 函/ 数的参数 argc 的值为 3,argv0 指向字符串 a:hello.exe,argv1 指向/ 参数字符串 -a,argv2 指向参数字符串 -b。/int main(int argc, char* argv)HANDLE ProducerHandle;HANDLE ConsumerHandle;#ifdef _DEBUG_asm(int $3n nop);#endif/ 创建用于互斥访问缓冲池的
14、 Mutex 对象。/MutexHandle = CreateMutex(FALSE, NULL);if (NULL = MutexHandle) return 1;/ 创建 Empty 信号量,表示缓冲池中空缓冲区数量。初始计数和最大计数都为 BUFFER_SIZE。/EmptySemaphoreHandle = CreateSemaphore(BUFFER_SIZE, BUFFER_SIZE, NULL);if (NULL = EmptySemaphoreHandle) return 2;/ 创建 Full 信号量,表示缓冲池中满缓冲区数量。初始计数为 0,最大计数为 BUFFER_SIZ
15、E。/FullSemaphoreHandle = CreateSemaphore(0, BUFFER_SIZE, NULL);if (NULL = FullSemaphoreHandle) return 3;/ 创建生产者线程。/ProducerHandle = CreateThread( 0,/ 默认堆栈大小 Producer,/ 线程函数入口地址 NULL,/ 线程函数参数 0,/ 创建标志 NULL );/ 线程 IDif (NULL = ProducerHandle) return 4;/ 创建消费者线程。/ConsumerHandle = CreateThread( 0, Consu
16、mer, NULL, 0, NULL );if (NULL = ConsumerHandle) return 5;/ 等待生产者线程和消费者线程结束。/WaitForSingleObject(ProducerHandle, INFINITE);WaitForSingleObject(ConsumerHandle, INFINITE);/ 关闭句柄/CloseHandle(MutexHandle);CloseHandle(EmptySemaphoreHandle);CloseHandle(FullSemaphoreHandle);CloseHandle(ProducerHandle);Close
17、Handle(ConsumerHandle);return 0;/ 生产者线程函数。/ULONG Producer(PVOID Param) int i;int InIndex = 0;for (i = 0; i PRODUCT_COUNT; i+) WaitForSingleObject(EmptySemaphoreHandle, INFINITE);WaitForSingleObject(MutexHandle, INFINITE);printf(Produce a %dn, i);BufferInIndex = i;InIndex = (InIndex + 1) % BUFFER_SIZ
18、E;ReleaseMutex(MutexHandle);ReleaseSemaphore(FullSemaphoreHandle, 1, NULL);/ 休息一会。每 500 毫秒生产一个数。/Sleep(500);return 0;/ 消费者线程函数。/ULONG Consumer(PVOID Param)int i;int OutIndex = 0;for (i = 0; i PRODUCT_COUNT; i+) WaitForSingleObject(FullSemaphoreHandle, INFINITE);WaitForSingleObject(MutexHandle, INFIN
19、ITE);printf(tttConsume a %dn, BufferOutIndex);OutIndex = (OutIndex + 1) % BUFFER_SIZE;ReleaseMutex(MutexHandle);ReleaseSemaphore(EmptySemaphoreHandle, 1, NULL);/ 休息一会儿。让前 10 个数的消费速度比较慢,后面的较快。/if (i 创建Empty信号量对象-创建Full信号量对象-创建生产者线程-创建消费者线程-等待生产者线程和消费者线程结束-关闭句柄结果: 调试 EOS 信号量的工作过程创建信号量1. 按 F5 启动调试 EOS
20、应用项目。OS Lab 会首先弹出一个调试异常对话框。 2. 在调试异常对话框中选择“是”,调试会中断。 3. 在 main 函数中创建 Empty 信号量的代码行(第 77 行) EmptySemaphoreHandle = CreateSemaphore(BUFFER_SIZE, BUFFER_SIZE, NULL); 添加一个断点。 4. 按 F5 继续调试,到此断点处中断。 5. 按 F11 调试进入 CreateSemaphore 函数。可以看到此 API 函数只是调用了 EOS 内核中的PsCreateSemaphoreObject 函数来创建信号量对象。 6. 按 F11 调试进
21、入 semaphore.c 文件中的 PsCreateSemaphoreObject 函数。在此函数中,会在 EOS内核管理的内存中创建一个信号量对象(分配一块内存),而初始化信号量对象中各个成员的操作是在 PsInitializeSemaphore 函数中完成的。 7. 在 semaphore.c 文件的顶部查找到 PsInitializeSemaphore 函数的定义(第 19 行),在此函数的第一行(第 39 行)代码处添加一个断点。 8. 按 F5 继续调试,到断点处中断。观察 PsInitializeSemaphore 函数中用来初始化信号量结构体成员的值,应该和传入 CreateS
22、emaphore 函数的参数值是一致的。 9. 按 F10 单步调试 PsInitializeSemaphore 函数执行的过程,查看信号量结构体被初始化的过程。打开“调用堆栈”窗口,查看函数的调用层次。验证: CreateSemaphore 函数只是调用了 EOS 内核中的PsCreateSemaphoreObject 函数来创建信号量对象, 初始化信号量对象中各个成员的操作是在 PsInitializeSemaphore 函数中完成的。等待信号量(不阻塞)1. 删除所有的断点(防止有些断点影响后面的调试)。 2. 在 eosapp.c 文件的 Producer 函数中,等待 Empty 信
23、号量的代码行(第 144 行) WaitForSingleObject(EmptySemaphoreHandle, INFINITE); 添加一个断点。 3. 按 F5 继续调试,到断点处中断。 4. WaitForSingleObject 函数最终会调用内核中的 PsWaitForSemaphore 函数完成等待操作。所以,在 semaphore.c 文件中 PsWaitForSemaphore 函数的第一行(第 68 行)添加一个断点。 5. 按 F5 继续调试,到断点处中断。 6. 按 F10 单步调试,直到完成 PsWaitForSemaphore 函数中的所有操作。可以看到此次执行并
24、没有进行等待,只是将 Empty 信号量的计数减少了 1(由 10 变为了 9)就返回了。验证:生产者在第一次调用WaitForSingleObject 函数等待 Empty 信号量时,应该不需要阻塞就可以立即返回。数据展示:Empty 信号量的计数由 10 变为了 9就返回了。释放信号量(不唤醒)1. 删除所有的断点(防止有些断点影响后面的调试)。 2. 在 eosapp.c 文件的 Producer 函数中,释放 Full 信号量的代码行(第 152 行) ReleaseSemaphore(FullSemaphoreHandle, 1, NULL); 添加一个断点。 3. 按 F5 继续调
25、试,到断点处中断。 4. 按 F11 调试进入 ReleaseSemaphore 函数。 5. 继续按 F11 调试进入 PsReleaseSemaphoreObject 函数。 6. 先使用 F10 单步调试,当黄色箭头指向第 269 行时使用 F11 单步调试,进入 PsReleaseSemaphore函数。 7. 按 F10 单步调试,直到完成 PsReleaseSemaphore 函数中的所有操作。验证:生产者线程通过等待 Empty 信号量使空缓冲区数量减少了 1,通过释放 Full 信号量使满缓冲区数量增加了 1,这样就表示生产者线程生产了一个产品并占用了一个缓冲区。数据展示: 完
26、成 PsReleaseSemaphore 函数中的所有操作没有唤醒其它线程(因为此时没有线程在 Full 信号量上被阻塞),只是将 Full 信号量的计数增加了 1(由 0 变为了 1)。等待信号量(阻塞)1. 结束之前的调试。 2. 删除所有的断点。 3. 按 F5 重新启动调试。OS Lab 会首先弹出一个调试异常对话框。 4. 在调试异常对话框中选择“是”,调试会中断。 5. 在 semaphore.c 文件中的 PsWaitForSemaphore 函数的 PspWait(&Semaphore-WaitListHead, INFINITE); 代码行(第 78 行)添加一个断点。 6.
27、 按 F5 继续调试,并立即激活虚拟机窗口查看输出。开始时生产者、消费者都不会被信号量阻塞,同步执行一段时间后才在断点处中断。 7. 中断后,查看“调用堆栈”窗口,有 Producer 函数对应的堆栈帧,说明此次调用是从生产者线程函数进入的。 8. 在“调用堆栈”窗口中双击 Producer 函数所在的堆栈帧,绿色箭头指向等待 Empty 信号量的代码行,查看 Producer 函数中变量 i 的值为 14,表示生产者线程正在尝试生产 14 号产品。 9. 在“调用堆栈”窗口中双击 PsWaitForSemaphore 函数的堆栈帧,查看 Empty 信号量计数(Semaphore-Count
28、)的值为-1,所以会调用 PspWait 函数将生产者线程放入 Empty信号量的等待队列中进行等待(让出 CPU)。 10. 激活虚拟机窗口查看输出的结果。验证: 当缓冲池中所有的缓冲区都被产品占用时,生产者在生产新的产品时会被阻塞.数据展示: Semaphore-Count值为-1; 生产了从 0 到 13 的 14 个产品,但是只消费了从0到3的4个产品。释放信号量(唤醒)1. 删除所有断点。 2. 在 eosapp.c 文件的 Consumer 函数中,释放 Empty 信号量的代码行(第 180 行) ReleaseSemaphore(EmptySemaphoreHandle, 1,
29、 NULL); 添加一个断点。 3. 按 F5 继续调试,到断点处中断。 4. 查看 Consumer 函数中变量 i 的值为 4,说明已经消费了 4 号产品。 5. 按照 3.3.2.2 中的方法使用 F10 和 F11 调试进入 PsReleaseSemaphore 函数。 6. 查看 PsReleaseSemaphore 函数中 Empty 信号量计数(Semaphore-Count)的值为-1,和生产者线程被阻塞时的值是一致的。 7. 按 F10 单步调试 PsReleaseSemaphore 函数,直到在代码行(第 132 行)PspWakeThread(&Semaphore-Wai
30、tListHead, STATUS_SUCCESS); 处中断。 按照下面的步骤验证生产者线程被唤醒后,是从之前被阻塞时的状态继续执行的: 1. 在 semaphore.c 文件中 PsWaitForSemaphore 函数的最后一行(第 83 行)代码处添加一个断点。 2. 按 F5 继续调试,在断点处中断。 3. 查看 PsWaitForSemaphore 函数中 Empty 信号量计数(Semaphore-Count)的值为 0,和生产者线程被唤醒时的值是一致的。 4. 在“调用堆栈”窗口中可以看到是由 Producer 函数进入的。激活 Producer 函数的堆栈帧,查看Produc
31、er 函数中变量 i 的值为 14,表明之前被阻塞的、正在尝试生产 14 号产品的生产者线程已经从 PspWait 函数返回并继续执行了。 5. 结束此次调试。验证: 只有当消费者线程从缓冲池中消费了一个产品,从而产生一个空缓冲区后,生产者线程才会被唤醒并继续生产 14 号产品。数据展示: (Semaphore-Count)值为-1; Producer 函数中变量 i 的值为e.修改 EOS 的信号量算法STATUSPsWaitForSemaphore(IN PSEMAPHORE Semaphore,IN ULONG Milliseconds)/*+功能描述:信号量的 Wait 操作(P 操作
32、)。参数:Semaphore - Wait 操作的信号量对象。Milliseconds - 等待超时上限,单位毫秒。返回值:STATUS_SUCCESS。当你修改信号量使之支持超时唤醒功能后,如果等待超时,应该返回 STATUS_TIMEOUT。-*/BOOL IntState;STATUS status;ASSERT(KeGetIntNesting() = 0); / 中断环境下不能调用此函数。IntState = KeEnableInterrupts(FALSE); / 开始原子操作,禁止中断。/ 目前仅实现了标准记录型信号量,不支持超时唤醒功能,所以 PspWait 函数/ 的第二个参数
33、的值只能是 INFINITE。/*Semaphore-Count-;if (Semaphore-Count WaitListHead, INFINITE);*/*if (Semaphore-Count 0) Semaphore-Count-;r=STATUS_SUCCESS;if (Semaphore-Count = 0) r=PspWait(&Semaphore-WaitListHead, Milliseconds);*/if (Semaphore-Count 0) Semaphore-Count-;status = STATUS_SUCCESS;else status = PspWait(
34、&Semaphore-WaitListHead, Milliseconds);KeEnableInterrupts(IntState); / 原子操作完成,恢复中断。/return STATUS_SUCCESS;return status;STATUSPsReleaseSemaphore(IN PSEMAPHORE Semaphore,IN LONG ReleaseCount,OUT PLONG PreviousCount)/*+功能描述:信号量的 Signal 操作(V 操作)。参数:Semaphore - Wait 操作的信号量对象。ReleaseCount - 信号量计数增加的数量。当前
35、只能为 1。当你修改信号量使之支持超时唤醒功能后,此参数的值能够大于等于 1。PreviousCount - 返回信号量计数在增加之前的值。返回值:如果成功释放信号量,返回 STATUS_SUCCESS。-*/STATUS Status;BOOL IntState;IntState = KeEnableInterrupts(FALSE); / 开始原子操作,禁止中断。if (Semaphore-Count + ReleaseCount Semaphore-MaximumCount) Status = STATUS_SEMAPHORE_LIMIT_EXCEEDED; else / 记录当前的信号量的值。/if (NULL != PreviousCount) *PreviousCount = Semaphore-Count;/ 目前仅实现了标准记录型信号量,每执行一次信号量的释放操作/ 只能使信号量的值增加 1。/*Semaphore-Count+;if (Semaphore-Count WaitListHead, STATUS_SUCCESS);*/for (Semap
限制150内