vi, 线程的内存映像如下:
线程的撤销过程中,一个关键的地方是在初始化线程私有堆栈时 需要将over()的入口地址压入线程的私有堆栈中,这样做的好处是:当线程所对应的函数正常结束时,over()函数的入口地址将最为函数的返回地址被弹出至CS,IP寄存器,那么控制将自动转向over(),从而使对应的线程被自动撤销,并重新进行CPU调度。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 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(®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);
else
return (-1);
}
/************InitTcb函数的实现*************/
/*对TCB进行初始化*/
void InitTcb(void){
int i;
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(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){
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;itcb[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;inext).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;isender=current; /*将发送方的内容加入缓冲区*/
buff->size=size;
buff->next=NULL;
for(i=0;isize;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;inext; /*下移一位*/
v(&tcb[current].mutex); /*释放当前线程消息队列的互斥信号*/
/*将消息缓冲区中信息复制到接受区*/
printf("\n%s receiver a message:",tcb[current].name);
for(i=0;isize;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