操作系统实验3-线程间的双向消息通信

        线程间的双向消息通信


       我们知道,信号量的值初始化为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(&regs, &regs, &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(&regs, &regs, &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");
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基本的 Linux 3 个线程消息通信例子,包括创建线程、发送消息和接收消息。其中,线程通信使用信号量和共享内存实现: ``` #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #include <semaphore.h> #include <fcntl.h> #include <sys/mman.h> #define SHARED_MEM_SIZE 128 #define SHARED_FILENAME "/shared_mem" #define SEM_FILENAME "/semaphore" typedef struct { int flag; // 消息标识 char data[64]; // 消息内容 } Message; Message* shared_mem; // 共享内存 sem_t* semaphore; // 信号量 void* thread1(void* arg) { // 发送线程 int i; for (i = 0; i < 5; i++) { sem_wait(semaphore); // 等待信号量 shared_mem->flag = 1; // 设置标识为 1 sprintf(shared_mem->data, "Thread 1 message %d", i); printf("Thread 1 sent message: %s\n", shared_mem->data); sem_post(semaphore); // 发送信号量 sleep(1); } pthread_exit(NULL); // 退出线程 } void* thread2(void* arg) { // 接收线程 int i; for (i = 0; i < 5; i++) { sem_wait(semaphore); // 等待信号量 if (shared_mem->flag == 1) { // 收到标识为 1 的消息 printf("Thread 2 received message: %s\n", shared_mem->data); shared_mem->flag = 0; // 清除标识 } sem_post(semaphore); // 发送信号量 sleep(1); } pthread_exit(NULL); // 退出线程 } int main() { int shm_fd; pthread_t tid1, tid2; // 创建共享内存 shm_fd = shm_open(SHARED_FILENAME, O_CREAT | O_RDWR, 0666); ftruncate(shm_fd, SHARED_MEM_SIZE); shared_mem = (Message*) mmap(NULL, SHARED_MEM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); // 创建信号量 semaphore = sem_open(SEM_FILENAME, O_CREAT, 0666, 1); // 创建线程 pthread_create(&tid1, NULL, thread1, NULL); pthread_create(&tid2, NULL, thread2, NULL); // 等待线程结束 pthread_join(tid1, NULL); pthread_join(tid2, NULL); // 关闭并删除共享内存和信号量 munmap(shared_mem, SHARED_MEM_SIZE); close(shm_fd); shm_unlink(SHARED_FILENAME); sem_close(semaphore); sem_unlink(SEM_FILENAME); return 0; } ``` 上述代码中,首先创建了一个消息结构体 `Message`,它包含一个消息标识 `flag` 和一个消息内容 `data`。 然后使用 `mmap()` 函数创建共享内存和 `sem_open()` 函数创建信号量。其中,`sem_post()` 和 `sem_wait()` 函数用于发送和接收信号量,在发送和接收消息前必须获取信号量。 创建线程使用 `pthread_create()` 函数,分别创建发送和接收线程,并使用 `pthread_join()` 函数等待线程结束,最后释放共享内存和信号量。在发送线程中,设置标识为 1,表示发送消息,在共享内存中存储消息内容。在接收线程中,等待标识为 1 的消息,然后读取共享内存中的消息内容,并清除标识。发送和接收线程都设置了睡眠时,以便更好地模拟消息的发送和接收。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值