【操作系统实验】Linux环境下用进程实现生产者消费者问题——C语言完整代码+详细实验报告

【注意】代码在文末,以下为详细实验报告

【实验目的】

  以生产者和消费者问题为例,学习并熟悉Linux下进程通信、同步机制的具体实现方法,主要是了解并掌握信号量机制和共享内存的使用方法,进一步熟悉Linux系统的相关指令的调用。

【实验内容】
  使用共享内存和信号量机制来实现生产者和消费者进程间的一对一、一对多和多对多的通信和同步,要求在Linux下实现。

【实验环境】(含主要设计设备、器材、软件等)
在这里插入图片描述

【实验步骤、过程】(含原理图、流程图、关键代码,或实验过程中的记录、数据等)

一、数据结构

1.共享内存定义为一个结构,使得其数据成员更清晰且操作变得简单。
2.共享缓冲区采用循环队列的数据结构,如下图所示。
在这里插入图片描述
        图1 共享缓冲区数据结构

  其中,head为队头指针,tail为队尾指针,str[MAX_BUFFER_SIZE]为数据区域,num为数据数量,is_empty为一个标志,用来指明缓冲区是否为空。

二、算法描述

大致可以分为以下四个部分

1.主程序(main)

(1)创建信号量、共享内存并初始化,将申请的共享内存附加到申请通信的进程空间
在这里插入图片描述
          图2 初始化参数和信号量

(2)创建生产者、消费者进程,并执行
(3)等待所有子进程结束
(4)删除信号量和共享内存,释放空间

2.生产者进程

(1)通过shmat函数连接共享内存标识符为shm_id的共享内存,把共享内存区对象映射到该生产者进程的地址空间
在这里插入图片描述
          图3 生产者进程—连接共享内存

(2)P(SEM_EMPTY),P(MUTEX),product,V(MUTEX),V(SEM_FULL)
在这里插入图片描述
          图4 生产者进程—生产

(3)解除和共享内存的关联
在这里插入图片描述
          图5 生产者进程—解除关联

3.消费者进程

(1)通过shmat函数连接共享内存标识符为shm_id的共享内存,把共享内存区对象映射到该生产者进程的地址空间
在这里插入图片描述
          图6 消费者进程—连接共享内存

(2)P(SEM_FULL),P(MUTEX),product,V(MUTEX),V(SEM_EMPTY)
在这里插入图片描述
          图7 消费者进程—消费

(3)解除和共享内存的关联
在这里插入图片描述
          图8 消费者进程—解除关联

4.循环队列部分
在这里插入图片描述
          图9 循环队列—加入数据
在这里插入图片描述
          图10 循环队列—取出数据

5.原子操作

  P操作、V操作对封装的信号量进行“减1”“加1”操作。
  在LINUX下,通过使用 semop 函数改变信号量的值。以下P、V操作分别使用两种代码书写方式。
在这里插入图片描述
          图11 wait函数(P操作)
在这里插入图片描述
          图12 signal函数(V操作)

三、程序流程图

1.主程序流程图
在这里插入图片描述
          图13 主程序流程图

2.生产者和消费者进程流程图
在这里插入图片描述
在这里插入图片描述

          图14 生产者和消费者进程流程图

四、编译指令

//1对1
$ gcc -o pro(1)_con(1).out pro(1)_con(1).c
$ ./pro(1)_con(1).out

//1对多
$ gcc -o pro(1)_con(n).out pro(1)_con(n).c
$ ./pro(1)_con(n).out

$ gcc -o pro(n)_con(1).out pro(n)_con(1).c
$ ./pro(n)_con(1).out
//多对多
$ gcc -o pro(n)_con(n).out pro(n)_con(n).c
$ ./pro(n)_con(n).out

图15 所有编译指令

五、运行结果
1.一个生产者+一个消费者
在这里插入图片描述
          图16 pro(1)-con(1).c运行结果
2.一个生产者+多个消费者(这里选3个消费者)
在这里插入图片描述
          图17 pro(1)-con(n).c运行结果
3.多个生产者(这里选4个生产者)+一个消费者
在这里插入图片描述
          图18 pro(n)-con(1).c运行结果
4.多个生产者(这里选5个生产者)+多个消费者(这里选5个消费者)
在这里插入图片描述
          图19 pro(n)-con(n).c运行结果

【实验结果或总结】(对实验结果进行相应分析,或总结实验的心得体会,并提出实验的改进意见)

  本次实验是关于生产者和消费者之间互斥和同步的问题。问题的实质是P, V操作,实验设一个共享缓冲区,生产者和消费者互斥的使用,当一个线程使用缓冲区的时候,另一个让其等待知道前一个线程释放缓冲区为止。
  生产者消费者问题是互相合作进程关系的一种抽象,例如,在输入时,输入进程是消费者,计算进程是生产者,在输出时,计算进程是生产者,打印进程是消费者,因此该问题很大的代表性和使用价值,也是操作系统在学习进程间同步与互斥的经典问题。
  通过本次实验,我对操作系统的P,V进一步的认识,深入的了解P,V操作的实质和其重要性。课本的理论知识进一步阐述了现实的实际问题。

【代码】

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/types.h>

#define MAX_BUFFER_SIZE 10
#define SHM_MODE 0600
#define SEM_MODE 0600

#define SEM_FULL 0     //semaphore 1 --- full
#define SEM_EMPTY 1    //semaphore 2 --- empty 
#define MUTEX 2        //semaphore 3 --- exclusive access

struct my_buffer //circular queue
{
	int head;
	int tail;
	char str[MAX_BUFFER_SIZE];
	int num; //number of letter
	int is_empty;
};

const int N_CONSUMER = 5;
const int N_PRODUCER = 5;
const int N_BUFFER = 10;
const int N_WORKTIME = 10;
int shm_id = -1;
int sem_id = -1;
pid_t child; //process type
pid_t parent; //process type


int get_rand_num() //0~9 random number
{
    int digit;
    srand((unsigned)(getpid() + time(NULL)));
    digit = rand() % 10;
    return digit;
}


char get_rand_letter() //A~Z random letter
{
    char letter;
    srand((unsigned)(getpid() + time(NULL)));
    letter = (char)((rand() % 26) + 'A');
    return letter;
}

/*
struct sembuf
{
    unsigned short int sem_num;    //semaphore sequence number
    short int sem_op;              //operation: >0 or =0 or <0 
    short int sem_flg;             //operation ID: 0(normal),IPC_WAIT,SEM_UNDO
}
*/

void Wait(int sem_id,int sem_num) //P operation
{
    struct sembuf sb;
    sb.sem_num = sem_num;
    sb.sem_op = -1; //Wait(-1)
    sb.sem_flg = SEM_UNDO; //add abs(sem_op)
    //the last 1 is the size of the sembuf type---sb
    if(semop(sem_id,&sb,1) < 0) //judge
    {
        perror("Wait Operation Failed!\n");
	exit(1);
    }
}


void Signal(int sem_id,int sem_num) //V operation
{
    struct sembuf sb;
    sb.sem_num = sem_num;
    sb.sem_op = 1; //Signal(+1)
    sb.sem_flg = SEM_UNDO; //sub sem_op
    //the last 1 is the size of the sembuf type---sb
    if(semop(sem_id,&sb,1) < 0) //judge
    {
        perror("Signal Operation Failed!\n");
        exit(1);
    }
}

void printTime() //output running time
{
    time_t now;
    struct tm *timenow;
    time(&now);
    timenow = localtime(&now);
    printf("runtime: %s",asctime(timenow));
}

int main()
{
    shm_id = shmget(IPC_PRIVATE,MAX_BUFFER_SIZE,SHM_MODE); //apply for shared memory
    if(shm_id < 0)
    {
        perror("apply for shared memory failed!\n");
        exit(1);
    }

    struct my_buffer *bf;
    bf = shmat(shm_id, 0, 0);//add buffer to process space  
    if (bf == (void*)-1) 
    {
        perror("add buffer to process space failed!\n");
        exit(1);
    }

    if((sem_id = semget(IPC_PRIVATE,3,SEM_MODE)) < 0) //create three semaphores
    {                                                 //SEM_FULL,SEM_EMPTY,MUTEX
        perror("create three semaphores failed!\n");
        exit(1);
    }

    //set the three semaphores
    if(semctl(sem_id,SEM_FULL,SETVAL,0) == -1) //SEM_FULL->0
    {
        perror("SEM_FULL set error!\n");
        exit(1);
    }

    if(semctl(sem_id,SEM_EMPTY,SETVAL,10) == -1) //SEM_EMPTY->10
    {
        perror("SEM_EMPTY set error!\n");
        exit(1);
    }

    if(semctl(sem_id,MUTEX,SETVAL,1) == -1) //MUTEX->1
    {
        perror("MUTEX set error!\n");
        exit(1);
    }
    
    //set queue(buffer-shmptr) message
    bf->head = 0;
    bf->tail = 0;
    bf->is_empty = 1;
    bf->num = 0;
    
    //gcc not allow for(int i=0;...)
    int i,j;
    for(i = 0; i < N_PRODUCER; i++) //producer
    {
        parent = fork();
        if(parent < 0)
        {
            perror("fork failed!\n");
            exit(1);
        }
        else if(parent == 0)
        {
            bf = shmat(shm_id, 0, 0);
            if(bf == (void*) - 1)
            {
                perror("add buffer to process space failed!\n");
                exit(1);
            }
            int count = 0;
            for(j = 0; j < N_WORKTIME; j++)
            {
                Wait(sem_id, SEM_EMPTY);
                Wait(sem_id, MUTEX);
                sleep(get_rand_num());
                //printf message and pid ppid
                printf("-------------------------------------------------------------\n");
                printf("This is producer %d , PID = %d , PPID = %d\n", i + 1, getpid(), getppid());
                //product
                char c = get_rand_letter();
                bf->str[bf->tail] = c;
                bf->tail = (bf->tail + 1) % MAX_BUFFER_SIZE;
                bf->is_empty = 0;
                bf->num++;

                printTime();
                //printf buffer
                int p;
                printf("buffer data (%d letters):",bf->num);
                p = (bf->tail-1 >= bf->head) ? (bf->tail-1) : (bf->tail-1 + MAX_BUFFER_SIZE);
                
                for (p; !(bf->is_empty) && p >= bf->head; p--)
                {
                    printf("%c", bf->str[p % MAX_BUFFER_SIZE]);
                }
                //printf message
                printf("\nproducer %d puts '%c'.\n", i + 1, c);
                printf("-------------------------------------------------------------\n");
                //flush buffer
                fflush(stdout);
                Signal(sem_id, MUTEX);
                Signal(sem_id, SEM_FULL);
            }
            //disconnect the shared segment from the process
            shmdt(bf);
            exit(0);
        }
    }

    for(i = 0; i < N_CONSUMER; i++)
    {
        child = fork();
        if(child < 0)
        {
            perror("fork failed!\n");
            exit(1);
        }
        else if(child == 0)
        {
            int count = 0;
            bf = shmat(shm_id, 0, 0);
            if (bf == (void*) - 1)
            {
                perror("add buffer to process space failed!\n");
                exit(1);
            }
            for(j = 0; j < N_WORKTIME; j++) //consumer
            {
                Wait(sem_id, SEM_FULL);
                Wait(sem_id, MUTEX);
                sleep(get_rand_num());
                //printf message and pid ppid
                printf("-------------------------------------------------------------\n");
                printf("This is consumer %d , PID = %d , PPID = %d\n", i + 1, getpid(), getppid());
                //consume
                char lt = bf->str[bf->head];
                bf->head = (bf->head + 1) % MAX_BUFFER_SIZE;
                bf->is_empty = (bf->head == bf->tail); 
                bf->num--;

                printTime();
                //printf buffer
                int p;
                printf("buffer data (%d letters):",bf->num);
                p = (bf->tail-1 >= bf->head) ? (bf->tail-1) : (bf->tail-1 + MAX_BUFFER_SIZE);
                for (p; !(bf->is_empty) && p >= bf->head; p--)
                {
                    printf("%c", bf->str[p % MAX_BUFFER_SIZE]);
                }
                //printf message
                printf("\nconsumer %d gets '%c'.\n", i + 1, lt);
                printf("-------------------------------------------------------------\n");
                //flush buffer
                fflush(stdout);
                Signal(sem_id,MUTEX);
                Signal(sem_id,SEM_EMPTY);
            }
            //disconnect the shared segment from the process
            shmdt(bf);
            exit(0);
        }
    }
    //main process quit
    while (wait(0) != -1);
    //disconnect the shared segment from the process
    shmdt(bf);

    shmctl(shm_id,IPC_RMID,0);
    shmctl(sem_id,IPC_RMID,0);
    printf("the main process is over !\n");
    fflush(stdout);
    exit(0);
    return 0;
}

  • 65
    点赞
  • 362
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小天才才

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

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

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

打赏作者

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

抵扣说明:

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

余额充值