♻️ 资源

大小: 5.62MB

➡️ 资源下载:https://download.csdn.net/download/s1t16/87450290

生产者消费者问题

实验目的

使用进程模拟消费者生产者进程,掌握进程之间的通信方法,区分在不同操作系统下进程通信的不同之处,并熟练使用信号量的创建删除等一系列操作

实验内容

一个大小为 3 的缓冲区,初始为空个生产者

随机等待一段时间,往缓冲区添加数据,若缓冲区已满,等待消费者取走数据后再添加重复 6 次

随机等待一段时间,从缓冲区读取数据若缓冲区为空,等待生产者添加数据后再读取

重复 4 次说明:

显示每次添加和读取数据的时间及缓冲区里的数据(指缓冲区里的具体内容)

生产者和消费者用进程模拟(不能用线程)

Linux/Window 下都需要做

一、实验环境

Windows:

Windows10

处理器 Inter core i5-8265u@1.60GHzmailto:i5-8265u@1.60GHz 内存 8.00GB

系统类型 64 位操作系统,基于 x64 的处理器

Linux:

虚拟机软件:VMware workstation 15

虚拟机操作系统:Ubuntu20.04 虚拟机内存:4GB

二、虚拟机硬盘容量:60GB 四、 程序设计与实现

在 Windows 下的实现:

要实现生产者消费者模型的模拟,首先要创建文件缓冲区,在 Windows 中,通过文件映射的方式来实现创建缓冲区。

定义缓冲区结构

定义共享内存的结构

2.1 然后使用 GreateFileMapping()创建文件映射对象,API 的解释如下:

返回一个文件对象句柄,通过文件对象句柄对该映射对象进行初始化等一系列操作

在主函数中,根据不同的进程号,判断是消费者进程还是生成者进程,并采取不同的操作

在主进程中,先创建映射视图和信号量并初始化

MapViewOfFile () 用于将视图映射到创建的共享文件,API 的解释如下:

参数:

hFileMappingObject 文件映射对象的句柄

dwDesiredAccess 文件映射对象的访问类型,取值为

FILE_MAP_ALL_ACCESS 表示对映射文件对象有读写的权利 dwFileOffsetHigh 文件高阶偏移量

dwFileOffsetLow 文件低阶偏移量

dwNumberOfBytesToMap 映射到视图的文件映射的字节数

CreateSemaphore() 此 API 用于创建或打开一个信号量,具体解释如下:

参数:

lpSemaphoreAttributes:为信号量的属性,一般可以设置为 NULL

lInitialCount :信号量初始值,必须大于等于 0 ,而且小于等于 lpMaximumCount,如果 lInitialCount 的初始值为 0,则该信号量默认为 unsignal 状态,如果 lInitialCount 的初始值大于 0,则该信号量默认为 signal 状态

lMaximumCount: 此值为设置信号量的最大值,必须大于 0

lpName:信号量的名字,长度不能超出 MAX_PATH ,可设置为 NULL,表示无名的信号量。当 lpName 不为空时,可创建有名的信号量,若当前信号量名与已存在的信号量的名字相同时,则该函数表示打开该信号量,这时参数 lInitialCount 和 lMaximumCount 将被忽略。

在 main 函数的主线程中,创建缓冲区和信号量并初始化

在生产者进程中,首先使用 OpenFileMapping(),此 API 的作用是打开一个命

名的文件映射对象。该 API 的详细解释如下:

OpenFileMapping (
    ByVal dwDesiredAccess As Long,  	//带有前缀 FILE_MAP_???的常数
    ByVal bInheritHandle As Long,      //如这个函数返回的句柄能由当前进程

启动的新进程继承,则这个参数为

TRUE
ByVal lpName As String  	 	 	//指定要打开的文件映射对象名称
)

打开共享内存区后,再使用 MapViewOfFile()将共享内存区映射到当前进程的地址空间。MapViewOfFile()的作用是将一个文件映射对象映射到当前的应用程序的地址空间,该 API 的解释如下:

参数:

hFileMappingObject 文件映射对象的句柄

dwDesiredAccess 对文件映射对象的访问类型

dwFileOffsetHigh 视图开始处文件的高阶偏移量

dwFileOffsetLow 视图开始处文件的低阶偏移量

dwNumberOfBytesToMap 映射到视图的字节数。如果此参数为 0(零),

则映射将从指定的偏移量扩展到文件映射的末尾。

相关部分代码如下:

然后使用 OpenSemaphore()打开信号量并对信号量进行初始化

在每一次写操作先调用生成随机数的函数,返回的随机数作为生产者写入缓冲区的数据,同时也作为进程运行前随机睡眠的时间。

使用 WaitforSingleObject 来实现信号量的 p 操作

相对应的,最后通过 ReleaseSemaphore()来实现对信号量的 v 操作然后关闭视图,断开与共享内存之间的连接

在消费者进程中,大致的步骤与生产者进程一样,要注意的是对信号量的操作是不同的

实验运行结果:

  • 实验结果:
  • 成功在 Windows 下使用进程模拟了生产者消费者模型
  • 在 Linux 下的实现:
  • 首先定义缓冲区结构

然后使用 semget()创建信号量集,其中包含 SEM_FULL,SEM_EMPTY,

2.1.1 SEM_MUTEX 三个信号量,并使用 semtcl()对信号量进行初始化

接着使用 shmget()创建共享内存区,改 API 的具体解释如下:

key 非 0 整数,为共享内存段命名 size 以字节为单位指定需要共享的内存容量

shmflg 权限标志,它的作用与 open 函数的 mode 参数一样,如果要想在 key 标识的共享内存不存在时,创建它的话,可以与 IPC_CREAT 做或操作。

相关代码如图:

再使用 shmat()函数将创建的共享内存区连接到父进程的地址空间上,然后初始化缓冲区。shmat()API 的解释如下:

  • 该函数作用是用来启动对共享内存的访问,并把共享内存连接到当前进程的地址空间参数:
  • shm_id 由 shmget()函数返回的共享内存标识。
  • shm_addr 指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。
  • shm_flg 一组标志位,通常为 0。
  • 部分相关代码如图:

信号量和缓冲区创建到这里就已经完成了,接下来是消费者和生产者进程。

首先源码中的 p 操作和 v 操作部分代码如图:

在生产者进程中,首先使用 shmat()函数将共享内存连接到生产者进程的地址空间上,然后在依次对 SEM_EMPTY 和 SEM_MUTEX 进行 p 操作,随机睡眠一段时间后向缓冲区写入数据

完成操作后对 SEM_FULL 和 SEM_MUTEX 进行 v 操作,并断开共享内存和进程之间的连接

消费者进程与生产者进程基本的操作一致,也是先将共享内存和进程的地址空间进行连接,不过这里是对 SEM_FULL 和 SEM_EMPTY 先后进行 p 操作,随机睡眠一段时间后从缓冲区取出数据,最后再对 SEM_EMPTY 和 SEM_MUTEX 进行 v 操作,然后断开共享内存的连接

最后,在所有的子进程运行结束后,父进程要删除信号量集和共享内存

实验运行结果如图:

实验结果:

在 Linux 下成功用线程模拟了生产者消费者模型

三、实验收获与体会

通过本次实验,我对 Windows 和 Linux 下的进程通信有了更加深入的认识,

同时也熟悉了相关 API,共享内存区和信号量的使用。而且在操作实践中,对消费者生产者这个经典模型有了更加全面的认识,掌握它是如何在不同的操作系统中实际运行的,对课程的理论知识的认识有了更加坚实的实践基础。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐