操作系统实验二 进程(线程)同步

前言

实验二相比实验一难度有所提升,首先得先掌握好相应的理论知识(读者-写者问题和消费者-生产者问题),才能在实验中得心应手。任务二的代码编写可以借鉴源码,所以我们要先读懂源码。

1.实验目的

掌握Linux环境下,进程(线程)同步以及临界资源的互斥访问方法。

2.实验要求

熟悉Linux操作系统线程创建、利用信号量机制进行同步和互斥访问临界资源。

3.实验内容

本实验包含生产者和消费者之间的同步以及读者和写者之间的同步两部分,请实现以下功能:

  1. 多个生产者多个消费者线程。我们使用含有n个元素的数组表示n个缓冲区生产者线程每间隔一段时间例如一秒钟生产一个产品放入缓冲区中。消费者线程等待生产者线程放入产品后在缓冲区中取出产品。请使用信号量机制实现生产者线程和消费者线程之间的同步。(生产者消费者)
  2. 有一个或多个写者线程多个读者线程。读者和写者之间共享一缓冲区(缓冲区可用字符数组表示),写者获得缓冲区访问权限后往缓冲区写入内容。读者获得缓冲区访问权限后从缓冲区中读出内容。当有一个读者获得缓冲区访问权限后,其它读者线程可以访问该缓冲区。而读者与写者之间需要互斥的访问缓冲区。

4.实验的内容与过程

阅读代码

在实验前,我们先把代码理清楚。

mainProg.c

解析:在mainProg.c中,我们根据定义的生产者和消费者数量生成了相应的空间大小。紧接着,我们又定义了一个生产者消费者管理者,这个的主要作用是管理临界资源。(后面我会分析)接着就是两个for循环,一个for循环是调用两个生产者执行生产任务,一个for循环是调用消费者执行消费任务。在这两个循环中,我们都用到了pthread_create函数,这个函数主要作用是创建以第三个参数start_routine为入口函数的线程。传入的四个参数分别为:线程的指针、线程属性、线程运行函数起始地址、传入到运行函数的参数。也就是生产者的for循环中产生的子线程完成producerThread函数中的任务,消费者的for循环中产生的子线程完成consumerThread函数中的任务。

producerThread在Producer.c文件中

解析:ProducerThread函数中,先获取了我们的临界资源,然后随机生成种子数,为我们后面生成随机数做准备。接着在while循环中生产产品,直到exit_flag变成1,我们才不继续生产产品。生产产品我们主要调用了自定义的generateProduct函数。在generateProduct函数中,我们先将三个信号赋值上相应的数值,然后随机生成一个随机数,也就是我们的产品。接着调用semwait (empty)和semWait(mutex)。我们应该注意把semwait (empty)放在 semWait(mutex)前面,否则会产生死等现象。接着现有产品数量+1,再把产品放到缓存区中,并输出相应的信息。最后我们在线程结束后,调用semsignal释放信号。

consumerThread在Consumer.c文件中

解析:consumerThread函数跟上面的ProducerThread函数类似,并且图中也有注释,我就不详细介绍了。接着就是getProduct函数。在函数中,先将三个信号赋值上相应的数值,然后调用semwait (full)和semWait(mutex)并取出一个商品,然后产品数量减1并打印出相应的信息。最后我们在线程结束后,调用semsignal释放信号。

ProducerConsumerUtilities.c文件:

        

 

 解析:在这个文件中,通过截屏中的注释,我们发现这个文件中无非是定义了一堆初始化和自定义的创建线程的方法,接着就是wait、signal、摧毁操作,方便我们在其他地方调用这些方法。

ProducerConsumerUtilities.h文件:

解析:在这个文件中定义了生产者消费者管理者这个结构体,其内容主要有记录mutex、full、empty这三个信号,生产者的偏移量,消费者的偏移量,现有产品的数量,缓存区,缓存区大小以及判断是否结束的变量。之所以定义这个结构体,是因为这些资源都是公共资源,即临界资源,是各个线程一起使用的。然后就是线程结构体和一些方法的声明。

至此,我们的代码关键部分基本已经分析完毕了,对实验的原理已经有了一定的了解,接下来就可以开始实验了。

任务1.写出生产者消费者程序对应Makefile

Makefile文件

 解析:根据我们的现有文件生成对应的.o文件,即第三行的文件名。(注意第二行的CFLAGS变量中加入-lpthread)

运行结果:

 可以看到,我们的生产者消费者程序已经可以运行了,输入q即可停止。(因为mainProg中定义了我们输入q的话exit_flag会变成1)

至此,我们的任务一就完成了,接下来就是任务二。

任务2:读者写者程序

mainProg.c

 解析:定义了2个写者和6个读者后(个数自行定义),给相应的线程分配足够的空间,并生成一个读者写者的管理者,用来管理临界资源。接着就是写者线程和读者线程的初始化,和生产者消费者的代码大同小异,主要还得看下面部分的代码。

ReaderWriterUtilities.h文件:

解析:这个头文件主要定义了读者写者程序的管理者的一些方法和结构。首先是读者写者管理者的结构体。这个结构体里面有信号量mutex和rmutex(mutex信号用来实现互斥访问缓冲区,rmutex信号主要用来实现互斥访问readcount变量),还有readcount变量用来存放读者的数量,buffer数组用来存放字符,类似于写者的作品。还有buffersize用来记录缓存区大小,exit_flag来确定线程的状态。接着是线程结构体还有一些方法的定义。由于这些内容跟生产者消费者的代码部分大同小异,就不再赘述。

ReaderWriterUtilities.c文件: 

 

 解析:可以看到,在这个文件中,主要是定义了一堆初始化和自定义的创建线程的方法,接着就是wait、signal、摧毁操作,跟任务一代码类似并且截屏中已有注释,不赘述。接下来就是关键的读者部分和写者部分代码了。

Reader.c文件:

 分析:先看看在这个文件中定义的read_data方法。这个方法主要先获取mutex信号、rmutex信号和readcount的值。然后定义了buf数组,用来存放作品的内容。接着,先使用wait语句来保证线程可以互斥访问readcount变量。如果readcount的值为0则说明当前没有读者在阅读作品,就先互斥访问临界资源来保证读者写者不会同时对临界资源进行操作(类似于第一个读的人要先去把作品拿过来),接着就是将readcount数量加1并且释放掉rmutex信号。当读者阅读完后,我们先互斥访问readcount,将readcount先减1,如果readcount的值为0,说明现在是最后一个读者,要释放掉mutex信号(类似于最后一个读完的人要归还作品),接着再释放掉rmutex信号。文件中定义的另一个方法readerThread则是实现读者线程的运行,如果exit_flag为0,则一直进行读操作。

Writer.c文件:

分析:先看这个文件中定义的write_data方法。在方法中,先获取mutex信号的值,然后调用wait方法,保证临界资源的互斥访问。接着,就在循环中往缓冲区写入字符,先随机获得一个偏移量(0到26),然后这个偏移量加上字符‘a’的ASCII码值就能得到‘a’到‘z’的一个字符。当我们把缓冲区填完了,就实现了一个写者的书写任务,接着就输出相应的信息并释放掉mutex信号。另一个方法writerThread方法则是实现写者线程的运行,当exit_flag的值为0时就一直执行写操作。

makefile文件:

 解析:生成文件为ReaderWriterUtilities.o、Writer.o、Reader.o和mainProg.o,在第三行填入即可,跟任务一操作类似。

运行结果:

 可以看到,我们的读者写者程序已经可以运行了,输入q即可停止。(因为mainProg中定义了我们输入q的话exit_flag会变成1)。通过观察,我们发现读者阅读的内容总跟最近一个写者书写的内容一样,说明代码是没问题的。

至此,我们的任务二也完成了。

要注意的是,writer.h和reader,h这两个头文件我没截屏出来,自己编写就行了,很简单的,仿照任务一的代码即可。

至此,我们的实验大功告成!如果大家有什么想法,可以在评论区提出,一起交流。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/20532.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

浅谈Redis

一、Redis的简介 1.开源免费的缓存中间件,性能高,读可达110000次/s,写可达81000次/s。 2.redis的单线程讨论: V4.0之前:是单线程的,所有任务处理都在一个线程内完成. V4.0:引入多线程,异步线程用于处理一些耗…

Linux三种网络模式 | 仅主机、桥接、NAT

💗wei_shuo的个人主页 💫wei_shuo的学习社区 🌐Hello World ! Linux三种网络模式 仅主机模式:虚拟机只能访问物理机,不能上网 桥接模式:虚拟机和物理机连接同一网络,虚拟机和物理机…

Docker的四种网络模式

1.Host 模式 通常来讲,启动新的Docker容器,都会分配独立的Network Namespace隔离子系统,如果在运行是指定为host模式,那么Docker容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace子…

山东专升本计算机第六章-数据库技术

数据库技术 SQL数据库与NOSQL数据库的区别 数据库管理系统 考点 6 数据库管理系统的组成和功能 组成 • 模式翻译 • 应用程序的翻译 • 交互式查询 • 数据的组织和存取 • 事务运行管理 • 数据库的维护 功能 • 数据定义功能 • 数据存取功能 • 数据库运行管理…

【图像基础知识】常见图像格式

文章目录 1 简介2 RGB3 BGR4 YUV4.1 YUV常见格式4.2 YUV420详解4.3 NV12 5 Gray6 图像格式之间的转换7 参考链接 原文来自于地平线开发者社区,未来会持续发布深度学习、板端部署的相关优质文章与视频,如果文章对您有帮助,麻烦给点个赞&#x…

Sentinel : 服务容错(降级熔断、流量整形)

什么是服务雪崩? 服务雪崩效应是一种因“服务提供者的不可用”(原因)导致“服务调用者不可用”(结果),并将不可用逐渐放大的现象。 我来用一个模拟场景带你感受一下服务雪崩的厉害之处。假设我有一个微服…

20 散列表的查找

散列表的查找 简介:散列表(也成哈希表)是一种高效的数据结构,他可以在平均复杂度为O(1)的情况下实现查找、插入和删除操作。 哈希表的基本思想是根据关键字的值来计算其应存储的位置。这个计算过程就是通过哈希函数来实现的。 根…

深度学习-第T5周——运动鞋品牌识别

深度学习-第T5周——运动鞋品牌识别 深度学习-第T5周——运动鞋品牌识别一、前言二、我的环境三、前期工作1、导入数据集2、查看图片数目3、查看数据 四、数据预处理1、 加载数据1、设置图片格式2、划分训练集3、划分验证集4、查看标签 2、数据可视化3、检查数据4、配置数据集 …

西瓜书读书笔记整理(三)—— 第二章 模型评估与选择

第二章 模型评估与选择 第 2 章 模型评估与选择2.1 经验误差与过拟合1. 错误率 / 精度 / 误差2. 训练误差 / 经验误差 / 泛化误差3. 过拟合 / 欠拟合4. 学习能力5. 模型选择 2.2 评估方法1. 评估方法概述2. 留出法3. 交叉验证法4. 自助法5. 调参 / 最终模型 2.3 性能度量1. 回归…

Ada语言学习(1)Basic Knowledge

文章目录 说在前头命名注释数字变量变量类型signed integersEnumerationsFloating Points 类型重用(继承)类型转换 运算符属性(Attributes)练习 说在前头 本系列教程将会通过提问的方式来完成整个学习过程,因为当你能…

86盒IP对讲一键报警器

86盒IP对讲一键报警器 86盒IP对讲一键报警器:革命性保障生命安全的利器! 随着科技的飞速发展,我们的生活变得越来越方便和智能化。而86盒IP对讲一键报警器更是在这种背景下应运而生。这款产品不仅无缝对接各种手机APP,也可以在智…

RabbitMQ --- 惰性队列、MQ集群

一、惰性队列 1.1、消息堆积问题 当生产者发送消息的速度超过了消费者处理消息的速度,就会导致队列中的消息堆积,直到队列存储消息达到上限。之后发送的消息就会成为死信,可能会被丢弃,这就是消息堆积问题。 解决消息堆积有三种…

互联网大厂测开面试记,二面被按地上血虐,所幸Offer已到手

在互联网做了几年之后,去大厂“镀镀金”是大部分人的首选。大厂不仅待遇高、福利好,更重要的是,它是对你专业能力的背书,大厂工作背景多少会给你的简历增加几分竞争力。 如何备战面试的? 第一步:准备简历 …

vue3回到上一个路由页面

学习链接 Vue Router获取当前页面由哪个路由跳转 在Vue3的setup中如何使用this beforeRouteEnter 在这个路由方法中不能访问到组件实例this,但是可以使用next里面的vm访问到组件实例,并通过vm.$data获取组件实例上的data数据getCurrentInstance 是vue3提…

51单片机也可以移植RTOS

说起RTOS移植,我们首先会想到32位单片机。 那么51单片机可以移植RTOS吗? 我的答案是,只要资源够用(ROM空间、RAM空间),可以移植。 前提是你对RTOS的实现原理非常清楚,并且可以自己完成移植工作…

数据可视化大屏的页面布局以及自适应

在做数据可视化大屏之前,我们需要考虑到页面的布局问题以及页面缩放自适应问题,下面分别就这两个方面讲解。 页面布局 类似这种页面区块的明显划分,常用的布局方式有两种: 1、flex布局 2、grid布局 grid布局 grid布局可以按区块…

深度学习用于医学预后-第二课第四周5-10节-为个体患者制定风险评估模型

文章目录 相对风险按风险对患者进行排序个体与基线风险吸烟者与不吸烟者年龄对风险的影响 在本课中,您将学习 Cox 比例风险模型(Cox Proportional Hazards Model)。您将了解 Cox 模型如何考虑患者变量来比较不同患者的风险,使用他们的患者概况。 但到目前…

mysql增量备份

目录 一、修改配置文件,开启增量备份功能 (1)查看是否已经开启了 (2)修改配置文件开启 (3)增量记录文件 二、还原增量备份 (1)修改了数据 (2&#xff…

nginx keepalive 高可用原理和实操

文章目录 前言一、nginxkeepalive搭建高可用服务方案?二、方案解析1.keepalive是什么2.nginx是什么 三、keepalive与nginx环境安装四、高可用配置实例总结 前言 一、nginxkeepalive搭建高可用服务方案? 使用nginx-keepalived双机热备机制,vi…

【云计算•云原生】4.云原生之什么是Kubernetes

文章目录 Kubernetes概念Kubernetes核心概念集群podConfigMap Kubernetes架构master节点的组件worker节点组件 Kubernetes网络架构内部网络外部网络 k8s各端口含义 Kubernetes概念 K8S就是Kubernetes,Kubernetes首字母为K,末尾为s,中间一共有…