【注意】代码在文末,以下为详细实验报告
【实验目的】
以生产者和消费者问题为例,学习并熟悉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
#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 tail = 0;
bf->is_empty = 1;
bf->num = 0;
//gcc not allow for(int i=0;...)
int i,j;
for(i = 0; i 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 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;
}