操作系统实验报告生产者消费者问题
操作系统 课程设计报告书
题目: 生产者-消费者问题
姓 名: 学 号: 专 业: 指导教师:
完成时间: 2013年 11月----2013 年 12月
第1章 引言
1.1 设计背景
生产者-消费者问题是一个经典的进程同步问题,该问题最早由Dijkstra 提出,用以演示他提出的信号量机制。在同一个进程地址空间内执行的两个线程。生产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费。消费者线程从缓冲区中获得物品,然后释放缓冲区。当生产者线程生产物品时,如果没有空缓冲区可用,那么生产者线程必须等待消费者线程释放出一个空缓冲区。当消费者线程消费物品时,如果没有满的缓冲区,那么消费者线程将被阻塞,直到新的物品被生产出来。
1.2 设计目标
以生产者和消费者问题为例,学习Linux 和Windows 下进程通信、同步机制的具体实现方法,主要是信号量和共享内存。
第2章 设计原理、函数说明
设计原理:
多进程是一种非常简洁的多任务操作方式。在Linux 系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种烦琐的多任务工作方式。
生产者-消费者方案是多进程应用程序开发中最常用的构造之一。因此困难也在于此。因为在一个应用程序中可以多次重复生产者-消费者行为,其代码也可以如此。设计中创建了Consumer 类,该类通过在一些多进程应用程序中促进代码重用以及简化代码调试和维护来解决这个问题。多进程应用程序通常利用生产者-消费者编程方案,其中由生产者进程创建重复性作业,将其传递给作业队列,然后由消费者进程处理作业。
多进程是一种使应用程序能同时处理多个操作的编程技术。通常有两种不同类型的多进程操作使用多个进程:适时事件,当作业必须在特定的时间或在特定的间隔内调度执行时;后台处理,当后台事件必须与当前执行流并行处理或执行时;适时事件的示例包括程序提醒、超时事件以及诸如轮询和刷新之类的重复性操作。后台处理的示例包括等待发送的包或等待处理的已接收的消息。
生产者-消费者方案很适合于后台处理类别的情况。这些情况通常围绕一个作业“生产者”方和一个作业“消费者”方。当然,关于作业并行执行还有其它考虑事项。在大多数情况下,对于使用同一资源的作业,应以FCFS 的方式按顺序处理,这可以通过使用单进程的消费者轻松实现。通过使用这种方法,使用单个进程来访问单个资源,而不是用多个进程来访问单个资源。要启用标准消费者,当作业到来时创建一个作业队列来存储所有作业。生产者进程通过将新对象添加到消费者队列来交付这个要处理的新对象。然后消费者进程从队列取出每个对象,并依次处理。当队列为空时,消费者进入休眠。当新的对象添加到空队列时,消费者会醒来并处理该对象。
函数说明:
a. 主程序 (main):
i. 创建信号量并进行初始化
ii. 创建生产者、消费者线程,生产者执行void *product(),消费者执行void
*prochase()
iii. 等待所有子进程的结束 iv. 销毁所有线程
b. 生产者方法 product() :
这个函数是生产者进行的生产过程,为所有的生产者所共享。 c. 消费者方法 prochase() :
这个函数是消费者进行的生产过程,为所有的消费者所共享。
第3章 程序详细设计
3.1程序模块设计
该实验主要分为三大模块:
1. 主程序,创建并控制程序的流程,
其中控制线程的活动以及信号量的操作,如图3-1-1所示; 2. 生产者模块:生产者对缓冲区的操作,如图3-1-2所示; 3. 消费者模块:消费者对缓冲区的操作,如图3-1-3所示。 程序相关模块的流程图
图3-1-1 主程序
图3-1-2 生产者流程
图3-1-3 消费者流程
3.2程序代码结构
通过分析,我们已经了解到了可以采用信号量来解决n 个进程的临界区问题,这n 个进程共享一个信号量mutex(mutual exclusion),并初始化为1。每个进程P i 的组织结构如下图。
由于本系统我们研究的是有限缓冲区(Bounded-Buffer )的生产者消费者问题。而且根据初始条件可知,该缓冲区内有20个缓冲项,每个缓冲项存储一个整形数。信号量mutex 提供了对缓冲池访问的互斥要求,并初始化为1。信号量empty 和full 分别用来表示空缓冲项和满缓冲项的数量。信号量empty 初始化为20,而信号量full 初始化为0;
生产者进程和消费者进程的代码如图。注意生产者和消费者之间的对称性可以这样来理解代码:生产者为消费者生产满缓冲项,或消费者为生产者生产空缓冲项。
实验结果如图4-1截图所示:
图4-1 实验结果截图
进程的同步与互斥是操作系统课程中非常重要的一部分内容。通过本次课程设计,我不仅学会了使用信号量机制解决有限缓冲区的生产者消费者问题,而且对Linux 系统下多线程编程有了更深入的了解。
虽然实验以前我已经对信号量机制解决进程间同步问题的原理有了很清楚的认识,但是此次课程设计中仍然遇到了很多问题,如Linux 系统下各种系统调用以及函数的各种参数,都花费了我很多时间去网上看各种资料——虽然Linux 系统中可以很方便的阅读源代码以及使用man 命令进行相应指令的查看,但是全英文的资料让人看了还是有点不懂,看来还要多多加强计算机专业英语的学习,以后便可以在Linux 系统中查看各种帮助文件。
另一个问题就是在编译的时候遇到的,刚开始用gcc –o 多对多 多对多.c 进行编译的时候总是提示出错,后来查了相关资料才知道pthread 库不是 Linux 系统默认的库,连接时需要使用静态库 libpthread.a,所以在使用pthread_create()等函数时,需要链接该库才能编译通过。以前再用Windows IDE 进行编程的时候基本上不会遇到这样的问题,看来的确IDE 为我们做了很多工作,隐藏了一些技术细节,有时候隐藏这些细节虽然可以方便我们,却让我们离真相越来越远。
最近使用Linux 进行相关的编程操作,感悟还是挺多的。Windows 下的层层封装的确让我们感到很方便,但同时也让我们编程变得越来越不灵活。因为我们不用再去了解底层的东西因此一遇到问题就会束手无策。这段时间一直用Linux 进行编程,感觉很方便,跟我以前想象地完全不一样,而且我掌握了不少命令,比我以前用鼠标操作的时候快多了,而且在Bash 下可以用Ctrl+Z随时中止正在运行的进程或命令,再用fg %id 重新运行。Linux 下的编程也很方便,
我还了解了Makefile 的编写方法,在多文件编程的时候可以极高地提高效率。 总之,我之后会加强专业英语的学习,以便更好的利用Linux 操作系统进行编程之路的学习。
附录:实验代码
#include #include #include #include #include
#define N 30 // 生产者的数目 #define M 30 //消费者的数目 #define p 20 // 缓冲数目
int in = 0; // 生产者放置产品的位置 int out = 0; // 消费者取产品的位置
int buff[p] = {0}; // 缓冲初始化为0, 开始时没有产品
sem_t empty_sem; // 同步信号量, 当满了时阻止生产者放产品 sem_t full_sem; // 同步信号量, 当没产品时阻止消费者消费 pthread_mutex_t mutex; // 互斥信号量, 一次只有一个线程访问缓冲
int product_id = 0; //生产者id int prochase_id = 0; //消费者id
/* 打印缓冲情况 */ void print() { int i;
for(i = 0; i
printf("\n"); }
/* 生产者方法 */ void *product() {
int id = ++product_id; while(1) {
// 用sleep 的数量可以调节生产和消费的速度,便于观察 sleep(1); //sleep(1);
sem_wait(&empty_sem); pthread_mutex_lock(&mutex);
in = in % p;
printf("product%d in %d. like: \t", id, in);
buff[in] = 1; print(); ++in;
pthread_mutex_unlock(&mutex); sem_post(&full_sem); } }
/* 消费者方法 */
void *prochase() {
int id = ++prochase_id; while(1) {
// 用sleep 的数量可以调节生产和消费的速度,便于观察 sleep(1); //sleep(1);
sem_wait(&full_sem); pthread_mutex_lock(&mutex);
out = out % p;
printf("prochase%d in %d. like: \t", id, out);
buff[out] = 0; print(); ++out;
pthread_mutex_unlock(&mutex); sem_post(&empty_sem); } } int main() {
pthread_t id1[N]; pthread_t id2[M]; int i;
int ret[N],RET[M];
// 初始化同步信号量
int ini1 = sem_init(&empty_sem, 0, M); int ini2 = sem_init(&full_sem, 0, 0); if(ini1 && ini2 != 0) {
printf("sem init failed \n"); exit(1); }
//初始化互斥信号量
int ini3 = pthread_mutex_init(&mutex, NULL); if(ini3 != 0) {
printf("mutex init failed \n"); exit(1); }
// 创建N 个生产者线程 for(i = 0; i
ret[i] = pthread_create(&id1[i], NULL, product, (void *)(&i)); if(ret[i] != 0) {
printf("product%d creation failed \n", i); exit(1); } }
//创建N 个消费者线程 for(i = 0; i
RET[i] = pthread_create(&id2[i], NULL, prochase, NULL);
if(RET[i] != 0) {
printf("prochase%d creation failed \n", i); exit(1); } }
//销毁线程 for(i = 0; i
pthread_join(id1[i],NULL); }
for(i = 0; i
pthread_join(id2[i],NULL); } exit(0); }