基于dos的多任务系统实现

一.程序的设计思想

该程序主要是分5大块内容:线程的创建和撤销,线程的调度,线程的同步与互斥,线程的阻塞与唤醒,利用消息缓冲队列的线程间的通信。由这五大块功能来完成的基于DOS的多任务系统的实现。在这个系统中,首先先由main函数进行一些初始化工作,然后直接创建0#线程对应于main函数,再由0#线程调用create创建1#,2#线程分别对应与函数f1(),f2(),最后将系统的中断服务程序设置为new_int8,并把控制交给1#线程,启动多个线程的并发执行。
0#线程是一个比较特殊的线程,它在创建的时候没有使用create来创建,而是在系统初始化后直接创建的,因它对应的程序段为main函数中的一段,所以也直接使用整个系统的堆栈,而不再创建时为私有堆栈分配额外的空间;同样,撤销的时也不需要释放私有堆栈的空间,所以也没有over()函数而是直接撤销,从这方面来看,它是一个系统线程。
此外,在启动多个线程并发执行过程后,0#线程将系统控制权转交出去,直至系统中其他进程都不具备执行条件时,它才有可能重新得到CPU,从这方面看,0#线程相当于是一个空转线程,最后,0#线程还担负着一个特别的使命:等待系统中所有其他的线程的完成,此时,它将直接撤销自己并恢复原来的时钟中断服务程序,从此终止整个多任务系统。


二.线程的创建

一.主要函数设计
1. create函数
(1)函数申明原型: typedef int (far codeptr)(void); /定义了一个函数指针类型*/
Int create(char *name,codeptr code,int stck) ;
(2)函数功能描述:在main()函数中调用,创建一个新线程,让其执行code开始的代码。
(3)输入:
name:新创建线程的外部标识符;
code:新创建线程要执行的代码的入口地址,此处用函数名作为传入地址;
stck: 新创建线程的私有堆栈的长度。
(4)输出:新创建线程的内部标识符,若创建失败,返回-1。
(5)线程创建的主要设计思路
i, 为新线程分配一空闲的线程控制块
ii, 为新线程的私有堆栈分配内存空间
iii, 初始化新线程的私有堆栈,即按CPU调度时现场信息的保存格式布置堆栈。为了方便堆栈的初始化工作,我们可以按照堆栈中的内容设计一个以下的数据结构:
struct int_regs {
unsigned bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flags,off,seg;
};然后用一个指向该数据结构的指针给堆栈赋值。
iv, 初始化线程控制块,即填入线程的外部标识符,设置好线程私有堆栈的始址,段址和栈顶指针,将线程的状态置为就绪状态。如图a所示。
v, 最后哦返回新线程的内部标识符
vi, 线程的内存映像如下:


三.线程的撤销

一.主要函数设计
1. destory函数

(1)函数申明原型:void destroy(int id);
(2)功能:撤销内部标识符为id的指定线程。
(3)输入:id:将要被撤销的线程的内部标识符。
(4)输出:无
撤销线程所要完成的工作比较简单,主要是将线程所占据的资源归还给系统。主要工作是:
①将线程的私有堆栈所占的内存空间归还给系统;
②将线程控制块TCB的各成员变量进行初始化操作。


四.线程的阻塞

一.主要函数设计
1. block函数

(1)函数申明原型:void block(struct TCB **qp);
(2)功能:实现线程的阻塞。
(3)输入:qp:指向TCB链表的二级指针。
(4)输出:无

我们将所有处于阻塞状态的线程的TCB按阻塞的不同原因排成多个队列。为此,需在TCB中增加一链接字段next,以方便TCB的排队。
一个正在执行的线程因某种原因而阻塞时,必须给出因该原因而阻塞的阻塞队列的队首信息,阻塞原语应完成以下主要工作:将线程的状态改成阻塞态;将线程插入指定的阻塞队列末尾;重新进行CPU调度。


五.线程的唤醒

一.主要函数设计
1.wakeup函数

唤醒原语应完成以下主要工作:把阻塞队列头上的第一个线程的TCB取下来,并将其状态改为就绪态,如果系统设置了就绪队列,还应插入就像队列。
(1)函数申明原型:void wakeup_first(struct TCB **qp);
(2)功能:实现线程的阻塞。
(3)输入:qp:指向TCB链表的二级指针。
(4)输出:无


六.线程的调度

一.主要设计思想
引起CPU调度原因主要是有三种情况:时间片到时,线程执行完毕或正在执行的线程因等待某种事件而不能继续执行。由这些原因,调度程序可以通过两个函数分别处理不同原因引起的调度:New_int8()函数主要是处理因时间片到时引起的调度该调度可以通过截取时钟中断(int 08)来完成;Swtch()函数主要是处理因其他原因引起的调度;New_int8()函数因为是通过截取时钟中断来实现,可以知道其是属于系统调度,由于涉及到系统调度的函数 都是需要对DOS状态进行判断,以防止出现系统数据混乱等情况的发生(从Dos的不可重入性来得出),而Swtch()函数是处理因其他原因引起的调度,所以它所涉及到的仅仅是用户级的函数调度,没有涉及到系统级的函数调度,因此Swtch()函数不需要对Dos状态进行判断。
二.主要函数设计
1. new_int8函数
(1)函数申明原型: void interrupt new_int8(void)
(2)功能:系统调度,即时间中断到达后,判断时间片到后才运行,调用老的时钟中断
(3)输入:无
(4)输出:无
(5)程序设计流程图

  1. my_swtch函数
    (1)函数申明原型: void interrupt my_swtch(void)
    (2)功能:由于它是解决由其他因素所引起的CPU调度,在这个实现过程,只需要判断线程的执行状态即可,其他阻塞等状态不需要进行判断,或者可以直接对当前线程的现场进行保护,然后寻找就绪线程,分配CPU以及现场进行执行
    (3)输入:无
    (4)输出:无
    (5)程序设计流程图

  2. find函数
    (1)函数申明原型:int Find(void)
    (2)功能:为了寻找就绪线程而且是优先权大的线程(根据优先数越大,优先权越小的思想,在TCB设置以优先数,然后进行选择)
    (3)输入:无
    (4)输出:就绪线程而且是优先权大的线程
    三.调度算法设计
    基于优先权的时间片轮转调度算法,即当前线程时间片到时,应暂停当前线程运行,重新选择一个优先权最高的就绪线程参加运行。在Tcb中增加一有限数字段(优先数越大优先权较低),可以按照等待时间的长度动态改变优先数(当一个线程在就绪队列中等待一个时间片),则将它的优先数减去某个值a,以提高它的优先权而避免长期等待;一个线程执行完一个时间片,则将它的优先数加上某个值b,以降低它的优先权。


七.线程的同步与互斥

一.主要变量
1.选择记录型信号量机制来作为线程的同步机制
记录型信号量的数据结构定义如下:
typedef struct{
int value;
struct TCB *wq;
} semaphore;
2.定义三个信号量
(1)mutex(初值为1)——–实现线程对缓冲池的互斥使用
(2)empty(初值为缓冲区的个数n)———–表示缓冲池中空缓冲区的数量
(3)full(初值为0)————-满缓冲区的数量

二.主要函数设计
1. wait函数
(1)函数申明原型: void wait(semaphore *sem)
(2)功能:申请一个空的缓冲区或者说是申请缓冲池的使用权
(3)输入:无
(4)输出:无
2. signal函数
(1)函数申明原型: void signal(semaphore *sem)
(2)功能:释放缓冲池使用权或者说是释放一个满缓冲区
(3)输入:无
(4)输出:无


八.利用消息缓冲队列通信机制实现线程间通信

一.主要变量
1.在消息缓冲队列通信机制中,主要用到的数据结构是消息缓冲区,它可描述如下:
struct buffer
{
int sender; /* 消息发送者的内部标识 */
int size; /* 消息长度 <=NTEXT 个字节 */
char text[NTEXT]; /* 消息正文 */
struct buffer next; / 指向下一个消息缓冲区的指针 */
};

2.在利用消息缓冲队列进行线程间的通信时,除了需要设置消息缓冲区外,还需对线程控制块TCB进行扩充,在原来的TCB的基础上再增加以下字段:
struct buffer mq; / 接收线程的消息队列队首指针 */
semaphore mutex; /* 接收线程的消息队列的互斥信号量 */
semaphore sm; /* 接收线程的消息队列的计数信号量,用于实现同步 */
二.主要函数设计
1. 获取空闲缓冲区函数getbuf()
(1)函数申明原型:struct buffer *getbuf(void)
(2)功能:从空闲消息缓冲队列头上取下一空闲消息缓冲区
(3)输入:无
(4)输出:指向所获得的空闲消息缓冲区的指针
2.插入缓冲区到缓冲队列函数insert()
(1)函数申明原型:void insert(struct buffer **mq,struct buffer *buff)
(2)功能:将buff所指的缓冲区插到*mq所指的缓冲队列末尾。
(3)输入:
mq:将要插入的缓冲队列的队首指针的指针;
buff:消息缓冲区指针。
(4)输出:无
3.发送原语send()
在发送消息时,消息的发送者必须提供接收者的标识符、消息长度及消息正文的起始地址等信息。然后在发送原语里申请一空闲的消息缓冲区,用相应的信息来装配该消息缓冲区,并将它插入到接受者的消息队列中去。
(1)函数申明原型:void send(char *receiver,char *a,int size)
(2)功能:将地址a开始的size个字节发送给外部标识符为receiver的线程。
(3)输入:
receiver:接收线程的外部标识符的指针;
a:要发送的消息正文的指针;
size:要发送的消息正文的长度。
(4)输出:无
4.获取消息缓冲区函数remov()
(1)函数申明原型:struct buffer *remov(struct buffer **mq,int sender)
(2)功能:接收线程从它自己的消息队列中取下sender发送给它的消息缓冲区。
(3)输入:
mq:接收线程的消息队列的队首指针的指针;
sender:发送线程的内部标识符;
(4)输出:所获得的消息缓冲区的指针
(5)函数实现的算法描述:
该函数需要完成的工作比较简单,首先在mq消息队列中查找发送线程标识符为sender的消息缓冲区,若找到则从mq所指示的消息缓冲队列中移除该消息缓冲区并返回该缓冲区,否则返回空。程序实现请同学们自己完成。
5.接收原语receive()
(1)函数申明原型:int receive(char *sender,char *b)
(2)功能:从接收线程的消息缓冲队列中取一个从sender发送过来消息并复制到b中。
(3)输入:
sender:发送线程的外部标识符;
b:接收线程内部的接收区,用来存放接收到的消息正文。
(4)输出:接收到的消息的长度
(5)函数实现的算法描述:
该函数要完成的主要工作是:首先检测指定的sender是否存在,如果不存在则直接返回;接着判断当前线程消息队列是否空闲,如果空闲,则调用remov()从消息队列取得sender发给它的消息;读取消息到b中;最后调用insert()将取完消息后的消息缓冲区插入到系统空闲消息缓冲队列中。


代码借鉴了一下网上和老师所给的


#include <stdlib.h>
#include <stdio.h>
#include <dos.h>
/***********************定义***********************/
#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 struct
{
    int value;
    struct TCB *wq;

}semaphore;
semaphore mutexfb={1,NULL};     /*互斥信号量*/
semaphore sfb={NBUF,NULL};      /*空闲缓冲队列的计数信号量*/





/*消息缓冲区的数据结构*/
struct buffer
{
    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);
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(&regs,&regs,&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(&regs,&regs,&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);
    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));

    /*获得堆栈的内存空间的高地址指针*/

    p=p+stacklen;

    /*对地址进行类型转换*/

    pt=(struct int_regs*)p;
    pt--;

    /*第三步:对线程的私有堆栈进行初始化;用FP_SEG和FP_OFF分别获得栈顶指针和栈顶指针的偏移量,此外系统对线程撤销的工作,
    需要在该步骤完成:通过在堆栈初始化时 原先将over()函数的入口地址压入线程的私有堆栈中;那么当线程所对应的函数正常结束时,
    over()函数的入口地址将作为函数的返回地址被弹出到CS,IP寄存器,控制自动转向over()函数执行*/

    pt->flags=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(tcb[i].name,name);   /*填入线程的外部标识符*/
    tcb[i].state=ready;         /*将线程的状态置成就绪态*/
    tcb[i].stack=p-stacklen;        /*私有堆栈的起始地址*/
    tcb[i].ss=FP_SEG(pt);           /*当前线程的段地址*/
    tcb[i].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的现场,暂停它的执行*/
        tcb[current].ss=_SS;
        tcb[current].sp=_SP;
        if(tcb[current].state==running) /*将执行状态变为就绪状态,暂停执行*/
            tcb[current].state=ready;
        /*找到以新的就绪线程*/
        for(i=1;i<NTCB;i++){
            if(tcb[i].state==ready && i!=current)   /*找到除了当前线程的其他就绪线程*/
                break;      
        }
        /*如果没有找到就绪线程,那么就回复当前线程,继续执行*/
        if(i>=NTCB){
            if(tcb[current].state==ready)
                tcb[current].state=running;
            enable();
            return;         /*如果超出了NTCB则恢复现场然后返回*/
        }
        /*如果找到就绪线程,那么恢复线程i的现场,把CPU分配给它*/
        _SS=tcb[i].ss;
        _SP=tcb[i].sp;
        tcb[i].state=running;
        /*置线程i为现有线程,并且重新开始计时*/
        current=i;
        timecount=0;
        enable();                           /*开中*/

    }
    return;

}


/*********swtch函数的实现************/
/*针对Swtch()函数的实现:由于它是解决由其他因素所引起的CPU调度,在这个实现过程,只需要判断线程的执行状态即可,其他阻塞等状态不需要进行判断,或者可以直接对当前线程的现场进行保护,然后寻找就绪线程,分配CPU以及现场进行执行*/
/*Find()函数是为了寻找就绪线程而且是优先权大的线程(根据优先数越大,优先权越小的思想,在TCB设置以优先数,然后进行选择)*/
int Find()
{
   int i,j;
   for(i=0;i<NTCB;i++)
     if(tcb[i].state==ready&&i!=current)
       break;
     if(i==NTCB)
     return -1;
   for(j=i+1;j<NTCB;j++)
   {
      if(tcb[j].state==ready&&j!=current)
        if(tcb[j].value>tcb[i].value)
      i=j;
    }

   return i;
}
/*swtch()调度,手工调度才能运行,处理因其他因素引起的中断*/

void interrupt swtch(void)
{
    int i;
    i=Find();
    if(i<0)
        i=0;
    disable();

   tcb[current].ss=_SS;
   tcb[current].sp=_SP;
   if(tcb[current].state==running)
        tcb[current].state=ready;
   _SS=tcb[i].ss;
   _SP=tcb[i].sp;
   tcb[i].state=running;
   current=i;
   enable();

}
/****************线程的阻塞和唤醒的实现****************/

/**(阻塞)block函数的实现**/
void block(struct TCB **qp){
    struct TCB *tp;         
    disable();

    tp=*qp;
    tcb[current].state=blocked; /*首先要将当前线程的状态置为阻塞状态*/
    /*需要将线程插入到指定的阻塞队列未尾,并重新进行CPU调度*/
    (*qp)->next=NULL;                   
    if(tp==NULL)
        tp=&tcb[current];       /*由于tp是一个指针,所以操作的是指针*/
    else{
        while(tp->next!=NULL)
            tp=tp->next;
        tp->next=&tcb[current];  /*将阻塞线程插入到队尾*/
    }
    enable();
    swtch();                    /*并重新进行CPU调度*/
}

/**(唤醒)wakeup_first函数的实现**/
void wakeup_first(struct TCB **qp){
    int i;
    struct TCB *tp;
    disable();
    tp=*qp;
/*寻找阻塞线程,因为线程状态的改变需要在TCB中修改,所以需要知道阻塞队列里面需要唤醒的线程对应TCB数组里面的哪一个*/
    for(i=1;i<NTCB;i++){
        if(strcmp(tcb[i].name,(*tp->next).name)==0){        /*如果两个外部标示符一样 说明找到需要唤醒的线程*/
            break;
        }
        tcb[i].state=ready;                 /*将其状态改为就绪状态*/
        enable();
    }
}




/***************线程的同步和互斥的实现****************/
/*用记录型信号量机制实现同步与互斥*/
/**对信号量的P操作**/
void p(semaphore *sem){
    struct TCB **qp;            /*设置一个指向TCB链表的二级指针*/
    disable();                  /*关中断*/
    sem->value=sem->value-1;    /*记录型信号量的value值减1*/
    if(sem->value<0){       /*如果记录型信号量的值小于0*/
        qp=&(sem->wq);  /*那么将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;i<=NBUF;i++){
        temp=(struct buffer*)malloc(sizeof(struct buffer));
        temp=temp->next;

    }

}
/**从空闲消息缓冲队列头上取下一缓冲区,返回指向该缓冲区的指针**/
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;
    else{
        temp=*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;i<NTCB;i++){
     if(strcmp(receiver,tcb[i].name)==0){
         id=i;
         break;
     }
   }
   /*如果没有收到,那么就显示错误,没有接收进程*/
   if(id==-1){
     printf("Error:Receiver not exist!\n");
     enable();
     return ;
   }

    printf("\n%s send %s a message:",tcb[current].name,receiver);
    printf("%s\n",a);

    p(&sfb);            /*sfb为空闲缓冲区队列的计数信号量,为全局变量*/
    p(&mutexfb);        /*mutexfb为互斥信号量*/
        buff=getbuf();  /*取一缓冲区*/
    v(&mutexfb);        /*释放互斥信号量,表示用完缓冲区*/

    buff->sender=current;   /*将发送方的内容加入缓冲区*/
    buff->size=size;
    buff->next=NULL;

    for(i=0;i<buff->size;i++,a++)   /*取得消息正文*/
        buff->text[i]=*a;

    p(&tcb[id].mutex);              /*互斥使用接收者线程的消息队列*/
        insert(&(tcb[id].mq),buff); /*将消息缓冲区插入消息队列*/
    v(&tcb[id].mutex);              /*撤销线程id消息队列互斥信号,接收者线程多了个消息*/
    v(&tcb[id].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;i<NTCB;i++){                        /*搜索发送进程的ID*/
        if(strcmp(sender,tcb[i].name)==0){
            id=i;
            break;      
        }   
    }
    if(id==-1){                                 /*如果发送线程已经终止*/
        printf("Error:Sender not exist!\n");
        tcb[current].state=blocked;
        tcb[current].ss=_SS;
        tcb[current].sp=_SP;

        for(j=1;j<NTCB;j++)                     /*寻找新的线程*/
        {                               
            if(tcb[i].state==ready){            /*恢复新线程的现场*/
                _SS=tcb[i].ss;
                _SP=tcb[i].sp;
                tcb[i].state=running;           
            }
        }
        enable();
        return;
    }

    p(&tcb[current].sm);        /*当前进程消息队-1,取第一个缓冲区*/
    p(&tcb[current].mutex);     /*当前线程消息队列的互斥信号量*/
        buff=tcb[current].mq;   /*消息队列的首指针*/
        tcb[current].mq=tcb[current].mq->next;      /*下移一位*/
    v(&tcb[current].mutex);         /*释放当前线程消息队列的互斥信号*/
    /*将消息缓冲区中信息复制到接受区*/
    printf("\n%s receiver a message:",tcb[current].name);
    for(i=0;i<buff->size;i++)                   
        printf("%c",buff->text[i]);         /*将消息正文复制出去*/
    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(tcb[current].stack);       /*堆栈指针的释放*/
    strcpy(tcb[current].name,'\0'); /*将线程的外部标示符置空*/
    tcb[current].state=finished;    /*将状态置为终止态*/



    swtch();                /*在线程撤销后,需要重新进行CPU调度*/
    enable();
}
/************tcb_state函数的实现**************/
/*输出所有线程的状态*/
void tcb_state(void){
    int i;
    for(i=1;i<NTCB;i++){
        switch(tcb[i].state){
        case 0:
            printf("The state of tcb[%d](%s) is finished\n",i,tcb[i].name);
            break;
        case 1:
            printf("The state of tcb[%d](%s) is running\n",i,tcb[i].name);
            break;
        case 2:
            printf("The state of tcb[%d](%s) is ready\n",i,tcb[i].name);
            break;
        case 3:
            printf("The state of tcb[%d](%s) is blocked\n",i,tcb[i].name);
            break;

        }
    }
}
/**************f1函数的实现****************/
void f1 (void){
    int i,j,k;
    char c[NTEXT]="you received it,f2?";
    for(i=0;i<10;i++){
        putchar('a');
        for(j=0;j<100000;j++){
            for(k=0;k<5000;k++);
        }
    }
    send("f2",c,20);
    printf("f2 is waken up!\n");
}
/***************f2函数的实现**************/
void f2(void){
    long i,j,k;
    for(i=0;i<10;i++){
        putchar('b');
        for(j=0;j<100000;j++)
            for(k=0;k<5000;k++);
    }
    receive("f1");
}
/*************all_finished函数的实现*****************/
/*判断是否全部线程都已经完成*/
int all_finished(void){
    int i;
    for(i=1;i<NTCB;i++){
        if(tcb[i].state!=finished)
            return 0;
    }
    return 1;
}
/****************************主函数*******************************/
void main()
{
    InitInDos();
    InitTcb();
    old_int8=getvect(8);            /*在使用新的时钟中断服务程序前,必须先用getvect()函数获取系统原来的INT 08H的入口地址并将它保存起来*/

    /*创建0#线程*/
    strcpy(tcb[0].name,"main");
    tcb[0].state=running;
    current=0;

    /*创建f1,f2线程后,在CPU中,CS,IP指向current=0的函数,而ss,sp指向main函数*/
    create("f1",(codeptr)f1,1024);
    create("f2",(codeptr)f2,1024);
    tcb_state();

    /*启动多个线程的并发执行*/
    setvect(8,new_int8);
    swtch();

    while(!all_finished()){}
    tcb[0].name[0]='\0';
    tcb[0].state=finished;
    setvect(8,old_int8);            /*回复8号中断*/
    tcb_state();
    printf("\n Multi_task system terminated. \n");
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: DOS系统下的Red Board是什么?Red Board是一种基于DOS操作系统的开发板。它具有易于使用和简单的设计,旨在帮助开发者快速构建和测试基于DOS的应用程序。Red Board的硬件配置包括处理器、内存和存储器等基本组件,以及用于扩展功能的接口和插槽。 在DOS系统下,Red Board提供了一种方便且高效的方式来开发DOS应用程序。它具有友好的用户界面和命令行界面,使开发者可以轻松地编写、调试和执行程序。红板还配备了一些常用的工具和库,以加速开发过程。 Red Board可用于各种应用场景,包括嵌入式系统开发、学术研究和实验等。它可以与外部设备和传感器进行连接,从而实现更复杂的功能。在DOS系统下,Red Board可以实现数据采集、控制设备、通讯等任务。 红板的优点在于它的稳定性和兼容性。作为基于DOS系统的开发板,它具有较低的硬件需求,可以在各种旧版和新版的计算机上运行。此外,使用Red Board开发的应用程序与其他DOS系统兼容,因此可以在更多的计算机上运行。 总之,DOS系统下的Red Board是一款用于开发和测试基于DOS的应用程序的开发板。它提供了一种简单而高效的方式来编写和执行程序,并具有广泛的应用领域和兼容性。 ### 回答2: DOS系统是一种基于磁盘操作系统的计算机操作系统,它是早期个人计算机系统中最为常见的操作系统之一。而红板是指红色基板电路板,它通常用于Arduino开发板。下面是关于DOS系统下红板的一些信息。 在DOS系统下使用红板,通常需要连接红板到计算机的串行端口上,使用串口通信进行数据传输。通过串口,DOS系统可以向红板发送控制指令和数据,以实现与外部设备的交互。 使用红板开发硬件项目时,DOS系统可以作为编程环境。用户可以在DOS系统中使用汇编语言或C语言等编程语言编写代码,通过红板控制外部设备的输入输出。例如,可以编写程序控制红板连接的传感器采集数据,并将数据传输到计算机上进行处理和分析。 此外,DOS系统下常用的集成开发环境(IDE)如Turbo Pascal和Borland C++等也可以用于开发红板项目。这些IDE提供了丰富的功能和工具,使得在DOS系统下开发红板项目更加便捷。 总的来说,使用DOS系统下的红板可以实现与外部设备的交互,进行硬件开发和编程。它为用户提供了一种简单而灵活的开发环境,可以进行各种实验和项目的探索,对于学习和理解计算机硬件和软件之间的关系也具有一定的帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值