《操作系统真相还原》实验记录2.7——生产者与消费者问题

一、生产者与消费者问题简述

  1. 我们知道,在计算机中可以并行多个线程,当它们之间相互合作时,必然会存在共享资源的问题,这是通过“线程同步”来解决的,而诠释“线程同步”最典型的例子就是著名的“生产者与消费者问题”。
    1. “同步”:指多个线程相互协作,共同完成一个任务,属于线程间工作步调的相互制约。
    2. “互斥”:指多个线程“分时”访问共享资源。
  2. 生产者与消费者问题是描述多个线程协同工作的模型,而信号量解决了协同工作中的“同步”和“互斥”。
    生产者与消费者问题模型
  3. 生产者与消费者问题描述如下:
    1. 有一个或多个生产者、一个或多个消费者和一个固定大小的缓冲区,所有生产者和消费者共享这同一个缓冲区。生产者生产某种类型的数据,每次放一个到缓冲区中,消费者消费这种数据,每次从缓冲区中消费一个。同一时刻,缓冲区只能被一个生产者或消费者使用。当缓冲区已满时,生产者不能继续往缓冲区中添加数据,当缓冲区为空时,消费者不能在缓冲区中消费数据。
  4. 生产者与消费者问题强调的是:对于有限大小的公共缓冲区,如何同步生产者与消费者的运行,以达到对共享缓冲区的互斥访问,并且保证生产者不会过度生产,消费者不会过度消费,缓冲区不会被破坏。
    1. 对这种缓冲区的破坏,要么是对缓冲区访问溢出,也就是数据存取的地址超过了缓冲区的范围;要么是缓冲区中的数据被破坏,也就是新数据把尚未读取的老数据覆盖。

二、环形缓冲区的实现

  1. 缓冲区是多个线程共同使用的共享内存,因此需要保证各线程对缓冲区是互斥访问,并且不会对其过度使用,从而确保不会使缓冲区遭到破坏。也就是说,只要我们能够设计出合理的缓冲区操作方式,就能够解决生产者与消费者问题
  2. 环形缓冲区
    1. 环形缓冲区本质上依然是线性缓冲区,但其使用方式像环一样,没有固定的起始地址和终止地址,环内任何地址都可以作为起始和结束。
    2. 对于缓冲区的访问,我们提供两个指针,一个是头指针,用于往缓冲区中写数据,另一个是尾指针,用于从缓冲区中读数据。每次通过头指针往缓冲区中写入一个数据后,使头指针加 1 指向缓冲区中下一个可写入数据的地址,每次通过尾指针从缓冲区中读取一个数据后,使尾指针加1 指向缓冲区中下一个可读入数据的地址,也就是说,缓冲区相当于一个队列,数据在队列头被写入,在队尾处被读出。
    3. 用线性空间实现这种逻辑上的环形空间,只要我们控制好头指针和尾指针的位置就好了,无论它们怎样变化,始终让它们落在缓冲区空间之内,当指针到达缓冲区的上边界后,想办法将指针置为缓冲区的下边界(通常是对缓冲区大小取模),从而使头尾指针形成回路,逻辑上实现环形缓冲区。这两个指针相当于缓冲区的游标,在缓冲区空间内来回滑动。
    4. 我们的环形缓冲区是个线性队列,队列可以用线性数据结构来实现,比如数组和链表,为了简单,我们用数组来定义队列,实现环形缓冲区。
      环形缓冲区

三、添加键盘输入缓冲区

  1. 虽然我们的环形缓冲区支持多个生产者和消费者,但目前我们应用的场合非常简单,只是用在单一生产者和单一消费者的环境中,即生产者是键盘驱动,消费者是将来的shell
  2. 本节的任务是:将“在键盘驱动中处理的字符”存入环形缓冲区当中。

3.1 代码详情

ioqueue.h:头文件中定义了队列结构体

#ifndef __DEVICE_IOQUEUE_H
#define __DEVICE_IOQUEUE_H
#include "stdint.h"
#include "stdbool.h"
#include "thread.h"
#include "sync.h"#define bufsize 64struct ioqueue {/*the lock of buffer*/struct lock lock;/*if buffer is full, "producer" record which thread become sleeping.*/struct task_struct* producer;/*if buffer is empty, "consumer" record which thread become sleeping.*/struct task_struct* consumer;char buf[bufsize];  //bufferint32_t head;      //the head of queueint32_t tail;      //the tail of queue
};void ioqueue_init(struct ioqueue* ioq);
bool ioq_full(struct ioqueue* ioq);
char ioq_getchar(struct ioqueue* ioq);
void ioq_putchar(struct ioqueue* ioq, char byte);#endif

ioqueue.c:文件中定义了对环形键盘缓冲区的各类操作函数

#include "ioqueue.h"
#include "interrupt.h"
#include "global.h"
#include "debug.h"
#include "stdbool.h"void ioqueue_init(struct ioqueue* ioq) {lock_init(&ioq->lock);ioq->producer = ioq->consumer = NULL;ioq->head = ioq->tail = 0;
}static int32_t next_pos(int32_t pos) {  //next position in ioqueue.return (pos + 1) % bufsize;
}bool ioq_full(struct ioqueue* ioq) {ASSERT(intr_get_status() == INTR_OFF);  //ioqueue is public space, so we must make sure the interrupt is close when we operating.return next_pos(ioq->head) == ioq->tail;
}static bool ioq_empty(struct ioqueue* ioq) {ASSERT(intr_get_status() == INTR_OFF);return ioq->head == ioq->tail;
}/*make producer or consumer to waite*/
/*pay attention: "struct task_struct** waiter" is pointed to ioqueue->producer or ioqueue->consumer.*/
static void ioq_wait(struct task_struct** waiter) {ASSERT(*waiter == NULL && waiter != NULL);*waiter = running_thread();thread_block(TASK_BLOCKED);
}/*wakeup the waiter*/
static void wakeup(struct task_struct** waiter) {ASSERT(*waiter != NULL);thread_unblock(*waiter);*waiter = NULL;
}/*consumer get one character from ioqueue*/
char ioq_getchar(struct ioqueue* ioq) {ASSERT(intr_get_status() == INTR_OFF);while(ioq_empty(ioq)) {/*using lock will have effect in situation of more consumers and more producers, not only one consumer and producer.*/lock_acquire(&ioq->lock);ioq_wait(&ioq->consumer);lock_release(&ioq->lock);}char byte = ioq->buf[ioq->tail];ioq->tail = next_pos(ioq->tail);if(ioq->producer != NULL) {wakeup(&ioq->producer);}return byte;
}/*producer write one Byte to ioqueue*/
void ioq_putchar(struct ioqueue* ioq, char byte) {ASSERT(intr_get_status() == INTR_OFF);while(ioq_full(ioq)) {lock_acquire(&ioq->lock);ioq_wait(&ioq->producer);lock_release(&ioq->lock);}ioq->buf[ioq->head] = byte;ioq->head = next_pos(ioq->head);if(ioq->consumer != NULL) {wakeup(&ioq->consumer);}
}

keyboard.c:ioq_putchar()函数将处理后的键盘输入字符存入环形键盘缓冲区

//略
if(cur_char != 0) {  //we use keymap[0] to occupy place.if(ioq_full(&kbd_buf) != true) {put_char(cur_char);ioq_putchar(&kbd_buf, cur_char);}return;}
//略

四、生产者与消费者实例测试

  1. 实例测试设计内容大致如下:
    1. 一位生产者
      1. 由键盘中断驱动,且生产者位于主线程中。
      2. 键盘中断使得输入的字符存储到 “环形键盘缓冲区kbd_buf[]” 中。
      3. 当缓冲区存满后,再产生键盘中断将不做任何处理直接返回。
    2. 两位消费者
      1. 由时钟中断驱动,消费者位于新创建的两个独立线程中。
      2. 消费者访问 “环形键盘缓冲区kbd_buf[]” 。
        1. 如果 “环形键盘缓冲区kbd_buf[]” 不为空,则输出信息(包含该独立线程标识);
        2. 如果 “环形键盘缓冲区kbd_buf[]” 为空,则进入等待;
          1. 第一个发现 “环形键盘缓冲区kbd_buf[]” 为空的线程得到 “环形键盘缓冲区kbd_buf[]” 的锁,同时将自身PCB指针保存在waiter中,并将自身换下进程;
          2. 第二个发现 “环形键盘缓冲区kbd_buf[]” 为空的线程会由于无法得到 “环形键盘缓冲区kbd_buf[]” 的锁,因此只好将自己加入到 “环形键盘缓冲区kbd_buf[]” 的锁的阻塞队列中等待锁的持有者释放锁后唤醒自己并将自己换下处理器。
          3. 此时主线程上处理器运行while(1);,等待键盘输入,且由于两个独立线程此时都不在thread_ready_list中,因此主线程始终被换上换下处理器。
  2. 具体测试代码请读者自行编程实现练习,以串联和巩固自线程创建以来的知识点。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CTF-web第一步!

本次的题比较简单,适合我这种入门学者。CTF菜狗杯的web2 c0me_t0_s1gn。进入靶场打开F12会得到前一半。在控制台复制函数give_flag()会得到另一半。 这样就完成了。

P1183 多边形的面积-向量的用法

原题链接 https://www.luogu.com.cn/problem/P1183 题目描述 给出一个没有缺口的简单多边形,它的边是垂直或者水平的,要求计算多边形的面积。 xOy 的笛卡尔平面上,它所有的边都平行于两条坐标轴之一。然后按逆时针方向给出各顶点的坐标值。所有的坐标值都是整数,因此多边形…

蓄水池漂浮物识别摄像机

蓄水池漂浮物识别摄像机具有高效的图像识别功能。通过高清晰度的摄像头捕捉到蓄水池表面的图像,并通过人工智能技术进行快速准确的漂浮物识别。这种摄像机可以自动检测出池面上的漂浮物,并生成相应报警信息。该摄像机支持多种智能算法分析,并通过智能算法对数据进行综合分析…

行为智能识别摄像机

行为智能识别摄像机通过结合人工智能技术和监控技术,实现了对各种行为动作的自动识别和分析,在提高安全性、减少事故发生率方面具有重要意义。随着科技的不断进步和应用范围的扩大,相信这种先进设备将会在更多领域得到广泛应用。行为智能识别摄像机是一种结合了人工智能技术…

AI人数智能统计监测摄像头

AI人数智能统计监测摄像头具有高效的图像识别功能。通过先进的图像处理算法,可以快速准确地识别出场景中的人群,并进行实时统计。无论是密集的人流场所还是较为稀疏的区域,这种摄像头都能够精准地进行人数统计,为管理者提供重要参考信息。AI人数智能统计监测摄像头具有实时…

Android 中的卡顿丢帧原因概述 - 方法论

Android 手机使用中的卡顿问题 , 一般来说手机厂商和 App 开发商都会非常重视 , 所以不管是手机厂商还是 App 开发者 , 都会对卡顿问题非常重视 , 内部一般也会有专门的基础组或者优化组来进行优化 . 目前市面上有一些非常棒的第三方性能监控工具 , 比如腾讯的 Matrix ; 手机厂…

JavaScript的常用库 —— jQuery

利用JS去操控HTML和CSS,常用库之jQuery ฅʕ•̫͡•ʔฅjQuery用来更加方便地去控制前端的HTML标签和CSS属性。使用方式:1. 直接在<head>元素中添加: <script src="https://cdn.acwing.com/static/jquery/js/jquery-3.3.1.min.js"></script> 2…

2 FreeRTOS移植

2 FreeRTOS移植 2.1 源码基本认识获取源码。官网地址:FreeRTOS™ - FreeRTOS™ 源码内文件结构:1) FreeRTOS文件夹结构2) Source文件夹结构3) portable文件夹结构 portable文件夹里面有编译器、内核环境可以选择。其中keil是我们使用的编译器类型,但Keil文件夹里只有一个…

22蓝帽初赛

参考wp:http://mp.weixin.qq.com/s?__biz=Mzk0MTQzNjIyNg==&mid=2247487196&idx=1&sn=48094c5a78749b45c3598ed51a5df0e3&chksm=c3901b8acd56bc2d1d06b323e9d1e86b90048a35dc3b3301b60bb1f488a764f0b52ebc490113&mpshare=1&scene=23&srcid=01218…

【Azure APIM】APIM服务配置网络之后出现3443端口不通,Management Endpoint不健康状态

如果没有关联的网络安全组,则阻止所有网络流量通过子网和网络接口。问题描述 APIM服务在配置网络之后,查看网络状态发现Management Endpoint是不健康状态, 提示无法连接到3443端口。错误消息: Failed to connect to management endpoint at xxxxxxxx.management.azure-api.…

【模拟电子技术】11-放大电路的性能指标

【模拟电子技术】11-放大电路的性能指标通过输入,输出侧的各一个电容来到输入纯交流,输出纯交流Ui变化引起UBE变化,UBE变化引起IB变化,IB变化引起IC变化,IC变化引起UCE变化,UCE变化引起Uo变化关于输入,输出等效电路的问题: 输入电阻Ri越大越好还是越小越好?当然是越大…

解决 WebSocket 连接断开问题:前端心跳机制的实现与优化

在开发过程中,我们经常会遇到需要实时通信的场景,而 WebSocket 是一种非常合适的技术选择。然而,在实际使用 WebSocket 的过程中,我们可能会遇到连接频繁断开的问题。最近,我在一个项目中就遇到了这样的问题,经过一番探索和优化,终于找到了解决方案,现在与大家分享一下…