@1405010312
2016-04-21T14:05:08.000000Z
字数 11538
阅读 797
VisualStudio可视化程序设计
银行家算法
报告提交日期:2016.4.20 报告提交截止日期:2016.4.21
姓名:肖罗罗 学号:1405010312 班级:计算机三班
- Windows进程管理
- 1.学会使用VC编写基本的Win32 Consol Application(控制台应用程序).
- 2.通过创建进程、观察正在运行的进程和终止进程的程序设计和调试操作,进一步熟悉操作系统的进程概念,理解Windows进程的“一生”。
- 3.通过阅读和分析实验程序,学习创建进程、观察进程、终止进程以及父子进程同步的基本程序设计方法。
- 带有vs2012的Windows操作系统平台
- 自己操作,了解vs可视化编程设计
Windows 所创建的每个进程都从调用 CreateProcess() API 函数开始,该函数的任务是在对象管 理器子系统内初始化进程对象。每一进程都以调用ExitProcess() 或TerminateProcess() API函数终止。 通常应用程序的框架负责调用 ExitProcess() 函数。对于 C++ 运行库来说,这一调用发生在应用程 序的 main() 函数返回之后。
CreateProcess() 调用的核心参数是可执行文件运行时的文件名及其命令行。表 1-1 详细地列出 了每个参数的类型和名称。
表 1-1 CreateProcess() 函数的参数
参数名称 | 使用目的 |
---|---|
LPCTSTR lpApplivationName | 全部或部分地指明包括可执行代码的 EXE 文件的文件名 |
LPCTSTR lpCommandLine | 向可执行文件发送的参数 |
LPSECURIITY_ATTRIBUTES lpProcessAttributes | 返回进程句柄的安全属性。主要指明这一句柄是否应该由其他 子进程所继承 |
LPSECURIITY_ATTRIBUTES lpThreadAttributes | 返回进程的主线程的句柄的安全属性 |
BOOL bInheritHandle | 一种标志,告诉系统允许新进程继承创建者进程的句柄 |
DWORD dwCreationFlage | 特殊的创建标志 (如 CREATE_SUSPENDED) 的位标记 |
LPVOID lpEnvironment | 向新进程发送的一套环境变量;如为 null 值则发送调用者环境 |
LPCTSTR lpCurrentDirectory | 新进程的启动目录 |
STARTUPINFO lpStartupInfo | STARTUPINFO 结构,包括新进程的输入和输出配置的详情 |
LPPROCESS_INFORMATION lpProcessInformation | 调用的结果块;发送新应用程序的进程和主线程的句柄和 ID |
可以指定第一个参数,即应用程序的名称,其中包括相对于当前进程的当前目录的全路径或者 利用搜索方法找到的路径;lpCommandLine 参数允许调用者向新应用程序发送数据;接下来的三个 参数与进程和它的主线程以及返回的指向该对象的句柄的安全性有关。
然后是标志参数,用以在 dwCreationFlags 参数中指明系统应该给予新进程什么行为。经常使用 的标志是 CREATE_SUSPNDED,告诉主线程立刻暂停。当准备好时,应该使用 ResumeThread() API 来启动进程。另一个常用的标志是 CREATE_NEW_CONSOLE,告诉新进程启动自己的控制台窗口, 而不是利用父窗口。这一参数还允许设置进程的优先级,用以向系统指明,相对于系统中所有其他 的活动进程来说,给此进程多少 CPU 时间。
接着是 CreateProcess() 函数调用所需要的三个通常使用缺省值的参数。第一个参数是
lpEnvironment 参数,指明为新进程提供的环境;第二个参数是 lpCurrentDirectory,可用于向主创进 程发送与缺省目录不同的新进程使用的特殊的当前目录;第三个参数是 STARTUPINFO 数据结构所 必需的,用于在必要时指明新应用程序的主窗口的外观.
CreateProcess() 的后一个参数是用于新进程对象及其主线程的句柄和 ID 的返回值缓冲区。 以 PROCESS_INFORMATION 结构中返回的句柄调用 CloseHandle() API 函数是重要的,因为如果 不将这些句柄关闭的话,有可能危及主创进程终止之前的任何未释放的资源。
如果一个进程拥有至少一个执行线程,则为正在系统中运行的进程。通常,这种进程使用主线 程来指示它的存在。当主线程结束时,调用 ExitProcess() API 函数,通知系统终止它所拥有的所有 正在运行、准备运行或正在挂起的其他线程。当进程正在运行时,可以查看它的许多特性,其中少 数特性也允许加以修改。
首先可查看的进程特性是系统进程标识符 (PID) ,可利用 GetCurrentProcessId() API 函数来查 看,与 GetCurrentProcess() 相似,对该函数的调用不能失败,但返回的 PID 在整个系统中都可使用。 其他的可显示当前进程信息的 API 函数还有 GetStartupInfo()和 GetProcessShutdownParameters() , 可给出进程存活期内的配置详情。
通常,一个进程需要它的运行期环境的信息。例如 API 函数 GetModuleFileName() 和 GetCommandLine() ,可以给出用在 CreateProcess() 中的参数以启动应用程序。在创建应用程序时 可使用的另一个 API 函数是 IsDebuggerPresent()
可利用 API 函数 GetGuiResources() 来查看进程的 GUI 资源。此函数既可返回指定进程中的打 开的 GUI 对象的数目,也可返回指定进程中打开的 USER 对象的数目。进程的其他性能信息可通过 GetProcessIoCounters()、GetProcessPriorityBoost() 、GetProcessTimes() 和 GetProcessWorkingSetSize() API 得到。以上这几个 API 函数都只需要具有 PROCESS_QUERY_INFORMATION 访问权限的指向 所感兴趣进程的句柄。
另一个可用于进程信息查询的 API 函数是 GetProcessVersion() 。此函数只需感兴趣进程的 PID (进程标识号) 。这一 API 函数与 GetVersionEx() 的共同作用,可确定运行进程的系统的版本号。
Windows 2000/XP 提供的常用对象可分成三类:核心应用服务、线程同步和线程间通讯。其中, 开发人员可以使用线程同步对象来协调线程和进程的工作,以使其共享信息并执行任务。此类对象 包括互锁数据、临界段、事件、互斥体和信号等。
多线程编程中关键的一步是保护所有的共享资源,工具主要有互锁函数、临界段和互斥体等; 另一个实质性部分是协调线程使其完成应用程序的任务,为此,可利用内核中的事件对象和信号。
在进程内或进程间实现线程同步的方便的方法是使用事件对象,这一组内核对象允许一个线 程对其受信状态进行直接控制 (见表 1-2) 。
而互斥体则是另一个可命名且安全的内核对象,其主要目的是引导对共享资源的访问。拥有单
3
一访问资源的线程创建互斥体,所有想要访问该资源的线程应该在实际执行操作之前获得互斥体, 而在访问结束时立即释放互斥体,以允许下一个等待线程获得互斥体,然后接着进行下去。
与事件对象类似,互斥体容易创建、打开、使用并清除。利用 CreateMutex() API 可创建互斥体, 创建时还可以指定一个初始的拥有权标志,通过使用这个标志,只有当线程完成了资源的所有的初 始化工作时,才允许创建线程释放互斥体。
表 1-2 用于管理事件对象的 API
API名称 | 描述 |
---|---|
CreateEvent() | 在内核中创建一个新的事件对象。此函数允许有安全性设置、手工还是 自动重置的标志以及初始时已接受还是未接受信号状态的标志 |
OpenEvent() | 创建对已经存在的事件对象的引用。此 API 函数需要名称、继承标志和 所需的访问级别 |
SetEvent() | 将手工重置事件转化为已接受信号状态 |
ResetEvent() | 将手工重置事件转化为非接受信号状态 |
PulseEvent() | 将自动重置事件对象转化为已接受信号状态。当系统释放所有的等待它 的线程时此种转化立即发生 |
为了获得互斥体,首先,想要访问调用的线程可使用 OpenMutex() API 来获得指向对象的句柄; 然后,线程将这个句柄提供给一个等待函数。当内核将互斥体对象发送给等待线程时,就表明该线 程获得了互斥体的拥有权。当线程获得拥有权时,线程控制了对共享资源的访问——必须设法尽快 地放弃互斥体。放弃共享资源时需要在该对象上调用 ReleaseMute() API。然后系统负责将互斥体拥 有权传递给下一个等待着的线程 (由到达时间决定顺序)
- 1.编写基本的 Win32 Consol Application
步骤 1:登录进入 Windows 系统,启动 VC++ 6.0。
步骤 2:在“FILE”菜单中单击“NEW”子菜单,在“projects”选项卡中选择“Win32 Consol Application”,然后在“Project name”处输入工程名,在“Location” 处输入工程目录。创建 一个新的控制台应用程序工程。
- 2.创建进程
步骤 3:在“命令提示符”窗口加入参数重新运行生成的可执行文件,列出运行结果。按下 ctrl+alt+del,调用 windows 的任务管理器,记录进程相关的行为属性
步骤 4:修改清单 1-2 中的程序,将 nClone 的定义和初始化方法按程序注释中的修改方法进行修改,编译成可执行文件(执行前请先保存已经完成的工作)。再按步骤 2 中的方式运行,看看结果会有什么不一样。列出行结果。从中你可以得出什么结论?说明 nClone 的作用。 变量的定义和
初始化方法(位置)对程序的执行结果有影响吗?为什么?
1.修改一没变化
2.带参数也会循环
- 3.父子进程的简单通信及终止进程
- 答:通过main(intargccharargv)传递参数每次运行时先检测argc的值若小于1程序运行结束否则继续往下执行
- 步骤 4:按源程序中注释中的提示,修改源程序 1-3,编译执行,列出运行结果。
- 步骤 5: 参考 MSDN 中的帮助文件 CreateMutex()、OpenMutex()、ReleaseMutex()和 WaitForSingleObject()的使用方法,理解父子进程如何利用互斥体进行同步的。给出父子进程同步过
程的一个大概描述。- 答:CreateMutex()创建互斥体OpenMutex()打开互斥体ReleaseMutex()释放互斥体WaitForSingleObject()检测hHandle事件的信号状态通过这些方法可实现当前只有一个进程被创建或使用实现进程的同步。
1-1
#include<iostream>
void main(){
std::cout << "Hello,Win32 Consol Application" << std::endl;
}
1-2
#include<windows.h>
#include<iostream>
#include<stdio.h>
///创建传递过来开的进程的克隆过程并赋予其ID值
void StartClone(int nCloneID){
///提取用于当前可执行文件的文件名
TCHAR szFilename[MAX_PATH];
GetModuleFileName(NULL, szFilename, MAX_PATH);
///格式化用于子进程的命令行并通知其EXE文件名和克隆ID
TCHAR szCmdLine[MAX_PATH];
sprintf(szCmdLine,"\"%s\"%d", szFilename, nCloneID);
///用于子进程的STARTUPINFO结构
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si); ///必须是本结构的大小
///返回的用于子进程的进程信息
PROCESS_INFORMATION pi;
///利用同样的可执行文件和命令行创建进程,并赋予其子进程的性质
BOOL bCreateOK=::CreateProcess(
szFilename, ///产生这个EXE的应用程序的名称
szCmdLine, ///告诉其行为像一个子进程的标志
NULL, ///缺省的进程安全性
NULL, ///缺省的线程安全性
FALSE, ///不继承句柄
CREATE_NEW_CONSOLE, ///使用新的控制台
NULL, ///新的环境
NULL, ///当前目录
&si, ///启动信息
&pi ///返回的进程信息
);
if(bCreateOK){
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
int main(int argc, char* argv[]){
///确定派生出几个进程,及派生进程在进程列表中的位置
int nClone = 0;
///修改语句: int nClone;
///第一次修改: nClone = 0;
if(argc > 1){
///从第二个参数中提取克隆ID
::sscanf(argv[1], "%d", &nClone);
}
///第二次修改: nClone = 0;
///显示进程位置
std::cout<<"Process ID:" << ::GetCurrentProcessId()<<",CloneID:"<<nClone<<std::endl;
///检查是否有创建子进程的需要
const int c_nCloneMax = 5;
if(nClone < c_nCloneMax){
///发送新进程的命令行和克隆号
StartClone(++nClone);
}
///等待响应键盘输入结束进程
getchar();
return 0;
}
1-3
#include<windows.h>
#include<iostream>
#include<stdio.h>
static LPCTSTR g_szMutexName="w2kdg.ProcTerm.mutex.Suicide";
void StartClone()
{
TCHAR szFilename[MAX_PATH];
GetModuleFileName(NULL,szFilename,MAX_PATH);
TCHAR szCmdLine[MAX_PATH];
sprintf(szCmdLine,"\"%s\"child",szFilename);
STARTUPINFO si;
ZeroMemory(&si,sizeof(si));
si.cb=sizeof(si);
PROCESS_INFORMATION pi;
BOOL bCreateOK=CreateProcess(
szFilename,
szCmdLine,
NULL,
NULL,
FALSE,
CREATE_NEW_CONSOLE,
NULL,
NULL,
&si,
&pi);
if(bCreateOK)
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
void Parent()
{
HANDLE hMutexSuicide=CreateMutex(
NULL,
TRUE,
g_szMutexName);
if(hMutexSuicide!=NULL)
{
std::cout<<"Creating thw child process."<<std::endl;
StartClone();
std::cout<<"Telling the child process to quit."<<std::endl;
getchar();
ReleaseMutex(hMutexSuicide);
CloseHandle(hMutexSuicide);
}
}
void Child()
{
HANDLE hMutexSuicide=OpenMutex(
SYNCHRONIZE,
FALSE,
g_szMutexName);
if(hMutexSuicide!=NULL)
{
std::cout<<"Child waiting for suicide instructions."<<std::endl;
WaitForSingleObject(hMutexSuicide,INFINITE);
std::cout<<"Child quiting."<<std::endl;
CloseHandle(hMutexSuicide);
}
}
int main(int argc,char* argv[])
{
if(argc>1&&::strcmp(argv[1],"child")==0)
{
Child();
}
else
{
Parent();
}
return 0;
}
- 1.掌握银行家算法
- 2.了解死锁,饥饿问题
- 3.学会使用方法来解决死锁问题
- 装有codeblocks的Windows平台
1.对银行家算法不熟悉,犯了很多小错误
2.很久没用c++编写比较大一点的文件了(都是编译大约200行的代码,这次有将近300了)
1-1
#include <stdio.h>
#include <string.h>
#include "banker.h"
//试探分配
void ProbeAlloc(int process,RESOURCE *res)
{
Available.A -= res->A;
Available.B -= res->B;
Available.C -= res->C;
Allocation[process].A += res->A;
Allocation[process].B += res->B;
Allocation[process].C += res->C;
Need[process].A -= res->A;
Need[process].B -= res->B;
Need[process].C -= res->C;
}
//若试探分配后进入不安全状态,将分配回滚
void RollBack(int process,RESOURCE *res)
{
Available.A += res->A;
Available.B += res->B;
Available.C += res->C;
Allocation[process].A -= res->A;
Allocation[process].B -= res->B;
Allocation[process].C -= res->C;
Need[process].A += res->A;
Need[process].B += res->B;
Need[process].C += res->C;
}
//安全性检查
bool SafeCheck()
{
RESOURCE Work = Available;
bool Finish[PROCESSES_NUMBER] = {false,false,false,false,false};
int i;
int j = 0;
for (i = 0; i < PROCESSES_NUMBER; i++)
{
//是否已检查过
if(Finish[i] == false)
{
//是否有足够的资源分配给该进程
if(Need[i].A <= Work.A && Need[i].B <= Work.B && Need[i].C <= Work.C)
{
//有则使其执行完成,并将已分配给该进程的资源全部回收
Work.A += Allocation[i].A;
Work.B += Allocation[i].B;
Work.C += Allocation[i].C;
Finish[i] = true;
safe[j++] = i;
i = -1; //重新进行遍历
}
}
}
//如果所有进程的Finish向量都为true则处于安全状态,否则为不安全状态
for (i = 0; i < PROCESSES_NUMBER; i++)
{
if (Finish[i] == false)
{
return false;
}
}
return true;
}
//资源分配请求
bool request(int process,RESOURCE *res)
{
//request向量需小于Need矩阵中对应的向量
if(res->A <= Need[process].A && res->B <= Need[process].B && res->C <= Need[process].C)
{
//request向量需小于Available向量
if(res->A <= Available.A && res->B <= Available.B && res->C <= Available.C)
{
//试探分配
ProbeAlloc(process,res);
//如果安全检查成立,则请求成功,否则将分配回滚并返回失败
if(SafeCheck())
{
return true;
}
else
{
printf("安全性检查失败。原因:系统将进入不安全状态,有可能引起死锁。\n");
printf("正在回滚...\n");
RollBack(process,res);
}
}
else
{
printf("安全性检查失败。原因:请求向量大于可利用资源向量。\n");
}
}
else
{
printf("安全性检查失败。原因:请求向量大于需求向量。\n");
}
return false;
}
//输出资源分配表
void PrintTable()
{
printf("\t\t\t*********资源分配表*********\n");
printf("Process Max Allocation Need Available\n");
printf(" A B C A B C A B C A B C\n");
printf(" P0 %d %d %d %d %d %d %d %d %d %d %d %d\n",Max[0].A,Max[0].B,Max[0].C,Allocation[0].A,Allocation[0].B,Allocation[0].C,Need[0].A,Need[0].B,Need[0].C,Available.A,Available.B,Available.C);
printf(" P1 %d %d %d %d %d %d %d %d %d\n",Max[1].A,Max[1].B,Max[1].C,Allocation[1].A,Allocation[1].B,Allocation[1].C,Need[1].A,Need[1].B,Need[1].C);
printf(" P2 %d %d %d %d %d %d %d %d %d\n",Max[2].A,Max[2].B,Max[2].C,Allocation[2].A,Allocation[2].B,Allocation[2].C,Need[2].A,Need[2].B,Need[2].C);
printf(" P3 %d %d %d %d %d %d %d %d %d\n",Max[3].A,Max[3].B,Max[3].C,Allocation[3].A,Allocation[3].B,Allocation[3].C,Need[3].A,Need[3].B,Need[3].C);
printf(" P4 %d %d %d %d %d %d %d %d %d\n",Max[4].A,Max[4].B,Max[4].C,Allocation[4].A,Allocation[4].B,Allocation[4].C,Need[4].A,Need[4].B,Need[4].C);
printf("\n");
}
int main()
{
int ch;
printf("先检查初始状态是否安全。\n");
if (SafeCheck())
{
printf("系统处于安全状态。\n");
printf("安全序列是{P%d,P%d,P%d,P%d,P%d}。\n",safe[0],safe[1],safe[2],safe[3],safe[4]);
}
else
{
printf("系统处于不安全状态。程序将退出...\n");
goto over;
}
do
{
int process;
RESOURCE res;
PrintTable();
printf("请依次输入请求分配的进程和对三类资源的请求数量:");
scanf("%d%d%d%d",&process,&res.A,&res.B,&res.C);
if (request(process,&res))
{
printf("分配成功。\n");
printf("安全序列是{P%d,P%d,P%d,P%d,P%d}。\n",safe[0],safe[1],safe[2],safe[3],safe[4]);
}
else
{
printf("分配失败。\n");
}
printf("是否继续分配?(Y/N):");
fflush(stdin);
ch = getchar();
} while (ch == 'Y' || ch == 'y');
over:
printf("执行完毕。");
return 0;
}
1-2
//系统中所有进程数量
#define PROCESSES_NUMBER 5
typedef struct {
int A;
int B;
int C;
}RESOURCE;
//最大需求矩阵
RESOURCE Max[PROCESSES_NUMBER] =
{
{10,10,10},
{10,10,10},
{10,10,10},
{10,10,10},
{10,10,10}
};
//已分配资源数矩阵
RESOURCE Allocation[PROCESSES_NUMBER] =
{
{0,0,0},
{0,0,0},
{0,0,0},
{0,0,0},
{0,0,0}
};
//需求矩阵
RESOURCE Need[PROCESSES_NUMBER] =
{
{1,2,3},
{2,1,3},
{1,3,2},
{2,3,1},
{3,2,1}
};
//可用资源向量
RESOURCE Available = {10,10,10};
int safe[PROCESSES_NUMBER];