:
endl;
:
:
CloseHandle(hMutexSuicide);
}}
intmain(intargc,char*argv[])
{
if(argc>1&&:
:
strcmp(argv[1],“child”)==0)
{Child();}
else
{Parent();}
return0;
}
程序说明了一个进程从“生”到“死”的整个一生。
第一次执行时,它创建一个子进程,其行为如同“父亲”。
在创建子进程之前,先创建一个互斥的内核对象,其行为对于子进程来说,如同一个“自杀弹”。
当创建子进程时,就打开了互斥体并在其他线程中进行别的处理工作,同时等待着父进程使用ReleaseMutex()API发出“死亡”信号。
然后用Sleep()API调用来模拟父进程处理其他工作,等完成时,指令子进程终止。
当调用ExitProcess()时要小心,进程中的所有线程都被立刻通知停止。
在设计应用程序时,必须让主线程在正常的C++运行期关闭(这是由编译器提供的缺省行为)之后来调用这一函数。
当它转向受信状态时,通常可创建一个每个活动线程都可等待和停止的终止事件。
在正常的终止操作中,进程的每个工作线程都要终止,由主线程调用ExitProcess()。
接着,管理层对进程增加的所有对象释放引用,并将用GetExitCodeProcess()建立的退出代码从STILL_ACTIVE改变为在ExitProcess()调用中返回的值。
最后,主线程对象也如同进程对象一样转变为受信状态。
等到所有打开的句柄都关闭之后,管理层的对象管理器才销毁进程对象本身。
还没有一种函数可取得终止后的进程对象为其参数,从而使其“复活”。
当进程对象引用一个终止了的对象时,有好几个API函数仍然是有用的。
进程可使用退出代码将终止方式通知给调用GetExitCodeProcess()的其他进程。
同时,GetProcessTimes()API函数可向主调者显示进程的终止时间。
先分析程序功能,再写出运行结果:
1)_第一次执行时,它创建一个子进程,其行为如同“父亲”。
表示:
_Creatingthechildprocess.
2)______用Sleep()API调用来模拟父进程处理其他工作,等完成时,指令子进程终止。
表示:
Tellingthechildprocesstoquit
在熟悉源代码的基础上,利用本实验介绍的API函数来尝试改进本程序(例如使用GetProcessTimes()API函数)并运行。
请描述你所做的工作:
____GetProcessTimes()API可向主调者显示进程终止时间___________________
_
四、实验总结
进程具有的特征:
结构特征、动态性、并发性、独立性和异步性。
对于进程的定义可以从不同的角度来说,其中较为典型的定义有:
(1)进程是程序的一次执行
(2)进程是一个程序及其数据在处理机上顺序执行时发生的活动
(3)进程是程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。
对于传统os中的进程定义为:
进程是进程实体的运行过程,使系统进行资源分配和调度的一个独立单位。
进程有三种基本状态:
就绪状态、执行状态、阻塞状态。
创建一个进程:
(1)、申请空白的PCB
(2)为进城分配资源
(3)初始化进程控制块
(4)将进程插入就绪队列
终止一个进程:
(1)根据被终止进程的标识符,从PCB集合中检索出该进程的PCB,从中读出该进程的状态
(2)若终止进程正处于执行状态,应立即中止该进程的执行,并置调度标志为真,用于
指示该进程被终止进程的后应该重新进行调度
(3)若该进程还有子孙进程,还应该将其所有的子孙进程终止,以防止他们成为不可控的进程
(4)将终止进程所拥有的全部资源,或者归还给其父进程,或者归还给系统
(5)将终止进程PCB从所在队列中移除,等待其他程序来搜索信
通过实验更清楚的了解了进程,理解了进程的创建过程和终止过程
实验二并发与调度
一、实验目的
在本实验中,通过对事件和互斥体对象的了解,来加深对Windows2000线程同步的理解。
通过分析实验程序,了解管理事件对象的API。
了解在进程中如何使用事件对象,在进程中如何使用互斥体对象,线程如何通过文件映射对象发送数据。
在LinuxRedhat操作系统平台上,用pipe()创建一个管道文件,然后用fork()创建两个生产进程和两个消费进程,它们之间通过pipe()传递消息。
二、实验环境
硬件环境:
计算机一台,局域网环境;
软件环境:
Windows2000Professional,LinuxRedhat操作系统平台,VisualC++企业版。
三、实验内容和步骤
第一部分:
互斥体对象
本程序中显示的类CCountUpDown使用了一个互斥体来保证对两个线程间单一数值的访问。
每个线程都企图获得控制权来改变该数值,然后将该数值写入输出流中。
创建者实际上创建的是互斥体对象,计数方法执行等待并释放,为的是共同使用互斥体所需的资源(因而也就是共享资源)。
利用互斥体保护共享资源
.(低32位)
0);.(低32位)
NULL);.(低32位)
0);."<:
endl;
:
:
WaitForSingleObject(hThread,INFINITE);
}
:
:
Sleep(500);
__MapViewOfFile()
b.___UnmapViewOfFile(pData)__________________________________________
3)运行时,程序首先通过(CreateFileMapping())函数创建一个小型的文件映射对象(hMapping),接着,使用系统API函数(g_hMutexMapping)再创建一个保护其应用的互斥体(CreateMutex())。
然后,应用程序创建100个线程,每个都允许进行同样的进程,即:
通过互斥体获得访问权,这个操作是由语句:
_for(intnTotal=100;nTotal>0;--nTotal)
{
.(低32位)
0);
hread_request[k]=-1;
Thread_Info[j].n_request=0;
}
erial;
inFile>>Thread_Info[n_Thread].entity;
inFile>>Thread_Info[n_Thread].delay;
charc;
(c);
while(c!
='\n'&&!
()){
inFile>>Thread_Info[n_Thread].thread_request[Thread_Info[n_Thread].n_request++];
(c);
}
n_Thread++;
}
erial;
charTemp_entity=Thread_Info[j].entity;
doubleTemp_delay=Thread_Info[j].delay;
printf("\nthread%2d%c%f",Temp_serial,Temp_entity,Temp_delay);
intTemp_request=Thread_Info[j].n_request;
for(intk=0;kprintf("%d",Thread_Info[j].thread_request[k]);
cout<}
printf("\n\n");
ntity=='P')
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(Produce),
&(Thread_Info[i]),0,NULL);
else
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(Consume),
&(Thread_Info[i]),0,NULL);
}
\n");
printf("按任意键返回!
\n");
_getch();
return0;
}
_request;j++)
if(Thread_Info[i].thread_request[j]==req)
returnTRUE;
returnFALSE;
}
n",m_serial);
n",m_serial,ProducePos);
Buffer_Critical[ProducePos]=m_serial;
printf("生产者%2d完成生产过程:
\n",m_serial);
printf("缓冲区[%2d]:
%3d\n",ProducePos,Buffer_Critical[ProducePos]);
rite==1)
printf("将页%d写回磁盘第%d块\n",j,page[j].dnumber);
5、page[j].flag=0;
6、page[lnumber].flag=1;
7、page[lnumber].write=0;
8、page[lnumber].pnumber=page[j].pnumber;
printf("淘汰主存块%2d中的页%2d,从磁盘第%d块中调入页%2d\n",
9、page[j].pnumber10、j11、page[lnumber].dnumber12、lnumber);
);
}
voidcommand(unsignedladdress,intwrite)
{unsignedpaddress,ad,pnumber,lnumber;
kk:
13、lnumber=laddress>>10;
14、ad=laddress&0x3FF;
if(lnumber>=page_length)
{
printf("不存在该页\n");
15、page_interrupt(lnumber);
}
if(page[lnumber].flag==1)
{
16、pnumber=page[lnumber].pnumber;
17、paddress=pnumber<<10|ad;
rite=1;
}
else
{
18、page_interrupt(lnumber);
gotokk;
}
}
main()
{intlnumber,flag,pnumber,write,dnumber;
unsignedladdress;
inti;
printf("输入页表的信息,创建页表(若页号为-1,则结束输入)\n");
printf("输入页号和辅存地址:
");
scanf("%d%d",&lnumber,&dnumber);
i=0;
while(lnumber!
=-1)
{19、page[lnumber].lnumber=lnumber;
20、page[lnumber].dnumber=dnumber;
21、i++;
printf("输入页号和辅存地址:
");
scanf("%d%d",&lnumber,&dnumber);
}
page_length=i;
printf("输入主存块号,主存块数要小于%d,(以-1结束):
",i);
scanf("%d",&pnumber);
m=0;
head=0;
while(pnumber!
=-1)
{if(m<=i)
{
24、p[m]=m;
25、page[m].pnumber=pnumber;
26、page[m].flag=1;
27、m++;
}
Scanf7d",&pnumber);
}
printf("输入指令性质(1-修改,0-不需要,其他-结束程序运行)和逻辑地址:
");
scanf("%d%x",&write,&laddress);
while(write==0||write==1)
{
28、command(laddress,write);
printf("输入指令性质(1-修改,0-不需要,其他-结束程序运行)和逻辑地址:
");
scanf("%d%x",&write,&laddress);
}
}
5、程序运行结果及简要分析
首先,通过键盘依次输入了4个主存块,块号为4,6,8,3,同时,将第0,1,2,3个页面依次调入块4,6,8,3中,将这4个页面的页号相应地存入数组p[0],p[1],p[2],p[3]中,然后再通过键盘依次输入逻辑地址:
23,456,abc,2000,然后分离出逻辑地址高六位的页号和低10位的页内地址,由于前3个逻辑地址对应的页号在主存中,所以将这3个页号对应的主存块号和页内地址合成为物理地址;对于最后1个逻辑地址,由于对应的页号不在主存中,所以要进行缺页处理,采用FIFO算法。
实验总结:
通过本次实验让我明白了许多,做事