线程间的双向消息通信
我们知道,信号量的值初始化为0时可作为同步控制,值为1时可作为互斥控制。在消息通信中,这里假设有两个线程A,B,那双向通信就需要两个信号量来同步消息。
在实验中,我们定义两个同步信号量: semaphore sem1 = {0,NULL}; semaphore sem2 = {0,NULL};
其中,sem1用于A向B发送消息和B向A接收消息,sem2用于B向A发送消息和A向B接收消息。
现在,我们来修改一下模拟两个线程执行内容的函数:
线程1:
void f1() /* 1#线程 */
{
long i, j, k;
char a[NTEXT];
int len = 0;
i = 0;
while(++i)
{
if(i == 5) break;
printf("f1 sending!\n");
send("f2", "f1 send message to f2", 21);
v(&sem1); // 发送消息后,sem1的value+1使得线程2被主动唤醒执行
p(&sem2); // 要接收消息,先检查sem2的value-1是否>=0,小于0的话将阻塞该线程直到有消息发送过来被主动唤醒
len = receive("f2", a);
printf("f1 received %d: %s\n\n", len, a);
}
}
线程2同理:
void f2() /* 2#线程 */
{
int i, j, k;
int len = 0;
char a[NTEXT];
i = 0;
while(++i)
{
if(i == 5) break;
p(&sem1); // 要接收消息,先检查sem1的value-1是否>=0,小于0的话将阻塞该线程直到有消息发送过来被主动唤醒
len = receive("f1", a);
printf("f2 received %d: %s\n\n", len, a);
printf("f2 sending!\n");
send("f1", "f2 send message to f1", 21);
v(&sem2); // 发送消息后,sem2的value+1使得线程1被主动唤醒执行
}
}
和之前两次实验一样,这里我们也要更改线程的调度函数,我这里是基于优先级调度而非时间片切换调度,注意要初始化默认的优先级值。
代码如下:
int Find()
{
int i = 0, index = 0, maxpri = 0;
for(i = 1; i < NTCB; i++)
{
if(tcb[i].state != FINISHED && tcb[i].state != BLOCKED
&& maxpri < tcb[i].value)
{
maxpri = tcb[i].value;
index = i;
}
}
return index;
}
在代码中,还有一些小细节需要修改,比如receive()函数:
int receive(char *sender, char *a)
{
struct buffer *buff;
int i;
int size = 0;
disable();
for(i = 0; i < NTCB; i++)
if(strcmp(sender, tcb[i].name) == 0)
break;
if(i == NTCB)
{
printf("error:sender not exist!\n");
enable();
return size;
}
if(tcb[current].mq == NULL)
{
p(&tcb[current].sm);
return size;
}
p(&tcb[current].mutex); /* 当前线程的消息缓冲队列资源的互斥操作*/
buff = tcb[current].mq;
tcb[current].mq = tcb[current].mq->next;
for(i = 0; i < buff->size; i++, a++) /* 输出所接受的内容 */
(*a) = buff->text[i];
*a = '\0';
size = buff->size;
v(&tcb[current].mutex); /* 当前线程的消息缓冲队列资源的互斥操作 */
p(&mutexfb);
buff->sender = -1;
buff->size = 0;
strcpy(buff->text, "");
buff->next = NULL;
putbuf(buff);
v(&mutexfb);
v(&sfb);
p(&tcb[current].sm);
return size;
// v(&test); /* 对应于p(&Tcb[current].sm) */
}
到此,我们看一下运行结果:
大家最关心的是最后代码了,好的,这就贴上:
#include <stdlib.h>
#include <dos.h>
#include <stdio.h>
#define GET_INDOS 0x34 /* 34H 系统功能调用 */
#define GET_CRIT_ERR 0x5d06 /* 5D06H号系统功能调用 */
#define BLANK -1
#define FINISHED 0 /* 终止 */
#define RUNNING 1 /* 执行 */
#define READY 2 /* 就绪 */
#define BLOCKED 3 /* 阻塞 */
#define NTCB 3 /* 系统线程的最大数 */
#define TL 10 /* 时间片大小 */
#define NBUF 2 /* 消息缓冲区数目 */
#define NTEXT 50 /* 文本输出大小 */
char far* intdos_ptr = 0;
char far* crit_err_ptr = 0;
int timecount = 0;
int current = -1;
typedef unsigned int UINT16;
typedef struct/* 信号量 */
{
int value;
struct TCB* wq;
} semaphore;
semaphore mutexfb = {1, NULL};
semaphore sfb = {2, NULL};
semaphore sem1 = {0, NULL};
semaphore sem2 = {0, NULL};
struct buffer/* 消息缓冲区 */
{
int sender; /*消息发送者的标识数 */
int size; /* 消息长度<=NTEXT 个字节 */
char text[NTEXT]; /* 消息正文 */
struct buffer* next; /* 指向下一个消息缓冲区的指针 */
} *freebuf;
struct TCB /* 线程控制块 */
{
unsigned char* stack; /* 堆栈的起始地址 */
unsigned ss;
unsigned sp; /* 堆栈段址和堆栈指针 */
char state; /* 进程状态 */
char name[10]; /* 线程的外部标识符 */
int value; /*优先级*/
struct TCB* next; /* 指向控制快指针 */
struct buffer* mq; /* 消息缓冲队列首指针 */
semaphore mutex; /* 互斥信号量 */
semaphore sm; /* 消息缓冲队列计数信号量*/
} tcb[NTCB];
struct int_regs /* 堆栈现场保护和恢复结构体 */
{
unsigned BP, DI, SI, DS, ES, DX, CX, BX, AX, IP, CS, Flags, off, seg;
};
typedef int(far* codeptr)(void);
void interrupt(*old_int8)(void);
int DosBusy(void);
void InitIndos(void);
void InitTcb();
void interrupt new_int8(void);
void interrupt swtch();
void send(char *receiver, char *a, int size);
int receive(char *sender, char *a);
void p(semaphore *sem);
void v(semaphore *sem);
int Create(char* name, codeptr code, int stacklen, int prio); /* 创建线程 */
void Destroy(int i);
void f1() /* 1#线程 */
{
long i, j, k;
char a[NTEXT];
int len = 0;
i = 0;
while(++i)
{
if(i == 5) break;
printf("f1 sending!\n");
send("f2", "f1 send message to f2", 21);
v(&sem1);
p(&sem2);
len = receive("f2", a);
printf("f1 received %d: %s\n\n", len, a);
}
}
void f2() /* 2#线程 */
{
int i, j, k;
int len = 0;
char a[NTEXT];
i = 0;
while(++i)
{
if(i == 5) break;
p(&sem1);
len = receive("f1", a);
printf("f2 received %d: %s\n\n", len, a);
printf("f2 sending!\n");
send("f1", "f2 send message to f1", 21);
v(&sem2);
}
}
void InitInDos() /* 取得INDOS标志和严重错误标志地址 */
{
union REGS regs;
struct SREGS segregs;
regs.h.ah = GET_INDOS; /* 使用34H号系统功能调用 */
intdosx(®s, ®s, &segregs);
intdos_ptr = MK_FP(segregs.es, regs.x.bx);
if(_osmajor < 3)
crit_err_ptr = intdos_ptr + 1; /* 严重错误在INDOS后一字节处 */
else if(_osmajor == 3 && _osminor == 0)
crit_err_ptr = intdos_ptr - 1; /* 严重错误在INDOS前一字节处 */
else
{
regs.x.ax = GET_CRIT_ERR;
intdosx(®s, ®s, &segregs);
crit_err_ptr = MK_FP(segregs.ds, regs.x.si);
}
}
int DosBusy(void) /* 判断DOS是否忙 */
{
if(intdos_ptr && crit_err_ptr)
return(*intdos_ptr || *crit_err_ptr); /* DOS忙,返回严重错误标志 */
else
return(-1); /* DOS不忙 */
}
void InitTcb() /* 初始化线程 */
{
int i;
for(i = 0; i < NTCB; i++)
{
tcb[i].state = BLANK; /* 初始状态标志 */
tcb[i].mq = NULL;
tcb[i].mutex.value = 1;
tcb[i].mutex.wq = NULL;
tcb[i].sm.value = 0;
tcb[i].sm.wq = NULL;
}
}
void Destroy(int i)
{
if(tcb[i].state == RUNNING)
{
disable();
tcb[i].state = FINISHED;
//strcpy(tcb[i].name,NULL);
free(tcb[i].stack);
tcb[i].ss = 0;
tcb[i].sp = 0;
enable();
}
// return;
}
void over()
{
Destroy(current);
swtch();
}
int Create(char *name, codeptr code, int stacklen, int value)
{
int i;
char *p;
struct int_regs *pt;
unsigned int *pp;
for(i = 1; i < NTCB; i++)
{
if(tcb[i].state == BLANK || tcb[i].state == FINISHED)
break;
}
if(i == NTCB)
return -1;
tcb[i].value = value;
strcpy(tcb[i].name, name);
tcb[i].stack = (p = (unsigned char*)malloc(stacklen));
memset(tcb[i].stack, 0xff, stacklen);
p = p + stacklen;
*(p - 1) = (FP_SEG(over) & 0xff00) >> 8;
*(p - 2) = FP_SEG(over) & 0x00ff;
*(p - 3) = (FP_OFF(over) & 0xff00) >> 8;
*(p - 4) = FP_OFF(over) & 0x00ff;
*(p - 5) = 0x02;
*(p - 6) = 0x00;
*(p - 7) = (FP_SEG(code) & 0xff00) >> 8;
*(p - 8) = FP_SEG(code) & 0x00ff;
*(p - 9) = (FP_OFF(code) & 0xff00) >> 8;
*(p - 10) = FP_OFF(code) & 0x00ff;
*(p - 19) = (_ES & 0xff00) >> 8;
*(p - 20) = _ES & 0x00ff;
*(p - 21) = (_DS & 0xff00) >> 8;
*(p - 22) = _DS & 0x00ff;
tcb[i].sp = FP_OFF((UINT16 *)(p - 28));
tcb[i].ss = FP_SEG((UINT16 *)(p - 28));
tcb[i].state = READY;
return i;
}
void tcb_state() /* 线程状态信息 */
{
int i;
for(i = 0; i < NTCB; i++)
if(tcb[i].state != BLANK)
{
switch(tcb[i].state)
{
case FINISHED:
printf("\ntcb[%d] is FINISHED\n", i);
break;
case RUNNING:
printf("tcb[%d] is RUNNING\n", i);
break;
case READY:
printf("tcb[%d] is READY\n", i);
break;
case BLOCKED:
printf("tcb[%d] is BLOCKED\n", i);
break;
}
}
}
int all_finished()
{
int i;
for(i = 1; i < NTCB; i++)
if(tcb[i].state == RUNNING || tcb[i].state == BLOCKED || tcb[i].state == READY)
return 0;
return 1;
}
int Find()
{
int i = 0, index = 0, maxpri = 0;
for(i = 1; i < NTCB; i++)
{
if(tcb[i].state != FINISHED && tcb[i].state != BLOCKED
&& maxpri < tcb[i].value)
{
maxpri = tcb[i].value;
index = i;
}
}
return index;
}
void interrupt new_int8(void) /* CPU 调度*/
{
int i;
(*old_int8)(); /* 指向原来时钟中断处理过程入口的中断处理函数指针 */
timecount++;
if(timecount == TL) /* 时间片是否到? */
{
if(!DosBusy()) /* DOS是否忙? */
{
disable();
tcb[current].ss = _SS; /* 保存现场 */
tcb[current].sp = _SP;
if(tcb[current].state == RUNNING)
tcb[current].state = READY;
i = Find();
if(i < 0)
return;
_SS = tcb[i].ss;
_SP = tcb[i].sp;
tcb[i].state = RUNNING;
current = i;
timecount = 0; /* 重新计时 */
enable();
}
else
return;
}
else
return;
}
void interrupt swtch() /* 其他原因CPU调度 */
{
int i;
if(tcb[current].state != FINISHED
&& current != 0 && tcb[current].state != BLOCKED) /* 当前线程还没结束 */
return;
i = Find();
if(i < 0)
return;
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();
}
struct buffer*Initbuf(void)
{
struct buffer *p, *pt, *pt2;
int i;
pt2 = pt = (struct buffer*)malloc(sizeof(struct buffer));
pt->sender = -1;
pt->size = 0;
strcmp(pt->text, "");
pt->next = NULL;
for(i = 0; i < NBUF - 1; i++)
{
p = (struct buffer*)malloc(sizeof(struct buffer));
p->sender = -1;
p->size = 0;
p->text[NTEXT] = '\0';
p->next = NULL;
pt2->next = p;
pt2 = p;
}
return pt;
}
struct buffer* getbuf(void) /* 从空闲消息缓冲队列队头上取下一缓冲区 */
{
struct buffer *buf;
buf = freebuf; /* 取得缓冲队列的缓冲区*/
freebuf = freebuf->next;
return(buf); /* 返回指向该缓冲区的指针 */
}
void putbuf(struct buffer *pt)
{
struct buffer *p = freebuf;
while(p->next)
p = p->next;
p->next = pt;
pt->next = NULL;
}
void block(struct TCB **p) /* 阻塞原语 */
{
struct TCB *pp;
tcb[current].state = BLOCKED;
if((*p) == NULL)
*p = &tcb[current]; /* 阻塞队列空,直接放入 */
else
{
pp = *p;
while(pp->next)
pp = pp->next; /* 找到阻塞队列最后一个节点 */
pp->next = &tcb[current]; /* 放入阻塞队列 */
}
tcb[current].next = NULL;
swtch(); /* 重新进行CPU调度 */
}
void wakeup_first(struct TCB **p) /* 唤醒队首线程 */
{
struct TCB *pl;
if((*p) == NULL)
return;
pl = (*p);
(*p) = (*p)->next; /* 得到阻塞队列队首线程 */
pl->state = READY; /* 修为就绪状态 */
pl->next = NULL;
swtch();
}
void p(semaphore *sem)
{
struct TCB **qp;
disable();
sem->value = sem->value - 1;
if(sem->value < 0)
{
qp = &(sem->wq);
block(qp);
}
enable();
}
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 insert(struct buffer **mq, struct buffer *buff)
{
/* 将buff所指的缓冲区插到*mq所指的缓冲队列末尾*/
struct buffer *temp;
if(buff == NULL)
return; /* buff为空 */
buff->next = NULL;
if(*mq == NULL) /* *mq为空 则直接插入*/
*mq = buff;
else
{
temp = *mq;
while(temp->next) /* 找到队尾 */
temp = temp->next;
temp->next = buff;
}
}
void send(char *receiver, char *a, int size)
{
/* 将地址a开始的size个字节发送给外部标识符为receiver的线程 */
struct buffer *buff;
int i, id = -1;
disable();
for(i = 0; i < NTCB; i++) /* 找receiver相对应的线程号*/
{
if(strcmp(receiver, tcb[i].name) == 0)
{
id = i;
break;
}
}
if(id == -1) /* 没找到 */
{
printf("Error:Receiver not exist!\n");
enable();
return;
}
p(&sfb); /* 空闲缓冲队列数量的互斥操作 */
p(&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); /* buff所指的缓冲区插到相应线程的消息缓冲队列末尾 */
v(&tcb[id].mutex);
v(&tcb[id].sm); /* 对应于p(&sfb),线程的消息缓冲队列数量的互斥操作 */
enable();
}
int receive(char *sender, char *a)
{
struct buffer *buff;
int i;
int size = 0;
disable();
for(i = 0; i < NTCB; i++)
if(strcmp(sender, tcb[i].name) == 0)
break;
if(i == NTCB)
{
printf("error:sender not exist!\n");
enable();
return size;
}
if(tcb[current].mq == NULL)
{
p(&tcb[current].sm);
return size;
}
p(&tcb[current].mutex); /* 当前线程的消息缓冲队列资源的互斥操作*/
buff = tcb[current].mq;
tcb[current].mq = tcb[current].mq->next;
for(i = 0; i < buff->size; i++, a++) /* 输出所接受的内容 */
(*a) = buff->text[i];
*a = '\0';
size = buff->size;
v(&tcb[current].mutex); /* 当前线程的消息缓冲队列资源的互斥操作 */
p(&mutexfb);
buff->sender = -1;
buff->size = 0;
strcpy(buff->text, "");
buff->next = NULL;
putbuf(buff);
v(&mutexfb);
v(&sfb);
p(&tcb[current].sm);
return size;
// v(&test); /* 对应于p(&Tcb[current].sm) */
}
void main()
{
long i, j, k;
InitInDos();
InitTcb();
freebuf = Initbuf();
old_int8 = getvect(8);
strcpy(tcb[0].name, "main");
tcb[0].state = RUNNING;
tcb[0].value = 0;
current = 0;
Create("f1", (codeptr)f1, 1024, 5);
Create("f2", (codeptr)f2, 1024, 6);
tcb_state();
setvect(8, new_int8);
// swtch();
while(!all_finished());
tcb[0].name[0] = '\0';
tcb[0].state = FINISHED;
setvect(8, old_int8);
tcb_state();
printf("\n Muli_task system teminated \n");
}