通过上面这段极其简洁的代码,完成了看似有些难度的任务,让我们充分感受到采用一些高级的编程手段所带来的便捷与高效。
3. 在上述实验2的基础上利用Windows提供的与进程相关的API函数,编写程序,实现创建进程、运行指定程序和终止进程(如创建写字板或者画图进程及终止该进程)。
要求:有交互界面(命令方式或图形界面方式,推荐使用图形界面的交互),如:
输入1创建进程,提示输入指定程序的路径创建对应的进程,并输出显示进程的相关信息;
输入2关闭该进程;
输入0退出程序。
注:与进程和线程相关的Windows API
(1) CancelWaitableTimer 这个函数用于取消一个可以等待下去的计时器操作 (2) CallNamedPipe 这个函数由一个希望通过管道通信的一个客户进程调用 (3) ConnectNamedPipe 指示一台服务器等待下去,直至客户机同一个命名管道
连接
(4) CreateEvent 创建一个事件对象
(5) CreateMailslot 创建一个邮路。返回的句柄由邮路服务器使用(收件人) (6) CreateMutex 创建一个互斥体(MUTEX)
(7) CreateNamedPipe 创建一个命名管道。返回的句柄由管道的服务器端使用 (8) CreatePipe 创建一个匿名管道
(9) CreateProcess 创建一个新进程(比如执行一个程序) (10) CreateSemaphore 创建一个新的信号机
(11) CreateWaitableTimer 创建一个可等待的计时器对象
(12) DisconnectNamedPipe 断开一个客户与一个命名管道的连接 (13) DuplicateHandle 在指出一个现有系统对象当前句柄的情况下,为那个对象创
建一个新句柄
(14) ExitProcess 中止一个进程
(15) FindCloseChangeNotification 关闭一个改动通知对象
(16) FindExecutable 查找与一个指定文件关联在一起的程序的文件名
(17) FindFirstChangeNotification 创建一个文件通知对象。该对象用于监视文件系
统发生的变化
(18) FindNextChangeNotification 重设一个文件改变通知对象,令其继续监视下一
次变化
(19) FreeLibrary 释放指定的动态链接库
(20) GetCurrentProcess 获取当前进程的一个伪句柄
(21) GetCurrentProcessId 获取当前进程一个唯一的标识符 (22) GetCurrentThread 获取当前线程的一个伪句柄
(23) GetCurrentThreadId 获取当前线程一个唯一的线程标识符 (24) GetExitCodeProces 获取一个已中断进程的退出代码
(25) GetExitCodeThread 获取一个已中止线程的退出代码
(26) GetHandleInformation 获取与一个系统对象句柄有关的信息 (27) GetMailslotInfo 获取与一个邮路有关的信息
(28) GetModuleFileName 获取一个已装载模板的完整路径名称 (29) GetModuleHandle 获取一个应用程序或动态链接库的模块句柄 (30) GetPriorityClass 获取特定进程的优先级别
(31) GetProcessShutdownParameters 调查系统关闭时一个指定的进程相对于其它
进程的关闭早迟情况
(32) GetProcessTimes 获取与一个进程的经过时间有关的信息
(33) GetProcessWorkingSetSize 了解一个应用程序在运行过程中实际向它交付了
多大容量的内存
(34) GetSartupInfo 获取一个进程的启动信息 (35) GetThreadPriority 获取特定线程的优先级别
(36) GetTheardTimes 获取与一个线程的经过时间有关的信息
(37) GetWindowThreadProcessId 获取与指定窗口关联在一起的一个进程和线程
标识符
(38) LoadLibrary 载入指定的动态链接库,并将它映射到当前进程使用的地址空
间
(39) LoadLibraryEx 装载指定的动态链接库,并为当前进程把它映射到地址空间 (40) LoadModule 载入一个Windows应用程序,并在指定的环境中运行
(41) MsgWaitForMultipleObjects 等侯单个对象或一系列对象发出信号。如返回条
件已经满足,则立即返回
(42) SetPriorityClass 设置一个进程的优先级别
(43) SetProcessShutdownParameters 在系统关闭期间,为指定进程设置他相对于其
它程序的关闭顺序
(44) SetProcessWorkingSetSize 设置操作系统实际划分给进程使用的内存容量 (45) SetThreadPriority 设定线程的优先级别
(46) ShellExecute 查找与指定文件关联在一起的程序的文件名 (47) TerminateProcess 结束一个进程 (48) WinExec 运行指定的程序
5.进程调度模拟
编写程序完成单处理机系统中的进程调度,要求采用先来先服务算法、最短CPU运行期优先调度算法、优先级调度算法、多级反馈队列轮转算法。
实验具体包括:首先确定进程控制块的内容和进程控制块的组织方式;然后完成进程创建原语和进程调度原语;最后编写主函数对所做工作进行测试。
1)实验提示与讲解
这个实验主要需要考虑三个问题:如何组织进程、如何创建进程和如何实现处理器调度。
考虑如何组织进程,首先就要设定进程控制块的内容。进程控制块PCB记录
各个进程执行时的情况。不同的操作系统,进程控制块记录的信息内容不一样。操作系统功能越强,软件也越庞大,进程控制块记录的内容也就越多。这里的实验只是用了必不可少的信息。一般操作系统中,无论进程控制块中信息量多少,信息都可以大致分为以下四类:
①标识信息
每个进程都要有一个唯一的标识符,用来标识进程的存在和区别于其他进程。这个标识符是必不可少的,可以用符号或编号实现,它必须是操作系统分配的。在后面给出的参考程序中,采用编号方式,也就是为每个进程依次分配一个不相同的正整数。
②说明信息
用于记录进程的基本情况,例如进程的状态、等待原因、进程程序存放的位置、进程数据存放位置等等。实验中,因为进程没有数据和程序,仅使用进程控制块模拟进程,所以这部分内容仅包括进程状态。
③现场信息
现场信息记录各个寄存器的内容。当进程由于某种原因让出处理器时,需要将现场信息记录在进程控制块中,当进行进程调度时,从选中进程的进程控制块中读取现场信息进行现场恢复。现场信息就是处理器的相关寄存器内容,包括通用寄存器、程序计数器和程序状态字等。在实验中,可选取几个寄存器作为代表。用大写的全局变量AX、BX、CX、DX模拟通用寄存器、大写的全局变量PC模拟程序计数器、大写的全局变量PSW模拟程序状态字。
④管理信息
管理信息记录进程管理和调度的信息。例如进程优先数、进程队列指针等。实验中,仅包括队列指针。
因此,可将进程控制块结构定义如下: struct pcb {
int name; //进程标识符 int status; //进程状态
int AX,BX,CX,DX; //进程现场信息,通用寄存器内容
int PC; //进程现场信息,程序计数器内容 int PSW; //进程现场信息,程序状态字内容 int next; //下一个进程控制块的位置 }
确定进程控制块内容后,要考虑的就是如何将进程控制块组织在一起。多道程序设计系统中,往往同时创建多个进程。在单处理器的情况下,每次只能有一个进程处于运行态、其他的进程处于就绪状态或等待状态。为了便于管理,通常把处于相同状态的进程的进程控制块链接在一起。单处理器系统中,正在运行的进程只有一个。因此,单处理器系统中进程控制块分成一个正在运行进程的进程控制块、就绪进程的进程控制块组织成的就绪队列和等待进程的进程控制块组成的等待队列。由于实验模拟的是进程调度,没有对等待队列的操作,所以实验中只有一个指向正在运行进程的进程控制块指针和一个就绪进程的进程控制块队列指针。操作系统的实现中,系统往往在主存中划分出一个连续的专门区域存放系统的进程控制块,实验中应该用数组模拟这个专门的进程控制块区域,定义如下:
#define n 10 //假定系统允许进程个数为10 struct pcb pcbarea[n]; //模拟进程控制块区域的数组 这样,进程控制块的链表实际上是数据结构中使用的静态链表。进程控制块的链接方式可以采用单向和双向链表,实验中,进程控制块队列采用单向不循环静态链表。为了管理空闲进程控制块,还应该将空闲进程控制块链接成一个队列。
实验示例程序中采用时间片轮转调度算法,这种算法是将进程控制块按照进入就绪队列的先后次序排成队列。关于就绪队列的操作就是从队头摘下一个进程控制块和从队尾挂入一个进程控制块。因此为就绪队列定义两个指针,一个头指针,指向就绪队列的第一个进程控制块;一个尾指针,指向就绪队列的最后一个进程控制块。
实验中指向运行进程的进程控制块指针、就绪队列指针和空闲进程控制块队列指针定义如下:
int run; //定义指向正在运行进程的进程控制块的指针 struct
{
int head; int tail;
}ready; //定义指向就绪队列的头指针head和尾指针tail int pfree; //定义指向空闲进程控制块队列的指针 以上是如何组织进程,下面考虑如何创建进程。
进程创建是一个原语,因此在实验中应该用一个函数实现,进程创建的过程应该包括:
①申请进程控制块:进程控制块的数量是有限的,如果没有空闲进程控制块,则进程不能创建,如果申请成功才可以执行第二步;
②申请资源:除了进程控制块外,还需要有必要的资源才能创建进程,如果申请资源不成功,则不能创建进程,并且归还已申请的进程控制块;如果申请成功,则执行第三步,实验无法申请资源,所以模拟程序忽略了申请资源这一步;
③填写进程控制块:将该进程信息写入进程控制块内,实验中只有进程标识符、进程状态可以填写(模拟进程创建时,需输入进程标识符,进程标识符本应系统建立,并且是唯一的,输入时注意不要冲突),刚创建的进程应该为就绪态,然后转去执行第四步;
④挂入就绪队列:如果原来的就绪队列不为空,则将该进程控制块挂入就绪队列尾部,并修改就绪队列尾部指针;如果原来就绪队列为空,则将就绪队列的头指针、尾指针均指向该进程控制块,进程创建完成。
进程创建流程图如图1所示。