基于DOS的多任务系统实现.doc_第1页
基于DOS的多任务系统实现.doc_第2页
基于DOS的多任务系统实现.doc_第3页
基于DOS的多任务系统实现.doc_第4页
基于DOS的多任务系统实现.doc_第5页
已阅读5页,还剩15页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

杭州电子科技大学操作系统课程设计课程设计题目: 基于DOS的多任务系统实现姓名:陈新光班级:050553学号:05055306学院:计算机学院专业:软件工程负责老师:梁红兵完成日期:2008-7-1基于DOS的多任务系统的实现一, 课程设计的目的通过对线程(和进程)的创建和撤销,CPU的调度,同步机制,通信机制的实现,以达到一下目的:1, 加深对线程和进程概念的理解,明确进程和程序的区别。2, 加深对CPU调度过程(现场保护,CPU的分派和现场的恢复)的理解。3, 进一步认识并执行的概念,明确顺序执行和并发执行的区别。4, 加深对临界资源,临界区,信号量以及同步机制的理解。5, 加深对消息缓冲通信的理解。二, 设计要求1, 用C语言完成线程的创建和撤销,并按优先权加时间片轮转算法对多线程进行调度。2, 改变时间片的大小,观察结果的变化,3, 假设两个线程共用同一软件资源(如某以变量,或者某以数据结构),请用记录型信号量来实现对它的互斥访问。4, 假设有两个线程共享一个可以存放5个整数的缓冲,一线程不停地计算1至50的平方,并将结构放入缓冲中,另一个线程不断地从缓冲中取出结果,并将它们打印出来,请用记录型信号量实现这一生产者和消费者的同步问题。5, 实现消息缓冲通信,并与3,4中的简单通信进行比较。三, 程序设计思想以及总流程图1, 程序的设计思想:该程序主要是分5大块内容:线程的创建和撤销,线程的调度,线程的同步与互斥,线程的阻塞与唤醒,利用消息缓冲队列的线程间的通信。由这五大块功能来完成的基于DOS的多任务系统的实现。在这个系统中,首先先由main函数进行一些初始化工作,然后直接创建0#线程对应于main函数,再由0#线程调用create创建1#,2#线程分别对应与函数f1(),f2(),最后将系统的中断服务程序设置为new_int8,并把控制交给1#线程,启动多个线程的并发执行。0#线程是一个比较特殊的线程,它在创建的时候没有使用create来创建,而是在系统初始化后直接创建的,因它对应的程序段为main函数中的一段,所以也直接使用整个系统的堆栈,而不再创建时为私有堆栈分配额外的空间;同样,撤销的时也不需要释放私有堆栈的空间,所以也没有over()函数而是直接撤销,从这方面来看,它是一个系统线程。此外,在启动多个线程并发执行过程后,0#线程将系统控制权转交出去,直至系统中其他进程都不具备执行条件时,它才有可能重新得到CPU,从这方面看,0#线程相当于是一个空转线程,最后,0#线程还担负着一个特别的使命:等待系统中所有其他的线程的完成,此时,它将直接撤销自己并恢复原来的时钟中断服务程序,从此终止整个多任务系统。2, 系统的总流程图 四, 系统各个功能的实现思想1, 线程的创建和撤销线程的创建过程关键就是对私有堆栈和TCB初始化的过程,其过程如下:i, 为新线程分配一空闲的线程控制块ii, 为新线程的私有堆栈分配内存空间(因为对等线程共享程序段和数据段空间,所以创建线程时不必像创建进程那样再为程序段和数据段分配内存空间)iii, 初始化新线程的私有堆栈,即按CPU调度时现场信息的保存格式布置堆栈。iv, 初始化线程控制块,即填入线程的外部标识符,设置好线程私有堆栈的始址,段址和栈顶指针,将线程的状态置为就绪状态。v, 最后哦返回新线程的内部标识符vi, 线程的内存映像如下:线程的撤销过程中,一个关键的地方是在初始化线程私有堆栈时 需要将over()的入口地址压入线程的私有堆栈中,这样做的好处是:当线程所对应的函数正常结束时,over()函数的入口地址将最为函数的返回地址被弹出至CS,IP寄存器,那么控制将自动转向over(),从而使对应的线程被自动撤销,并重新进行CPU调度。2, 线程的调度引起CPU调度原因主要是有三种情况:时间片到时,线程执行完毕或正在执行的线程因等待某种事件而不能继续执行。由这些原因,调度程序可以通过两个函数分别处理不同原因引起的调度:New_int8()函数主要是处理因时间片到时引起的调度该调度可以通过截取时钟中断(int 08)来完成;Swtch()函数主要是处理因其他原因引起的调度;New_int8()函数因为是通过截取时钟中断来实现,可以知道其是属于系统调度,由于涉及到系统调度的函数 都是需要对DOS状态进行判断,以防止出现系统数据混乱等情况的发生(从Dos的不可重入性来得出),而Swtch()函数是处理因其他原因引起的调度,所以它所涉及到的仅仅是用户级的函数调度,没有涉及到系统级的函数调度,因此Swtch()函数不需要对Dos状态进行判断。对于线程的两种调度函数的过程,因其相似,给出New_int8()函数的执行过程图,如下:需要主要的是:新的时钟中断处理程序不能太长,否则系统效率将大大下降甚至使系统无法正常工作;在新的时钟中断处理程序必须调用系统原来的INT 08H,否则将影响磁盘马达的关闭和系统的计时,另外,我们还主要依赖原来的INT 08H向中断控制器发中断结束指令(EOI);3, 线程的阻塞与唤醒线程的阻塞:主要是当某一线程需要阻塞的时候,将其插入阻塞队列中,等待唤醒进程唤醒,所以其过程为:首先,将线程的状态置为阻塞态,然后将线程插入指定的阻塞队列末尾,并重新进行CPU调度。线程的唤醒:主要是唤醒阻塞队列里面的线程,所以其过程是:把阻塞队列头上的第一个线程的TCB取下来,并将其状态改为就绪状态,等待CPU调度4, 线程的同步与互斥在这个系统中是采用记录型信号量机制来实现同步与互斥的,实现的方法:采用P ,V操作,设置两个信号量:一个为互斥信号量,一个为临界资源数目;5, 利用消息缓冲队列的线程间通信线程间的通信,关键采用send()与receive()来实现,通过发送一个文本信息来显示通信的过程,其过程为:send()函数:消息的发送者需要提供接收者的标识符,消息的长度以及消息正文的起始地址等信息,然后在发送原语里申请一空闲的消息缓冲区,用相应的信息来装配该消息缓冲区,并把它插入到接收者的消息队列中去。Receive()函数:消息的接受者必须给出发送者的标识符,接受区的起始地址等信息,然后从自己的消息队列中取得相应的发送者发送来的消息缓冲区,将消息正文复制到接受区中,并释放相应的消息缓冲区。五, 系统的详细过程#include #include #include /*定义*/#define GET_INDOS 0x34#define GET_CRIT_ERR 0x5d06/*定义四个状态*/#define finished 0#define running 1#define ready 2#define blocked 3#define TL 3/*设置TL(时间片)时间为3*/#define NTCB 10/*NTCB是系统允许的最多任务数也就是进程数*/#define NBUF 5#define NTEXT 30/*声明变量*/char far *indos_ptr=0;char far *crit_err_ptr=0;int current;/*全部变量,始终等于正在执行的线程的内部标识符*/int timecount=0;/*全局变量,等于上次调度至今的时间,在每次时钟中断发生时,timecount+1,通过它与TL课判断时间片是否到时,从而决定是否进行CPU调度*/*定义数据结构*/typedef int (far *codeptr)(void);/*定义codeptr函数指针*/*定义记录型信号量的数据结构*/typedef structint value;struct TCB *wq;semaphore;semaphore mutexfb=1,NULL;/*互斥信号量*/semaphore sfb=NBUF,NULL;/*空闲缓冲队列的计数信号量*/*消息缓冲区的数据结构*/struct bufferint sender;/*消息发送者的标识数*/int size;/*消息长度=NTEXT个字节*/char textNTEXT;/*消息正文*/struct buffer *next;/指向下一个消息缓冲区的指针*/;struct buffer *freebuf;/*空闲消息缓冲队列,是临界资源,由NBUF个空闲的消息缓冲区组成*/*定义TCB数据结构*/struct TCBunsigned char *stack;/*堆栈的起始地址*/unsigned ss;/*堆栈的段址*/unsigned sp;/*堆栈的栈指针*/char state;/*线程的状态*/char name10;/*线程的外部标示符*/struct TCB* next;/*链接字段,把所有就绪的线程按某种方式排成一显式队列,如优先权从高到底的队列*/struct buffer *mq;/*消息队列队首指针*/semaphore mutex;/*消息队列的互斥信号量*/semaphore sm;/*消息队列计数信号量*/int value; tcbNTCB;/*NTCB是系统允许的最多任务数*/*现场保护和恢复中要用到的一个数据结构*/struct int_regsunsigned bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flags,off,seg;/*声明函数*/int DosBusy(void);void InitInDos(void);void InitTcb(void);/*对TCB的初始化*/int create(char *name,codeptr code,int stacklen);void over(void);/*撤销线程,归还所占资源*/void interrupt(*old_int8)(void); /*原来的时间中断程序,需要先声明*/void interrupt new_int8(void);/*因时间片到时而引起的调度由new_int8()函数来完成*/void interrupt swtch(void);/*其他原因引起的CPU调度由函数swtch()完成*/void tcb_state(void);/*输出所有线程的状态信息*/int all_finished(void);void p(semaphore *sem);/*信号量P操作*/void v(semaphore *sem);/*信号量V操作*/*函数的实现*/*InitInDos函数的实现*/void InitInDos(void)union REGS regs;struct SREGS segregs;/*获得INDOS flag 的地址*/regs.h.ah=GET_INDOS;intdosx(®s,®s,&segregs),indos_ptr=MK_FP(segregs.es,regs.x.bx);/*get the address of CRIT_ERR flag*/if(_osmajor3)crit_err_ptr=indos_ptr+1;else if(_osmajor=3 & _osminor=0)crit_err_ptr=indos_ptr-1;elseregs.x.ax=GET_CRIT_ERR,intdosx(®s,®s,&segregs);crit_err_ptr=MK_FP(segregs.ds,regs.x.si);/*DosBusy函数的实现*/int DosBusy(void)if(indos_ptr&crit_err_ptr)return (*indos_ptr| *crit_err_ptr);elsereturn (-1);/*InitTcb函数的实现*/*对TCB进行初始化*/void InitTcb(void)int i;for(i=1;iNTCB;i+)tcbi.stack=NULL;tcbi.state=finished;strcpy(,0);tcbi.mq=NULL;tcbi.sm.value=0;/*消息队列计数信号量*/tcbi.mutex.value=1;/*缓冲区的互斥信号量*/*create函数的实现*/*创建一对应于函数name(外部标识符)的线程*/int create(char *name,codeptr code, int stacklen)int i;char *p;struct int_regs *pt;/*第一步:寻找空白的TCB*/for(i=1;iflags=0x200;/*flags寄存器的允许中断位*/pt-cs=FP_SEG(code);/*代码段的段地址*/pt-ip=FP_OFF(code);/*代码段的段内偏移地址*/pt-ds=_DS;/*数据段的段地址*/pt-es=_ES;/*附加数据段的段地址*/pt-off=FP_OFF(over);/*撤销线程代码的偏移地址*/pt-seg=FP_SEG(over);/*撤销线程代码的段址*/*第四步:初始化线程的控制块TCB*/strcpy(,name);/*填入线程的外部标识符*/tcbi.state=ready;/*将线程的状态置成就绪态*/tcbi.stack=p-stacklen;/*私有堆栈的起始地址*/tcbi.ss=FP_SEG(pt);/*当前线程的段地址*/tcbi.sp=FP_OFF(pt);/*当前线程的栈顶指针*/return i;/*返回线程的内部标示符*/*new_int8函数的实现*/*系统调度,即时间中断到达后,判断时间片到后才运行,调用老的时钟中断*/void interrupt new_int8(void)int i;(*old_int8)();/*调用原来的时钟中断服务程序*/timecount+;/*每次发生中断时加1*/if(timecount=TL)/*时间片到时*/if(DosBusy()/*如果Dos忙*/return;disable();/*关中*/*保护正在执行的线程current的现场,暂停它的执行*/tcbcurrent.ss=_SS;tcbcurrent.sp=_SP;if(tcbcurrent.state=running)/*将执行状态变为就绪状态,暂停执行*/tcbcurrent.state=ready;/*找到以新的就绪线程*/for(i=1;i=NTCB)if(tcbcurrent.state=ready)tcbcurrent.state=running;enable();return;/*如果超出了NTCB则恢复现场然后返回*/*如果找到就绪线程,那么恢复线程i的现场,把CPU分配给它*/_SS=tcbi.ss;_SP=tcbi.sp;tcbi.state=running;/*置线程i为现有线程,并且重新开始计时*/current=i;timecount=0;enable();/*开中*/return;/*swtch函数的实现*/*针对Swtch()函数的实现:由于它是解决由其他因素所引起的CPU调度,在这个实现过程,只需要判断线程的执行状态即可,其他阻塞等状态不需要进行判断,或者可以直接对当前线程的现场进行保护,然后寻找就绪线程,分配CPU以及现场进行执行*/*Find()函数是为了寻找就绪线程而且是优先权大的线程(根据优先数越大,优先权越小的思想,在TCB设置以优先数,然后进行选择)*/int Find() int i,j; for(i=0;iNTCB;i+) if(tcbi.state=ready&i!=current) break; if(i=NTCB) return -1; for(j=i+1;jtcbi.value) i=j; return i;/*swtch()调度,手工调度才能运行,处理因其他因素引起的中断*/void interrupt swtch(void)int i;i=Find();if(inext=NULL;if(tp=NULL)tp=&tcbcurrent;/*由于tp是一个指针,所以操作的是指针*/elsewhile(tp-next!=NULL)tp=tp-next;tp-next=&tcbcurrent; /*将阻塞线程插入到队尾*/enable();swtch();/*并重新进行CPU调度*/*(唤醒)wakeup_first函数的实现*/void wakeup_first(struct TCB *qp)int i;struct TCB *tp;disable();tp=*qp;/*寻找阻塞线程,因为线程状态的改变需要在TCB中修改,所以需要知道阻塞队列里面需要唤醒的线程对应TCB数组里面的哪一个*/for(i=1;inext).name)=0)/*如果两个外部标示符一样 说明找到需要唤醒的线程*/break;tcbi.state=ready;/*将其状态改为就绪状态*/enable();/*线程的同步和互斥的实现*/*用记录型信号量机制实现同步与互斥*/*对信号量的P操作*/void p(semaphore *sem)struct TCB *qp;/*设置一个指向TCB链表的二级指针*/disable();/*关中断*/sem-value=sem-value-1;/*记录型信号量的value值减1*/if(sem-valuewq);/*那么将qp指针指向sem信号量的阻塞队列*/block(qp);/*阻塞相应进程到阻塞队列*/enable();/*对信号量的V操作*/void v(semaphore *sem)struct TCB *qp;disable();qp=&(sem-wq);sem-value=sem-value+1;if(sem-value=0)wakeup_first(qp);enable();/*消息缓冲队列的线程间的通信*/*初始化消息队列*/void InitBuf(void)int i;struct buffer *freebuf,*temp;freebuf=(struct buffer*)malloc(sizeof(struct buffer);/*申请空间*/temp=freebuf;for(i=1;inext;/*从空闲消息缓冲队列头上取下一缓冲区,返回指向该缓冲区的指针*/struct buffer *getbuf(void)struct buffer *buff;buff=freebuf;/*空闲消息缓冲头*/freebuf=freebuf-next;return buff;/*将buff所指的缓冲区插到*mq所指的缓冲队列尾*/void insert (struct buffer *mq,struct buffer *buff)struct buffer *temp;if(buff=NULL)return;buff-next=NULL;if(*mq=NULL)*mq=buff;elsetemp=*mq;while(temp-next!=NULL)temp=temp-next;temp-next=buff;/*将地址a开始的size个字节发送给外部标示符为receiver的线程*/void send(char *receiver,char *a,int size) struct buffer *buff; int i,id=-1; disable();/*原语要关中断*/*首先需要进行搜索接受进程*/ for(i=0;isender=current;/*将发送方的内容加入缓冲区*/ buff-size=size; buff-next=NULL; for(i=0;isize;i+,a+)/*取得消息正文*/buff-texti=*a; p(&tcbid.mutex);/*互斥使用接收者线程的消息队列*/insert(&(tcbid.mq),buff);/*将消息缓冲区插入消息队列*/ v(&tcbid.mutex);/*撤销线程id消息队列互斥信号,接收者线程多了个消息*/v(&tcbid.sm);/*消息队列计数信号量加1*/ enable();/*释放缓冲区函数*/struct buffer *payback(struct buffer *bq)struct buffer *temp;temp=freebuf;while(temp-next!=NULL)temp=temp-next;temp-next=bq;return freebuf;/*接收者函数receiver*/void receive(char *sender)int i,j,id=-1;struct buffer *buff;disable();for(i=1;iNTCB;i+)/*搜索发送进程的ID*/if(strcmp(sender,)=0)id=i;break;if(id=-1)/*如果发送线程已经终止*/printf(Error:Sender not exist!n);tcbcurrent.state=blocked;tcbcurrent.ss=_SS;tcbcurrent.sp=_SP;for(j=1;jnext;/*下移一位*/v(&tcbcurrent.mutex);/*释放当前线程消息队列的互斥信号*/*将消息缓冲区中信息复制到接受区*/printf(n%s receiver a message:,);for(i=0;isize;i+)printf(%c,buff-texti);/*将消息正文复制出去*/printf(n);p(&mutexfb);/*缓冲区信号量*/*释放缓冲区*/buff-sender=-1;buff-size=0;strcpy(buff-text,0);buff-next=NULL;/*将当前进程的消息缓冲区内的第一个消息删除,并且将缓冲量收回缓冲区*/payback(buff);/*释放缓冲区信号量*/v(&mutexfb);/*空闲缓冲区数+1*/v(&sfb);printf(f1 and f2 is finished!);enable();/*over函数的实现*/*撤销线程,收回被撤销线程的资源*/void over(void)disable();/*通过disable()与enable()来实现在执行该代码段时防止中断*/*撤销当前线程所申请的TCB内存空空间,因为一个线程在它执行完毕之后就需要撤销自己,所以是要用到current(当前)线程*/free(tcbcurrent.stack);/*堆栈指针的释放*/strcpy(,0);/*将线程的外部标示符置空*/tcbcurrent.state=finished;/*将状态置为终止态*/swtch();/*在线程撤销后,需要重新进行CPU调度*/enable();/*tcb_state函数的实现*/*输出所有线程的状态*/void tcb_state(void)int i;for(i=1;iNTCB;i+)switch(tcbi.state)case 0:printf(The state of tcb%d(%s) is finishedn,i,);break;case 1:printf(The state of tcb%d(%s) is runningn,i,);break;case 2:printf(The state of tcb%d(%s) is readyn,i,);break;case 3:printf(The state of tcb%d(%s) is blockedn,i,);break;/*f1函数的实现*/void f1 (void)int i,j,k;char cNTEXT=you received it,f2?;for(i=0;i10;i+)putchar(a);for(j=0;j100000;j+)for(k=0;k5000;k+);send(f2,c,20);printf(f2 is waken up!n);/*f2函数的实现*/void f2(void)long i,j,k;for(i=0;i10;i+)putchar(b);for(j=0;j100000;j+)for(k=0;k5000;k+);receive(f1);/*all_finished函数的实现*/*判断是否全部线程都已经完成*/

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论