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

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

【实验目的】

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

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

【实验环境】(含主要设计设备、器材、软件等)
【操作系统实验】Linux环境下用进程实现生产者消费者问题——C语言完整代码+详细实验报告_第1张图片

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

一、数据结构

1.共享内存定义为一个结构,使得其数据成员更清晰且操作变得简单。
2.共享缓冲区采用循环队列的数据结构,如下图所示。
【操作系统实验】Linux环境下用进程实现生产者消费者问题——C语言完整代码+详细实验报告_第2张图片
        图1 共享缓冲区数据结构

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

二、算法描述

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

1.主程序(main)

(1)创建信号量、共享内存并初始化,将申请的共享内存附加到申请通信的进程空间
【操作系统实验】Linux环境下用进程实现生产者消费者问题——C语言完整代码+详细实验报告_第3张图片
          图2 初始化参数和信号量

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

2.生产者进程

(1)通过shmat函数连接共享内存标识符为shm_id的共享内存,把共享内存区对象映射到该生产者进程的地址空间
【操作系统实验】Linux环境下用进程实现生产者消费者问题——C语言完整代码+详细实验报告_第4张图片
          图3 生产者进程—连接共享内存

(2)P(SEM_EMPTY),P(MUTEX),product,V(MUTEX),V(SEM_FULL)
【操作系统实验】Linux环境下用进程实现生产者消费者问题——C语言完整代码+详细实验报告_第5张图片
          图4 生产者进程—生产

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

3.消费者进程

(1)通过shmat函数连接共享内存标识符为shm_id的共享内存,把共享内存区对象映射到该生产者进程的地址空间
【操作系统实验】Linux环境下用进程实现生产者消费者问题——C语言完整代码+详细实验报告_第6张图片
          图6 消费者进程—连接共享内存

(2)P(SEM_FULL),P(MUTEX),product,V(MUTEX),V(SEM_EMPTY)
【操作系统实验】Linux环境下用进程实现生产者消费者问题——C语言完整代码+详细实验报告_第7张图片
          图7 消费者进程—消费

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

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

5.原子操作

  P操作、V操作对封装的信号量进行“减1”“加1”操作。
  在LINUX下,通过使用 semop 函数改变信号量的值。以下P、V操作分别使用两种代码书写方式。
【操作系统实验】Linux环境下用进程实现生产者消费者问题——C语言完整代码+详细实验报告_第8张图片
          图11 wait函数(P操作)
【操作系统实验】Linux环境下用进程实现生产者消费者问题——C语言完整代码+详细实验报告_第9张图片
          图12 signal函数(V操作)

三、程序流程图

1.主程序流程图
【操作系统实验】Linux环境下用进程实现生产者消费者问题——C语言完整代码+详细实验报告_第10张图片
          图13 主程序流程图

2.生产者和消费者进程流程图
【操作系统实验】Linux环境下用进程实现生产者消费者问题——C语言完整代码+详细实验报告_第11张图片
【操作系统实验】Linux环境下用进程实现生产者消费者问题——C语言完整代码+详细实验报告_第12张图片

          图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.一个生产者+一个消费者
【操作系统实验】Linux环境下用进程实现生产者消费者问题——C语言完整代码+详细实验报告_第13张图片
          图16 pro(1)-con(1).c运行结果
2.一个生产者+多个消费者(这里选3个消费者)
【操作系统实验】Linux环境下用进程实现生产者消费者问题——C语言完整代码+详细实验报告_第14张图片
          图17 pro(1)-con(n).c运行结果
3.多个生产者(这里选4个生产者)+一个消费者
【操作系统实验】Linux环境下用进程实现生产者消费者问题——C语言完整代码+详细实验报告_第15张图片
          图18 pro(n)-con(1).c运行结果
4.多个生产者(这里选5个生产者)+多个消费者(这里选5个消费者)
【操作系统实验】Linux环境下用进程实现生产者消费者问题——C语言完整代码+详细实验报告_第16张图片
          图19 pro(n)-con(n).c运行结果

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

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

【代码】

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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;
}

你可能感兴趣的:(操作系统,操作系统,C/C++,进程,生产者消费者)