【SEU&SE】操作系统实验:消费者-生产者问题

README

本实验报告仅供参考,请勿直接抄袭!

一、实验目的

通过实验,掌握 Windows 和 Linux 环境下互斥锁和信号量的实现方法,加深对临界区问题和进程同步机制的理解,同时熟悉利用 Windows API 和 Pthread API 进行多线程编程的方法。

二、实验内容

  1. 在 Windows 操作系统上,利用 Win32 API 提供的信号量机制,编写应用 程序实现生产者——消费者问题。
  2. 在 Linux 操作系统上,利用 Pthread API 提供的信号量机制,编写应用程序实现生产者——消费者问题。
  3. 两种环境下,生产者和消费者均作为独立线程,并通过 empty、full、mutex 三个信号量实现对缓冲进行插入与删除。
  4. 通过打印缓冲区中的内容至屏幕,来验证应用程序的正确性。 具体内容可参见“Operating System Concepts (Seventh Edition)” Chapter 6 后的 Project(P236-241)。

三、实验步骤

  1. 首先写出解决生产者——消费者问题的伪代码,如下图所示:
    生产者——消费者问题的伪代码
  2. 查阅Win32 API提供的信号量机制和Pthread API提供的信号量机制,掌握HANDLE、CreateSemaphore、WaitForSingleObject、ReleaseSemaphore、CreateThread和sem_t、sem_init、sem_wait、sem_post、pthread_create等变量和函数的用法;
  3. 将第1步中的伪代码实现成可运行的代码:首先声明三个信号量——互斥信号量Mutex、计数信号量Empty和计数信号量Full,再声明缓冲区Buffer(1代表该位置有产品,0代表该位置没有产品),其大小为SizeOfBuffer,其中Empty的初始值为SizeOfBuffer,Full的初始值为0,Mutex的初始值为1;然后分别根据伪代码实现生产者线程produce和消费者线程consume;最后在main()中创建多线程——5个生产者和5个生产者,并使主线程等待一定时间;

四、主要数据结构及其说明

  1. HANDLE:初始化函数为 HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName),其中第一个参数表示安全控制,一般直接传入NULL,第二个参数表示初始资源数量,第三个参数表示最大并发数量,第四个参数表示信号量名称,传入NULL表示匿名信号量,返回值为创建好的HANDLE;
    wait操作为DWORD WaitForSingleObject(HANDLE hHandle, DWORDdwMilliseconds),其中第一个参数为要等待的HANDLE,第二个参数为Timeout时间,一般设为最大值INFINITE;
    signal操作为BOOL ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, LPLONG lpPreviousCount),其中第一个参数为被操作的信号量,第二个参数为增加的值,第三 个参数为指向返回信号量上次值的变量的指针,如果不需要信号量上次的值,那么这个参数可以设置为NULL,该函数如果成功返回TRUE,否则返回FALSE;
  2. sem_t:初始化函数为int sem_init(sem_t *sem, int pshared, unsigned int value),其 中第一个参数为需要初始化的sem_t的地址,第二个参数决定了信号量能否在进程之间共享,第三个参数为该信号量值的大小;
    wai操作为int sem_wait(sem_t * sem),参数为需要操作的信号量,该函数成功返回0,失败的话信号量的值不变,返回-1;
    signal操作为int sem_post(sem_t *sem),参数为需要操作的信号量,该函数成功返回0,失败 的话信号量的值不变,返回-1;

五、程序运行时的初值和运行结果

  • Windows操作系统:
    Windows操作系统

  • Linux操作系统:
    Linux操作系统

六、实验体会

实验中产生的错误以及原因分析以及解决过程:在Linux操作系统下使用终端运行程序时,输出文件中总是提示“段错误(核心已转储)”。在CSDN上查阅了相关资料后,首先推断可能是栈空间大小不足,于是在终端中使用ulimit -c unlimited和ulimit -s 819200两条指令扩大源文件大小以及栈空间大小。再次编译运行,发现依然出现相同的问题。排除了栈空间不足这个原因之后,推断可能是访问了不存在的内存地址,即指针越界。于是逐行检查各个涉及到指针的代码的运行状况。最后发现是刚开始的时候为图方便,在没有查看函数参数含义的情况下,直接将创建线程的函数写为了pthread_create(NULL, NULL, produce, NULL),而该函数的前两个参数是不能为NULL的,否则就会出现指针越界。将此处代码修改后,再次编译运行,发现可以正确运行了!

实验的体会及收获:不可以为求省事就不查看函数的参数列表以及每个参数的取值范围,否则就会出现很难排查出来的错误。幸好此次试验的代码体量较小,只有大约100行左右,排查错误较为简单。然而,如果是在大型实验中,像本次这种指针越界的Bug,是非常难排查出来的。所以,在今后的实验中,我不能再犯这种低级错误!

七、源程序

  • OnWindows:
#include<iostream>
#include<stdio.h>
#include<windows.h>
using namespace std;

HANDLE Mutex;//互斥信号 
HANDLE Full;//计数信号 ,代表目前缓冲区内有几个位置已满
HANDLE Empty;//计数信号 ,代表目前缓冲区内有几个位置是空的
const int SizeOfBuffer = 10;//缓冲区的大小,即有几个位置
int* Buffer;//缓冲区

//显示缓冲区当前的存储情况
void showBuffer()
{
	cout << "目前缓冲区的存储情况:";
	for (int i = 0; i < SizeOfBuffer; ++i)
	{
		cout << Buffer[i] << " ";
	}
	cout << endl;
}

//生产者线程
DWORD WINAPI produce(LPVOID param)
{
	do
	{
		WaitForSingleObject(Empty, INFINITE);//等待缓冲区中有空位
		WaitForSingleObject(Mutex, INFINITE);//对缓冲区形成互斥访问

		//寻找缓冲区里面的一个空位置
		for (int i = 0; i < SizeOfBuffer; ++i)
		{
			if (Buffer[i] == 0)
			{
				Buffer[i] = 1;//放置产品
				cout<<"ID为"<<GetCurrentThreadId()<<"的生产者在缓冲区的 第"<<i<<"个位置放置一个产品"<<endl;
				showBuffer();
				break;
			}
		}

		ReleaseSemaphore(Mutex, 1, NULL);//释放互斥信号量Mutex
		ReleaseSemaphore(Full, 1, NULL);//使Full加一
	} while (true);

	return 0;
}

//消费者线程
DWORD WINAPI consume(LPVOID param)
{
	do
	{
		WaitForSingleObject(Full, INFINITE);//等待缓冲区中有产品
		WaitForSingleObject(Mutex, INFINITE);//对缓冲区形成互斥访问

		//寻找缓冲区里面的一个有产品的位置
		for (int i = 0; i < SizeOfBuffer; ++i)
		{
			if (Buffer[i] == 1)
			{
				Buffer[i] = 0;//»°◊fl≤˙∆∑
				cout << "ID为" << GetCurrentThreadId() << "的消费者在缓冲区的第" << i << "个位置取走了一个产品" << endl;
				showBuffer();
				break;
			}
		}

		ReleaseSemaphore(Mutex, 1, NULL);//释放互斥信号量Mutex
		ReleaseSemaphore(Empty, 1, NULL);//使Empty加一
	} while (true);

	return 0;
}

int main()
{
	Empty = CreateSemaphore(NULL, 10, 10, NULL);//初始化Empty,初始值设为10,最大值为10
	Full = CreateSemaphore(NULL, 0, 10, NULL);//初始化Full,初始值设为0,最大值为10
	Mutex = CreateSemaphore(NULL, 1, 1, NULL);//初始化Mutex,初始值设为1
	

	//初始化缓冲区
	Buffer = new int[SizeOfBuffer];
	for (int i = 0; i < SizeOfBuffer; ++i)
	{
		Buffer[i] = 0;
	}
	
	//创建5个生产者线程和5个消费者线程
	for (int i = 0; i < 5; ++i)
	{
		CreateThread(NULL, 0, consume, NULL, 0, NULL);
		CreateThread(NULL, 0, produce, NULL, 0, NULL);
	}

	//让主线程等待一定的时间
	Sleep(1000);
}
  • OnLinux:
#include<pthread.h>
#include<semaphore.h>
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
#include<unistd.h>
#include<semaphore.h>

//缓冲区的大小,即有几个位置
#define SizeOfBuffer 5

sem_t Mutex;//互斥信号量
sem_t Full;//计数信号量,代表目前缓冲区内有几个位置已满
sem_t Empty;//计数信号量,代表目前缓冲区内有几个位置为空
int *Buffer;//缓冲区,1代表有产品,0代表没有产品

//显示缓冲区当前的存储情况
void showBuffer()
{
	printf("\t缓冲区:");
	for(int i = 0; i < SizeOfBuffer; ++i)
	{
		printf("%d", Buffer[i]);
		printf(" ");
	}
	printf("\n");
}

//生产者线程
void *produce(void *arg)
{
	do
	{
		sem_wait(&Empty);//等待缓冲区中有空位
		sem_wait(&Mutex);//对缓冲区形成互斥访问
	
		//寻找缓冲区里面的一个空位置
		for(int i = 0; i < SizeOfBuffer; ++i)
		{
			if(Buffer[i] == 0)
			{
				Buffer[i] = 1;//放置产品
				printf("%s%lu%s%d%s", "ID为", pthread_self(), "的生产者在缓冲区的第", i, "个位置放置了一个产品");
				showBuffer();
				break;
			}
		}

		sem_post(&Mutex);//释放互斥信号量Mutex
		sem_post(&Full);//使Full加1

	}while(true);
}

//消费者线程
void *consume(void *arg)
{
	do
	{
		sem_wait(&Full);//等待缓冲区中有空位
		sem_wait(&Mutex);//对缓冲区形成互斥访问
	
		//寻找缓冲区里面的一个产品
		for(int i = 0; i < SizeOfBuffer; ++i)
		{
			if(Buffer[i] == 1)
			{
				Buffer[i] = 0;//取走产品
				printf("%s%lu%s%d%s", "ID为", pthread_self(), "的消费者在缓冲区的第", i, "个位置取走了一个产品");
				showBuffer();
				break;
			}
		}

		sem_post(&Mutex);//释放互斥信号量Mutex
		sem_post(&Empty);//使Empty加1

	}while(true);
}

int main()
{
	//初始化Buffer数组
	Buffer = (int*)malloc(SizeOfBuffer*sizeof(int*));
	//Buffer = new int[SizeOfBuffer];
	for(int i = 0; i < SizeOfBuffer; ++i)
	{
		Buffer[i] = 0;
	}

	sem_init(&Mutex, 1, 1);//初始化Mutex为1
	sem_init(&Empty, 0, 10);//初始化Empty为10
	sem_init(&Full, 0, 0);//初始化Full为0

	pthread_t tid[10];
	pthread_attr_t attr;
	pthread_attr_init(&attr);

	//创建5个消费者线程和5个生产者线程
	for(int i = 0; i < SizeOfBuffer; ++i)
	{
		pthread_create(&tid[i], &attr, consume, NULL);
		pthread_create(&tid[i + 5], &attr, produce, NULL);
	}

	sleep(0.999);
	return 0;
}
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值