基于线程的多任务调度系统的设计与实现实验报告
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
/*互斥信号量*/ /*空闲缓冲队列的计数
{ int sender; /*消息发送者的标识数 */ int size; /*消息长度<=NTEXT 个字节*/ char text[NTEXT]; struct buffer *next; /指向下一个消息缓冲区的指针*/ }; struct buffer *freebuf; /*空闲消息缓冲队 列,是临界资源,由NBUF个空闲的消息缓冲区组成*/ /*定义TCB数据结构*/ struct TCB{ unsigned char *stack; /*堆栈的起始地址*/ unsigned ss; /*堆栈的段址*/ unsigned sp; /*堆栈的栈指针* char state; /*线程的状态*/ char name[10]; /*线程的外部标示符*/ struct TCB* next; /*链接字段,把所有就绪 的线程按某种方式排成一显式队列,如优先权从高到底的队列*/ struct buffer *mq; /*消息队列队首指针*/ semaphore mutex; /*消息队列的互斥信号量 */ semaphore sm; /*消息队列计数信号量*/ int value; } tcb[NTCB]; /*NTCB是系统允许的最多 任务数*/ /*现场保护和恢复中要用到的一个数据结构*/ struct int_regs{ unsigned 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);
finished 0 running 1 ready 2 blocked 3 /*设置TL(时间片)时间为3*/ /*NTCB是系统允许的最多任务数也
#define TL 3 #define NTCB 10 就是进程数*/ #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 struct { int value; struct TCB *wq; }semaphore; semaphore mutexfb={1,NULL}; semaphore sfb={NBUF,NULL}; 信号量*/ /*消息缓冲区的数据结构*/ struct buffer
p=p+stacklen pt=(struct int_regs*)p; pt--; pt->flags=0x200; 位*/ 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); /*撤销线程代码的段址*/ /*撤销线程代码的偏移地 /*数据段的段地址*/ /*附加数据段的段地址 /*代码段的段地址*/ /*代码段的段内偏移地址 /*flags寄存器的允许中断
2 线程的调度
引起CPU调度原因主要是有三种情况:时间片到时,线程 执行完毕或正在执行的线程因等待某种事件而不能继续执行。 由这些原因,调度程序可以通过两个函数分别处理不同原因引 起的调度: New_int8()函数主要是处理因时间片到时引起的调度该调 度可以通过截取时钟中断(int 08)来完成; Swtch()函数主要是处理因其他原因引起的调度; New_int8()函数因为是通过截取时钟中断来实现,可以知道其 是属于系统调度,由于涉及到系统调度的函数 都是需要对DOS 状态进行判断,以防止出现系统数据混乱等情况的发生(从 Dos的不可重入性来得出),而Swtch()函数是处理因其他原因 引起的调度,所以它所涉及到的仅仅是用户级的函数调度,没 有涉及到系统级的函数调度,因此Swtch()函数不需要对Dos状 态进行判断。
else return (-1); } /************InitTcb函数的实现*************/ /*对TCB进行初始化*/ void InitTcb(void){ int i; for(i=1;i<NTCB;i++){ tcb[i].stack=NULL; tcb[i].state=finished; strcpy(tcb[i].name,'\0'); tcb[i].mq=NULL; tcb[i].sm.value=0; /*消息队列计 数信号量*/ tcb[i].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;i<NTCB;i++){ if(tcb[i].state==finished) break; } /*第二步:申请线程的私有堆栈内存空间,分配stacklen 个字节长度的内存空间,利用malloc函数返回内存地址指针指向 该内存空间, 所返回的值是该内存空间的起始地址*/ p=(char *)malloc(stacklen*sizeof(char)); /*获得堆栈的内存空间的高地址指针*/
12224506 5班
功能设计 1 线程的创建和撤销
线程的创建过程关键就是对私有堆栈和TCB初始化的过 程,其过程如下: i, 为新线程分配一空闲的线程控制块 ii, 为新线程的私有堆栈分配内存空间(因为对 等线程共享程序段和数据段空间,所以创建 线程时不必像创建进程那样再为程序段和数 据段分配内存空间) iii, 初始化新线程的私有堆栈,即按CPU调度时现 场信息的保存格式布置堆栈。 初始化线程控制块,即填入线程的外部标识符,设置好线程私 有堆栈的始址,段址和栈顶指针,将线程的状态置为就绪状态
4 线程的同步与互斥
在这个系统中是采用记录型信号量机制来实现同步与互斥 的,实现的方法: 采用P ,V操作,设置两个信号量:一个为互斥信号量,一 个为临界资源数目;
5利用消息缓冲队列的线程间通信
线程间的通信,关键采用send()与receive()来实现,通 过发送一个文本信息来显示通信的过程,其过程为: send()函数:消息的发送者需要提供接收者的标识符,消 息的长度以及消息正文的起始地址等信息,然后在发送原语里 申请一空闲的消息缓冲区,用相应的信息来装配该消息缓冲 区,并把它插入到接收者的消息队列中去。 Receive()函数:消息的接受者必须给出发送者的标识 符,接受区的起始地址等信息,然后从自己的消息队列中取得 相应的发送者发送来的消息缓冲区,将消息正文复制到接受区 中,并释放相应的消息缓冲区。
基于线程的多任务调度系统的设计与实现实验报 告 姓名 陈振辉 学号 班级 1 实验要求
(1)线程的创建、撤消和CPU切换。 掌握线程的定义和特征,线程的基本状态,线程的私 有堆栈,线程控制块TCB,理解线程与进程的区别,实现线程 的创建、撤消和CPU切换。 (2)时间片轮转调度 理解各种调度算法、调度的原因,完成时钟中断的截 取,具体实现调度程序。 (3)最高优先权优先调度 理解优先权的概念,并实现最高优先权优先调度策 略。 (4)利用记录型信号量实现线程的同步 理解同步的相关概念,掌握记录型信号量的概念及 应用,并用记录型信号量实现生产者和消费者问题。 (5)消息缓冲队列通信机制 理解进程(线程)通信的基本概念,并用消息缓冲队 列实现线程间的通信。
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(_osmajor<3) crit_err_ptr=indos_ptr+1; else if(_osmajor==3 && _osminor==0) crit_err_ptr=indos_ptr-1; else{ regs.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);
源代码及解析如下
#include <stdlib.h> #include <stdio.h> #include <dos.h> #define GET_INDOS 0x34 #define GET_CRIT_ERR 0x5d06 /*定义四个状态*/
#define #define #define #define
3 线程的阻塞与唤醒
线程的阻塞:主要是当某一线程需要阻塞的时候,将其插 入阻塞队列中,等待唤醒进程唤醒,所以其过程为:首先,将
线程的状态置为阻塞态,然后将线程插入指定的阻塞队列末 尾,并重新进行CPU调度。 线程的唤醒:主要是唤醒阻塞队列里面的线程,所以其过 程是:把阻塞队列头上的第一个线程的TCB取下来,并将其状 态改为就绪状态,等待CPU调度