操作系统课程设计报告操作系统模拟之进程通信.docx
《操作系统课程设计报告操作系统模拟之进程通信.docx》由会员分享,可在线阅读,更多相关《操作系统课程设计报告操作系统模拟之进程通信.docx(22页珍藏版)》请在冰豆网上搜索。
操作系统课程设计报告操作系统模拟之进程通信
中南大学
操作系统
课程设计报告
学院:
_信息科学与工程学院____
专业班级:
计算机科学与技术09**班
学号:
09090928**
姓名:
***
指导教师:
宋虹
2012年6月
目录
一、概述………………………………………1
二、课程设计要求……………………………1
三、设计的基本原理和概念……………………1
四、详细设计:
使用主要控件、函数…………6
1.线程1函数(获得两个随机数)……6
2.线程2函数(计算它们的和)………7
3.线程3函数(输出结果)……………8
4.信号量控制…………………………8
5.登录以及用户切换………………………8
6.新增用户函数……………………………10
五、运行及使用…………………………………11
六、实验总结…………………………………14
参考文献……………………………………15
一、概述
操作系统课程主要讲述的内容是多道操作系统的原理与技术,与其它计算机原理、编译原理、汇编语言、计算机网络、程序设计等专业课程关系十分密切。
本课程设计的目的综合应用学生所学知识,建立系统和完整的计算机系统概念,理解和巩固操作系统基本理论、原理和方法,掌握操作系统开发的基本技能。
二、课程设计要求
采用VC、VB、java或C等编程语言,模拟开发一个多用户多任务的类操作系统软件。
要求如下:
1.模拟进程调度算法,模拟各种进程调度算法,并能管理各种进程调度算法的演示过程。
2.模拟各种内存分配算法及回收过程,并能动态演示。
结合进程调度算法,能为进程模拟分配内存,并设计一个跟踪该内存模拟分配的进程或线程,记录内存分配的结果;
3.模拟多用户登录管理过程,能允许多个用户登录,为每个用户建立进程或线程,并进行进程管理;
4.能模拟文件系统管理,进程可以对文件系统(包括文件和目录)进行读、写、创建、删除、打开、关闭等操作;
5.模拟磁盘管理过程,结合文件系统管理,显示磁盘管理的结构和文件在磁盘上的分布情况。
6.能模拟进程通信管理,用户之间可以相互通信交流;
7.最好能采用类似于Windows的窗口界面。
三、设计的基本原理和概念
我所做的部分是模拟多用户登录管理过程,能允许多个用户登录,为每个用户建立进程或线程,并进行进程管理。
还有进程间的通信。
进程的用户空间是互相独立的,一般而言是不能互相访问的,唯一的例外是共享内存区。
但是,系统空间却是“公共场所”,所以内核显然可以提供这样的条件。
除此以外,那就是双方都可以访问的外设了。
在这个意义上,两个进程当然也可以通过磁盘上的普通文件交换信息,或者通过“注册表”或其它数据库中的某些表项和记录交换信息。
广义上这也是进程间通信的手段,但是一般都不把这算作“进程间通信”。
进程间通信目的:
a)数据传输:
一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间。
b)共享数据:
多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。
c)通知事件:
一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
d)资源共享:
多个进程之间共享同样的资源。
为了作到这一点,需要内核提供锁和同步机制。
e)进程控制:
有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。
进程可以对任何信号指定另一个动作或重载缺省动作,指定的新动作可以是忽略信号。
进程也可以暂时地阻塞一个信号。
因此进程可以选择对某种信号所采取的特定操作,这些操作包括:
(a)忽略信号:
进程可忽略产生的信号,但SIGKILL和SIGSTOP信号不能被忽略,必须处理(由进程自己或由内核处理)。
进程可以忽略掉系统产生的大多数信号。
(b)阻塞信号:
进程可选择阻塞某些信号,即先将到来的某些信号记录下来,等到以后(解除阻塞后)再处理它。
(c)由进程处理该信号:
进程本身可在系统中注册处理信号的处理程序地址,当发出该信号时,由注册的处理程序处理信号。
(d)由内核进行缺省处理:
信号由内核的缺省处理程序处理,执行该信号的缺省动作。
例如,进程接收到SIGFPE(浮点异常)的缺省动作是产生core并退出。
大多数情况下,信号由内核处理。
对信号的任何处理,包括终止进程,都必须由接收到信号的进程来执行。
而进程要执行信号处理程序,就必须等到它真正运行时。
因此,对信号的处理可能需要延迟一段时间。
信号没有固有的优先级。
如果为一个进程同时产生了两个信号,这两个信号会以任意顺序出现在进程中并会按任意顺序被处理。
另外,也没有机制用于区分同一种类的多个信号。
如果进程在处理某个信号之前,又有相同的信号发出,则进程只能接收到一个信号。
进程间通信主要包括管道,系统IPC(包括消息队列,信号量,共享存储),SOCKET。
操作系统通过信号来通知进程系统中发生了某种预先规定好的事件(一组事件中的一个),它也是用户进程之间通信和同步的一种原始机制。
一个键盘中断或者一个错误条件(比如进程试图访问它的虚拟内存中不存在的位置等)都有可能产生一个信号。
Shell也使用信号向它的子进程发送作业控制信号。
信号量:
它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目.信号量对象对线程的同步方式与前面几种方法不同,信号允许多个线程同时使用共享资源,这与操作系统中的PV操作相同。
它指出了同时访问共享资源的线程最大数目。
它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。
信号的PV操作。
信号量S是一个整数,S大于等于零时代表可供并发进程使用的资源实体数,但S小于零时则表示正在等待使用共享资源的进程数。
P操作申请资源:
(1)S减1;
(2)若S减1后仍大于等于零,则进程继续执行;
(3)若S减1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转入进程调度。
V操作释放资源:
(1)S加1;
(2)若相加结果大于零,则进程继续执行;
(3)若相加结果小于等于零,则从该信号的等待队列中唤醒一个等待进程,然后再返回原进程继续执行或转入进程调度。
本次试验,我采用的线程模拟进程间的通信.
线程主要有以下几个操作
(a)线程的启动
创建一个线程,首先要从类CwinThread产生一个派生类,同时必须使用DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE来声明和实现这个CwinThread派生类。
第二步是根据需要重载该派生类的一些成员函数如:
ExitInstance()、InitInstance()、OnIdle()、PreTranslateMessage()等函数。
最后调用AfxBeginThread()函数的一个版本:
CWinThread*AfxBeginThread(CRuntimeClass*pThreadClass,intnPriority=THREAD_PRIORITY_NORMAL,UINTnStackSize=0,DWORDdwCreateFlags=0,LPSECURITY_ATTRIBUTESlpSecurityAttrs=NULL)启动该用户界面线程,其中第一个参数为指向定义的用户界面线程类指针变量,第二个参数为线程的优先级,第三个参数为线程所对应的堆栈大小,第四个参数为线程创建时的附加标志,缺省为正常状态,如为CREATE_SUSPENDED则线程启动后为挂起状态。
对于工作线程来说,启动一个线程,首先需要编写一个希望与应用程序的其余部分并行运行的函数如Fun1(),接着定义一个指向CwinThread对象的指针变量*pThread,调用AfxBeginThread(Fun1,param,priority)函数,返回值赋给
pThread变量的同时一并启动该线程来执行上面的Fun1()函数,其中Fun1是线程要运行的函数的名字,也既是上面所说的控制函数的名字,param是准备传送给线程函数Fun1的任意32位值,priority则是定义该线程的优先级别,它是预定义的常数,读者可参考MSDN。
(b)线程的悬挂和恢复
CWinThread类中包含了应用程序悬挂和恢复它所创建的线程的函数,其中SuspendThread()用来悬挂线程,暂停线程的执行;ResumeThread()用来恢复线程的执行。
如果你对一个线程连续若干次执行SuspendThread(),则需要连续执行相应次的ResumeThread()来恢复线程的运行。
(c)线程的悬挂和恢复
CWinThread类中包含了应用程序悬挂和恢复它所创建的线程的函数,其中SuspendThread()用来悬挂线程,暂停线程的执行;ResumeThread()用来恢复线程的执行。
如果你对一个线程连续若干次执行SuspendThread(),则需要连续执行相应次的ResumeThread()来恢复线程的运行。
(d)结束线程
终止线程有三种途径,线程可以在自身内部调用AfxEndThread()来终止自身的运行;可以在线程的外部调用BOOLTerminateThread(HANDLEhThread,DWORDdwExitCode)来强行终止一个线程的运行,然后调用CloseHandle()函数释放线程所占用的堆栈;第三种方法是改变全局变量,使线程的执行函数返回,则该线程终止。
下面是第三种方式的代码:
CtestViewmessagehandlers
SettoTruetoendthread
Boolbend=FALSE;//定义的全局变量,用于控制线程的运行;
TheThreadFunction;
UINTThreadFunction(LPVOIDpParam)//线程函数
{
while(!
bend)
{
Beep(100,100);
Sleep(1000);
}
return0;
}
CwinThread*pThread;
HWNDhWnd;
VoidCtestView:
:
OninitialUpdate()
{
hWnd=GetSafeHwnd();
pThread=AfxBeginThread(ThradFunction,hWnd);//启动线程
pThread->m_bAutoDelete=FALSE;//线程为 Cview:
:
OnInitialUpdate();
}
VoidCtestView:
:
OnDestroy()
{
bend=TRUE;//改变变量,线程结束
WaitForSingleObject(pThread->m_hThread,INFINITE);//等待线程结束
deletepThread;//删除线程
Cview:
:
OnDestroy();
共享内存式的通信方式
通常由一个进程创建,其余进程对这块内存区进行读写。
得到共享内存有两种方式:
映射/dev/mem设备和内存映像文件。
前一种方式不给系统带来额外的开销,但在现实中并不常用,因为它控制存取的是实际的物理内存;常用的方式是通过shmXXX函数族来实现共享内存:
intshmget(key_tkey,intsize,intflag);/*获得一个共享存储标识符*/
该函数使得系统分配size大小的内存用作共享内存;
void*shmat(intshmid,void*addr,intflag);/*将共享内存连接到自身地址空间中*/
如果一个进程通过fork创建了子进程,则子进程继承父进程的共享内存,既而可以直接对共享内存使用,不过子进程可以自身脱离共享内存。
shmid为shmget函数返回的共享存储标识符,addr和flag参数决定了以什么方式来确定连接的地址,函数的返回值即是该进程数据段所连接的实际地址。
此后,进程可以对此地址进行读写操作访问共享内存。
对于共享内存,linux本身无法对其做同步,需要程序自己来对共享的内存做出同步计算,而这种同步很多时候就是用信号量实现。
共享内存是存在于内核级别的一种资源,在shell中可以使用ipcs命令来查看当前系统IPC中的状态,在文件系统/proc目录下有对其描述的相应文件。
函数shmget可以创建或打开一块共享内存区。
函数原型如下:
#include
intshmget(key_tkey,size_tsize,intflag);
函数中参数key用来变换成一个标识符,而且每一个IPC对象与一个key相对应。
当新建一个共享内存段时,size参数为要请求的内存长度(以字节为单位)。
注意:
内核是以页为单位分配内存,当size参数的值不是系统内存页长的整数倍时,系统会分配给进程最小的可以满足size长的页数,但是最后一页的剩余部分内存是不可用的。
当打开一个内存段时,参数size的值为0。
参数flag中的相应权限位初始化ipc_perm结构体中的mode域。
同时参数flag是函数行为参数,它指定一些当函数遇到阻塞或其他情况时应做出的反应。
四、详细设计:
使用主要控件、函数;
详细设计:
使用主要控件、函数;
本次试验采用三个线程线程来模拟,采用共享内存的方式。
HANDLEhEvent;//事件对象信号量
HANDLEhSemap;//线程2.3之间的信号量
1、线程1函数(获得两个随机数)
DWORDWINAPIinput(LPVOIDlpParameter)
{
CvosDlg*Dlg=(CvosDlg*)lpParameter;
inti=0;
while(i<100)
{
Dlg->editVal_1.Format(_T("%d"),rand()%100);
Dlg->editVal_2.Format(_T("%d"),rand()%100);
Dlg->m_Edit_1.SetWindowTextW(Dlg->editVal_1);
Dlg->m_Edit_2.SetWindowTextW(Dlg->editVal_2);
SetEvent(hEvent);//v操作
i++;
Sleep(1000);
}
return0;
}
2、线程2函数(计算它们的和)
DWORDWINAPIcalculate(LPVOIDlpParameter)
{
CvosDlg*Dlg=(CvosDlg*)lpParameter;
inti=0;
while(i<100)
{
WaitForSingleObject(hEvent,INFINITE);//p操作
Sleep(200);
intsum=_tstoi(Dlg->editVal_1)+_tstoi(Dlg->editVal_2);
Dlg->editVal_3.Format(_T("%s+%s=%d"),Dlg->editVal_1,Dlg->editVal_2,sum);
Dlg->m_Edit_3.SetWindowTextW(Dlg->editVal_3);
Dlg->editVal_4.Format(_T("%d"),sum);
ReleaseSemaphore(hSemap,1,NULL);
i++;
}
CloseHandle(hEvent);
return0;
}
3、线程3函数(输出结果)
DWORDWINAPIprint(LPVOIDlpParameter)
{
CvosDlg*Dlg=(CvosDlg*)lpParameter;
inti=0;
while(i<100)
{
WaitForSingleObject(hSemap,INFINITE);
Sleep(200);
Dlg->m_Edit_4.SetWindowTextW(Dlg->editVal_4);
i++;
}
CloseHandle(hSemap);
return0;
}
4、信号量控制
voidCvosDlg:
:
OnBnClickedProcom()//进程通信按钮函数
{
//TODO:
在此添加控件通知处理程序代码
hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);//使用事件对象
hSemap=CreateSemaphore(NULL,0,1,NULL);//使用信号量
CloseHandle(CreateThread(NULL,0,input,this,0,NULL));
CloseHandle(CreateThread(NULL,0,calculate,this,0,NULL));
CloseHandle(CreateThread(NULL,0,print,this,0,NULL));
5、登录以及用户切换
密码和账号保存在pwd.ini文档内
voidChangeUsr:
:
DoDataExchange(CDataExchange*pDX)
{
CDialogEx:
:
DoDataExchange(pDX);
DDX_Control(pDX,IDC_COMBO1,m_usrList);
for(inti=0;i{
m_usrList.AddString(usrName[i]);
}
DDX_Control(pDX,IDC_EDIT1,m_ChangPwd);
}
BEGIN_MESSAGE_MAP(ChangeUsr,CDialogEx)
ON_BN_CLICKED(IDOK,&ChangeUsr:
:
OnBnClickedOk)
END_MESSAGE_MAP()
//ChangeUsr消息处理程序
voidChangeUsr:
:
OnBnClickedOk()
{
//TODO:
在此添加控件通知处理程序代码
index=m_usrList.GetCurSel();
m_usrList.GetWindowTextW(Name);
if(!
checkPwd())
{
Name="";
}
CDialogEx:
:
OnOK();
}
boolChangeUsr:
:
checkPwd()
{
CStringUsrpwd;
ifstreampwd;
m_ChangPwd.GetWindowTextW(Usrpwd);
pwd.open("pwd.ini");
if(!
pwd.is_open())
{
exit(0);
}
charname[20]={0};
charpassword[30]={0};
while(!
pwd.eof())
{
pwd>>name;
pwd>>password;
if(Name==(CString)name)
{
if(Usrpwd==(CString)password)
{
returntrue;
}
else
{
MessageBox(_T("密码错误"),_T("警告"),MB_OK|MB_ICONERROR);
returnfalse;
}
}
}
if(pwd.eof())//是否到文件尾
{
if(IDYES==MessageBox(_T("是否要添加用户?
"),_T("提示"),MB_YESNO|MB_ICONQUESTION))
{
AddUsr();
returntrue;
}
}
pwd.close();
returnfalse;
}
6、新增用户函数
voidChangeUsr:
:
AddUsr()
{
ofstreamadd;
add.open("pwd.ini",ios:
:
app|ios:
:
out);
if(!
add.is_open())
{
exit(0);
}
CStringpwdstr;
m_ChangPwd.GetWindowTextW(pwdstr);
add<<"\n";
stringstr=CStringA(Name);
add<str=CStringA(pwdstr);
add<add.close();
}
五、运行及使用
系统运行后提示输入用户
用户信息的保存内容,为明文保存
系统登录后的运行效果截图
切换用户
显示为add用户
若用户不存在时会提示是否新增用户
增加新的用户后的工作界面
六、实验总结
进程通信主要实现进程间通信要求,
通过此次课程设计我了解了文件系统和文件系统驱动程序的一般原理,理解了进程间通信的工作机制,对windows操作系统有了更深一层的理解。
深刻理解了main函数的调用和执行,以及程序的编译链接和执行过程,还有有关环境变量的一些知识。
开始时对于信号量数目的个数我犹豫了很久,想用一个然后设置它有三个值,可是在实际的操作过程中发现这样运行的效率很低,真个程序的运行速度回大打折扣,是以空间换时间的方式。
所以我最后采取了设置两个信号量,这样整个程序的运行的速度很提高很多。
在程序的开发过程中,我们经常用到一些头文件,这些头文件中主要包括函数的声明,数据结构的定义,宏定义等信息。
程序在编译的时候会将这些头文件也一起编译进来,而在链接的时候程序才考虑具体是怎么实现的。
系统的登录和新增密码过程比较简单,然后分配的线的线程只是在初始化的时候加上了就可以了,此处没有什么技术难题。
我们小组虽然一开始就统一了以下变量和函数的命名规则和方法,可是在进行整合时仍然还是有很多的冲突,在统一变量我们就花费了很多的时间。
所以在以后的编程中必须更加细致的描述变量的命名和尽量的统一,这可能也是架构师的任务吧。
这样也会减少我们很多的工作量,加快我们代码的开发效率。
此次课程设计让我学到了一些知识,对专业知识有了更深一层次的理解,增强了以后学习的信心,同时也发现我还有很多知识要学习,我要更加努力!
七、参考文献
(1)胡志刚等.计算机操作系统.中南大学出版社,2005
(2)陈向群等.Windows内核实验教程.机械工业出版社,2004