模拟 PV 操作同步机构,且用 PV 操作解决生产者——消费者问题

实验内容:模拟实现用同步机构避免并发进程执行时可能出现的与时间有关的错误。

实验目的:进程是程序在一个数据集合上运行的过程,进程是并发执行的,也即系统中的多个进 程轮流地占用处理器运行。 我们把如干个进程都能进行访问和修改地那些变量成为公共变量。由于进程是并发执 行的,所以,如果对进程访问公共变量不加限制,那么就会产生“与时间有关”的错误,即 进程执行后,所得到的结果与访问公共变量的时间有关。为了防止这类错误,系统必须要用 同步机构来控制进程对公共变量的访问。一般说,同步机构是由若干条原语——同步原语— —所组成。本实验要求学生模拟 PV 操作同步机构的实现,模拟进程的并发执行,了解进程 并发执行时同步机构的作用。

注: 编程语言 C/C++

        编译环境:Vs code

        网上代码较多,但我所查询到的(包括我借鉴的)均是有错误的。像循环数组输出存在问题,阻塞队列指针与就绪队列指针混乱错误,我对其进行了修正。

运行结果截图:

源代码:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define BUFSIZE 3     // 缓存的大小
#define MAXSTRSIZE 10 // 最大可以输入的字符串长度
struct PCB
{
    char name[10];                   // 进程名
    char status[10];                 // 运行状态
    char blockReason[5];             // 若阻塞,其原因
    int breakPoint;                  // 断点保护
    PCB *next;                       // 指向下一个进程
} * produceProcess, *consumeProcess; // 生产者和消费者进程
struct semaphore                     // 信号量
{
    int value;  // 信号量的值
    PCB *queue; // 阻塞队列
} s1, s2;

PCB *queue_ready = new PCB;     // 就绪队列,队首指向运行的进程
char str[MAXSTRSIZE];           // 输入的字符串
char buffer[BUFSIZE + 1];       // 缓冲池
int len;                        // 输入长度
int stringPointer = 0;          // string的指针
int producePointer = 0;         // 生产者指针
int consumePointer = 0;         // 消费者指针
char temp;                      // 供打印的临时产品
char recordProduce[MAXSTRSIZE]; // 生产记录
int recordProducePointer = 0;   // 生产记录指针
char recordConsume[MAXSTRSIZE]; // 消费记录
int recordConsumePointer = 0;   // 消费记录指针
int pc = 0;                     // 程序计数器
int count_characters;           // 字符计数器
int count_consume;              // 消费计数器

void block(int s) // 阻塞函数的定义
{
    PCB *p;
    int queueNum_produce = 0;
    int queueNum_consume = 0;
    if (s == 1) // 生产进程
    {
        strcpy(produceProcess->status, "Block");     // 变为阻塞态
        strcpy(produceProcess->blockReason, "full"); // 说明原因
        p = s1.queue;
        while (p->next != NULL)
        {
            queueNum_produce++;
            p = p->next; // 找到最后一个
        }
        if (s1.queue->next == NULL)
            s1.queue->next = produceProcess;
        else
            p->next = produceProcess; // 放入阻塞队列

        queue_ready->next->breakPoint = pc;          // 保存断点
        queue_ready->next = queue_ready->next->next; // 在就绪队列中去掉,指向下一个
        produceProcess->next = NULL;
        printf("\t* produceProcess生产进程阻塞了!\n");
        queueNum_produce++;
    }
    else // 消费进程
    {
        strcpy(consumeProcess->status, "Block");      // 变为阻塞态
        strcpy(consumeProcess->blockReason, "empty"); // 说明原因
        p = s2.queue;
        while (p->next)
        {
            queueNum_consume++;
            p = p->next; // 找到最后一个
        }
        if (s2.queue->next == NULL)
            s2.queue->next = consumeProcess;
        else
            p->next = consumeProcess;                // 放入阻塞队列
        queue_ready->next->breakPoint = pc;          // 保存断点
        queue_ready->next = queue_ready->next->next; // 在就绪队列中去掉,指向下一个
        consumeProcess->next = NULL;
        printf("\t* consumeProcess消费进程阻塞了!\n");
        queueNum_consume++;
    }
    printf("\t* 阻塞的生产进程个数为:%d\n", queueNum_produce);
    printf("\t* 阻塞的消费进程个数为:%d\n", queueNum_consume);
}

void wakeup(int s) // 唤醒函数的定义
{
    PCB *p;
    PCB *q = queue_ready;
    if (s == 1) // 唤醒s1.queue队首进程,生产进程队列
    {
        p = s1.queue->next;
        s1.queue->next = p->next; // 阻塞指针指向下一个阻塞进程
        strcpy(p->status, "Ready");
        strcpy(p->blockReason, "Null");
        while (q->next) // 插入就绪队列
            q = q->next;
        q->next = p;
        p->next = NULL;
        printf("\t* p1生产进程唤醒了!\n");
    }
    else // 唤醒s2.queue队首进程,消费进程队列
    {
        p = s2.queue->next;
        s2.queue->next = p->next; // 阻塞指针指向下一个阻塞进程
        strcpy(p->status, "Ready");
        strcpy(p->blockReason, "Null");
        while (q->next) // 插入就绪队列
            q = q->next;
        q->next = p;
        p->next = NULL;
        printf("\t* c1消费进程唤醒了!\n");
    }
}

void init() // 初始化
{
    s1.value = BUFSIZE;
    s2.value = 0;
    produceProcess = (PCB *)malloc(sizeof(PCB)); // 建立新的结点,并初始化为生产者
    strcpy(produceProcess->name, "Producer");    // 初始化名字
    strcpy(produceProcess->status, "Ready");     // 初始化状态
    strcpy(produceProcess->blockReason, "Null");
    produceProcess->breakPoint = 0; // 断点保护为0
    produceProcess->next = NULL;

    consumeProcess = (PCB *)malloc(sizeof(PCB)); // 建立新的结点,并初始化为消费者
    strcpy(consumeProcess->name, "Consumer");    // 初始化名字
    strcpy(consumeProcess->status, "Ready");     // 初始化状态
    strcpy(consumeProcess->blockReason, "Null");
    consumeProcess->breakPoint = 0; // 断点保护为0
    consumeProcess->next = NULL;

    queue_ready->next = produceProcess;
    queue_ready->next->next = consumeProcess; // 初始化为生产进程在前,消费进程在后
    queue_ready->next->next->next = NULL;
    s1.queue = (PCB *)malloc(sizeof(PCB));
    s2.queue = (PCB *)malloc(sizeof(PCB)); // 信号量阻塞进程为NULL
    s1.queue->next = NULL;
    s2.queue->next = NULL;
    pc = 0;            // 初始化pc为0
    count_consume = 0; // 初始消费计数器为0
}

void P(int s)
{
    if (s == 1) // P(s1)
    {
        s1.value--;
        if (s1.value < 0)
            block(1); // 阻塞当前生产进程
        else
        {
            printf("\t* s1信号申请成功!\n");
            queue_ready->next->breakPoint = pc; // 保存断点
        }
    }
    else // P(s2)
    {
        s2.value--;
        if (s2.value < 0)
            block(2); // 阻塞当前消费进程
        else
        {
            printf("\t* s2信号申请成功!\n");
            queue_ready->next->breakPoint = pc; // 保存断点
        }
    }
}

void V(int s)
{
    if (s == 1) // V(s1)
    {
        s1.value++;
        if (s1.value <= 0)
            wakeup(1);                      // 唤醒生产进程
        queue_ready->next->breakPoint = pc; // 保存断点
    }
    else // V(s2)
    {
        s2.value++;
        if (s2.value <= 0)
            wakeup(2);                      // 唤醒消费进程
        queue_ready->next->breakPoint = pc; // 保存断点
    }
}

void processorScheduling() // 处理器调度程序
{
    int rd;
    int readyProcessNumber = 0; // 就绪进程个数,取值为1或2

    PCB *p = queue_ready;
    if (queue_ready->next == NULL) // 若无就绪进程,return
        return;
    while (p->next) // 统计就绪进程个数
    {
        readyProcessNumber++;
        p = p->next;
    }
    printf("\t* 就绪进程个数为:%d\n", readyProcessNumber);

    srand((unsigned)time(NULL));      // 随机数种子
    rd = rand() % readyProcessNumber; // 随机函数产生随机数,取值范围为0~1
    if (rd == 1)
    {
        p = queue_ready->next;
        queue_ready->next = queue_ready->next->next;
        queue_ready->next->next = p;
        p->next = NULL;                                   // 将queue_ready->next与queue_ready->next->next交换
        strcpy(queue_ready->next->status, "Runing");      // 将queue_ready->next状态改为Runing
        strcpy(queue_ready->next->next->status, "Ready"); // 将queue_ready->next->next状态改为Ready
    }
    else
        strcpy(queue_ready->next->status, "Runing"); // 将queue_ready->next状态改为Runing
    pc = queue_ready->next->breakPoint;
}

void processorExecution() // 模拟处理器指令执行
{
    if (strcmp(queue_ready->next->name, "Producer") == 0) // 当前进程为生产者
        switch (pc)
        {
        case 0: // produce
            printf("\t* 生产者生产了字符%c\n", str[stringPointer]);
            recordProduce[recordProducePointer] = str[stringPointer]; // 添加到生产记录
            stringPointer = (stringPointer + 1) % len;
            pc++;
            queue_ready->next->breakPoint = pc; // 保存断点
            break;
        case 1: // p(s1)
            pc++;
            P(1);
            break;
        case 2:                                                           // put
            buffer[producePointer] = recordProduce[recordProducePointer]; // 放到缓冲区
            printf("\t* %c字符成功入驻空缓存!\n", buffer[producePointer]);
            recordProducePointer++;
            producePointer = (producePointer + 1) % (BUFSIZE + 1);
            pc++;
            queue_ready->next->breakPoint = pc; // 保存断点
            break;
        case 3: // v(s2)
            pc++;
            printf("\t* 释放一个s2信号\n");
            V(2);
            break;
        case 4: // goto 0
            printf("\t* 生产进程goto 0 操作\n");
            pc = 0;
            count_characters--; // 剩余字符个数减1
            printf("\t* 剩余字符count=%d个\n", count_characters);
            queue_ready->next->breakPoint = pc; // 保存断点

            if (count_characters <= 0) // 生产结束
            {
                printf("\t* 生产者结束生产!\n");
                strcpy(produceProcess->status, "Stop");      // 生产者状态改为Stop
                strcpy(produceProcess->blockReason, "Null"); // 生产者阻塞原因改为Null
                produceProcess->breakPoint = -1;             // 生产者断点改为-1
                queue_ready->next = queue_ready->next->next; // 在就绪队列中去掉
            }
        }
    else // 当前进程为消费者
        switch (pc)
        {
        case 0: // p(s2)
            pc++;
            P(2);
            break;
        case 1: // get
            printf("\t* 消费者取字符!\n");
            temp = buffer[consumePointer];
            consumePointer = (consumePointer + 1) % (BUFSIZE + 1);
            pc++;
            queue_ready->next->breakPoint = pc; // 保存断点
            break;
        case 2: // v(s1)
            pc++;
            printf("\t* 释放一个s1\n");
            V(1);
            break;
        case 3: // consume
            printf("\t* 消费了字符%c\n", temp);
            recordConsume[recordConsumePointer] = temp; // 添加到消费记录
            recordConsumePointer++;
            count_consume++;
            if (count_consume >= len)
            {
                strcpy(consumeProcess->status, "Stop"); // 完成态
                consumeProcess->breakPoint = -1;
                return;
            }
            pc++;
            queue_ready->next->breakPoint = pc; // 保存断点
            break;
        case 4: // goto0
            printf("\t* 消费进程goto 0 操作\n");
            pc = 0;
            queue_ready->next->breakPoint = pc; // 保存断点
        }
}

void showProcessCondition()
{
    printf("-----------------Producer consumer simulation-----------------\n");
    printf("* The string for the simulation procedure is:\t");
    printf("%s\n", str);
    printf("* 已生产:");
    for (int i = 0; i < strlen(recordProduce); i++)
        printf("%c", recordProduce[i]);
    printf("\n* 空缓存:");
    if (consumePointer < producePointer)
        for (int i = consumePointer; i < producePointer; i++)
            printf("%c", buffer[i]);
    else if (consumePointer > producePointer)
    {
        for (int i = consumePointer; i < (BUFSIZE + 1); i++)
            printf("%c", buffer[i]);
        for (int i = 0; i < producePointer; i++)
            printf("%c", buffer[i]);
    }
    printf("\n* 已消费:");
    for (int i = 0; i < strlen(recordConsume); i++)
        printf("%c", recordConsume[i]);
    printf("\n----------------------Information of PCB----------------------\n");
    printf("进程名\t\t状态\t\t等待原因\t断点\n");
    printf("%s\t%s\t\t%s\t\t%d\n\n", produceProcess->name, produceProcess->status, produceProcess->blockReason, produceProcess->breakPoint);
    printf("%s\t%s\t\t%s\t\t%d\n", consumeProcess->name, consumeProcess->status, consumeProcess->blockReason, consumeProcess->breakPoint);
    printf("------------------------------------------This is the dividing line.--------------------------------------------------\n");
}

int main()
{
    bool runForAll = false;
    char choice;
    printf("-----------Producer consumer simulation-----------\n");
    printf("* Please enter a string:\n");
    do
    {
        scanf("%s", &str);
        getchar();
        len = strlen(str);
        if (len > MAXSTRSIZE)
        {
            printf("* The string is too long!\n");
            printf("* Please enter a string:\n");
        }
    } while (len > MAXSTRSIZE);
    count_characters = len;     // 输入字符的个数
    init();                     // 初始化
    while (count_consume < len) // 消费完所有的字符为结束
    {
        printf("*************The flow of instructions*************\n");
        processorScheduling();  // 处理器调度程序
        processorExecution();   // 模拟处理器指令执行
        showProcessCondition(); // 输出显示各个信息
        if (!runForAll)
        {
            printf("0. Exit    1. Directly running    others. Step by step\n");
            scanf("%c", &choice);
            if (choice == '0')
                exit(0);
            else if (choice == '1')
                runForAll = true;
        }
    }
    printf("\nAll elements in a string sequence have been produced and consumed.\n");
    return 0;
}
  • 15
    点赞
  • 73
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
模拟进程管理 #include <stdio.h> #include <malloc.h> //Can only be used in independent situation; //#define getmem(type) (type*)malloc(sizeof(type)) #define buffersize 5 int processnum=0;//the num of processes struct pcb { /* 定义进程控制块PCB */ int flag;//flag=1 denote producer;flag=2 denote consumer; int numlabel; char product; char state; struct pcb* processlink; }*exe=NULL,*over=NULL; typedef struct pcb PCB; PCB* readyhead=NULL,* readytail=NULL; PCB* consumerhead=NULL,* consumertail=NULL; PCB* producerhead=NULL,* producertail=NULL; //产品数量 int productnum=0; int full=0,empty=buffersize;// semaphore char buffer[buffersize];//缓冲区 int bufferpoint=0;//缓冲区指针 void linkqueue(PCB* process,PCB** tail); PCB* getq(PCB* head,PCB** tail); bool hasElement(PCB* pro); void display(PCB* p); void linklist(PCB* p,PCB* listhead); void freelink(PCB* linkhead); bool processproc(); bool waitempty(); bool waitfull(); void signalempty(); void signalfull(); void producerrun(); void comsuerrun(); bool hasElement(PCB* pro); void linklist(PCB* p,PCB* listhead) { PCB* cursor=listhead; while(cursor->processlink!=NULL){ cursor=cursor->processlink; } cursor->processlink=p; } void freelink(PCB* linkhead) { PCB* p; while(linkhead!=NULL){ p=linkhead; linkhead=linkhead->processlink; free(p); } } void linkqueue(PCB* process,PCB** tail) { if((*tail)!=NULL){ (*tail)->processlink=process; (*tail)=process; } else{ printf("队列未初始化!"); } } PCB* getq(PCB* head,PCB** tail) { PCB* p; p=head->processlink; if(p!=NULL){ head->processlink=p->processlink; p->processlink=NULL; if( head->processlink ==NULL ) (*tail)=head; } else return NULL; return p; } bool processproc() { int i,f,num; char ch; PCB* p=NULL; PCB** p1=NULL; printf("\n 请输入希望产生的进程个数?"); scanf("%d",&num); getchar(); // if(num>=100){ // printf("您怎么要产生这么多进程!Demands Denied!"); // return false; // } for(i=0;i<num;i++){ printf("\n 请输入您要产生的进程:输入1为生产者进程;输入2为消费者进程\n"); scanf("%d",&f); getchar(); p=(PCB*)malloc(sizeof(PCB)) ; if( !p) { printf("内存分配失败"); return false; } p->flag=f; processnum++; p->numlabel=processnum; p->state='w'; p->processlink=NULL; if(p->flag==1){ printf("您要产生的进程是生产者,它是第%d个进程。请您输入您要该进程产生的字符!\n",processnum); scanf("%c",&ch); getchar(); p->product=ch; productnum++; printf("您要该进程产生的字符是%c \n",p->product); } else { printf("您要产生的进程是消费者,它是第%d个进程。\n",p->numlabel); } linkqueue(p,&readytail); } return true; } bool waitempty() { if(empty<=0) { printf("进程%d:缓冲区存数,缓冲区满,该进程进入生产者等待队列\n",exe->numlabel); linkqueue(exe,&producertail); return false; } else{ empty--; return true; } } void signalempty() { PCB* p; if(hasElement(producerhead)){ p=getq(producerhead,&producertail); linkqueue(p,&readytail); printf("等待中的生产者进程进入就绪队列,它的进程号是%d\n",p->numlabel); } empty++; } bool waitfull() { if(full<=0) { printf("进程%d:缓冲区取数,缓冲区空,该进程进入消费者等待队列\n",exe->numlabel); linkqueue(exe,&consumertail); return false; } else{ full--; return true;} } void signalfull() { PCB* p; if(hasElement(consumerhead)){ p=getq(consumerhead,&consumertail); linkqueue(p,&readytail); printf("等待中的消费者进程进入就绪队列,它的进程号是%d\n",p->numlabel); } full++; } void producerrun() { if(!waitempty()) return; printf("进程%d开始向缓冲区存数%c\n",exe->numlabel,exe->product); buffer[bufferpoint]=exe->product; bufferpoint++; printf("进程%d向缓冲区存数操作结束\n",exe->numlabel); signalfull(); linklist(exe,over); } void comsuerrun() { if(!waitfull()) return; printf("进程%d开始向缓冲区取数\n",exe->numlabel); exe->product=buffer[bufferpoint-1]; bufferpoint--; printf("进程%d向缓冲区取数操作结束,取数是%c\n",exe->numlabel,exe->product); signalempty(); linklist(exe,over); } void display(PCB* p) { p=p->processlink; while(p!=NULL){ printf("进程%d,它是一个",p->numlabel); p->flag==1? printf("生产者\n"):printf("消费者\n"); p=p->processlink; } } bool hasElement(PCB* pro) { if(pro->processlink==NULL) return false; else return true; } void main() { char terminate; bool element; printf("你想开始程序吗?(y/n)"); scanf("%c",&terminate); getchar(); //Queue initialize; readyhead=(PCB*)malloc(sizeof(PCB)); if(readyhead==NULL) return; readytail=readyhead; readyhead->flag=3; readyhead->numlabel=processnum; readyhead->state='w'; readyhead->processlink=NULL; consumerhead=(PCB*)malloc(sizeof(PCB)); if(consumerhead==NULL) return; consumertail=consumerhead; consumerhead->processlink=NULL; consumerhead->flag=4; consumerhead->numlabel=processnum; consumerhead->state='w'; consumerhead->processlink=NULL; producerhead=(PCB*)malloc(sizeof(PCB)); if(producerhead==NULL) return; producertail=producerhead; producerhead->processlink=NULL; producerhead->flag=5; producerhead->numlabel=processnum; producerhead->state='w'; producerhead->processlink=NULL; over=(PCB*)malloc(sizeof(PCB)); if(over==NULL) return; over->processlink=NULL; while(terminate=='y') { if(!processproc()) break; element=hasElement(readyhead); while(element){ exe=getq(readyhead,&readytail); printf("进程%d申请运行,它是一个",exe->numlabel); exe->flag==1? printf("生产者\n"):printf("消费者\n"); if(exe->flag==1) producerrun(); else comsuerrun(); element=hasElement(readyhead); } printf("就绪队列没有进程\n"); if(hasElement(consumerhead)) { printf("消费者等待队列中有进程:\n"); display(consumerhead); } else { printf("消费者等待队列中没有进程\n"); } if(hasElement(producerhead)) { printf("生产者等待队列中有进程:\n"); display(producerhead); } else { printf("生产者等待队列中没有进程\n"); } printf("你想继续吗?(press 'y' for on)"); scanf("%c",&terminate); getchar(); } printf("\n\n 进程模拟完成.\n"); //Free the room; freelink(over); over=NULL; freelink(readyhead); readyhead=NULL; readytail=NULL; freelink(consumerhead); consumerhead=NULL; consumertail=NULL; freelink(producerhead); producerhead=NULL; producertail=NULL; getchar(); }

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

I_love_hanser_QAQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值