实验18.环形缓冲区

已完成实验

已完成实验链接

简介

实验 18. 环形缓冲区

总结

    1.生产者消费者问题

主要代码

引导

省略

内核

ioqueue.h

// 文件: ioqueue.h
// 时间: 2024-07-30
// 来自: ccj
// 描述: 环形队列数据结构

#ifndef __DEVICE_IOQUEUE_H
#define __DEVICE_IOQUEUE_H
#include "stdint.h"
#include "thread.h"
#include "sync.h"

#define bufsize 64

/// @brief 环形队列, 生产者消费者问题
struct ioqueue {
    struct lock lock;

    struct task_struct* producer;  // 阻塞的生产者pcb
    struct task_struct* consumer;  // 阻塞的消费者pcb

    char buf[bufsize];  // 缓冲区大小
    int32_t head;       // 队首,数据往队首处写入
    int32_t tail;       // 队尾,数据从队尾处读出
};

void ioqueue_init(struct ioqueue* ioq);
bool ioq_full(struct ioqueue* ioq);
bool ioq_empty(struct ioqueue* ioq);
char ioq_getchar(struct ioqueue* ioq);
void ioq_putchar(struct ioqueue* ioq, char byte);
uint32_t ioq_length(struct ioqueue* ioq);

#endif

ioqueue.c

// 文件: ioqueue.c
// 时间: 2024-07-30
// 来自: ccj
// 描述: 生产者消费者环形缓冲区,锁用于多生产者消费者。缓冲区含有要环形的生产者和消费者

#include "ioqueue.h"
#include "interrupt.h"
#include "global.h"
#include "debug.h"

/// @brief 初始化io队列ioq
/// @param ioq
void ioqueue_init(struct ioqueue* ioq) {
    lock_init(&ioq->lock);                 // 初始化io队列的锁
    ioq->producer = ioq->consumer = NULL;  // 生产者和消费者置空
    ioq->head = ioq->tail = 0;  // 队列的首尾指针指向缓冲区数组第0个位置
}

/// @brief 返回pos在缓冲区中的下一个位置值
/// @param pos
/// @return
static int32_t next_pos(int32_t pos) { return (pos + 1) % bufsize; }

/// @brief 判断队列是否已满
/// @param ioq
/// @return
bool ioq_full(struct ioqueue* ioq) {
    ASSERT(intr_get_status() == INTR_OFF);
    return next_pos(ioq->head) == ioq->tail;
}

/// @brief 判断队列是否已空
/// @param ioq
/// @return
bool ioq_empty(struct ioqueue* ioq) {
    ASSERT(intr_get_status() == INTR_OFF);
    return ioq->head == ioq->tail;
}

/// @brief 阻塞当前线程,并把pcb指针给到消费者或生产者
/// @param waiter
static void ioq_wait(struct task_struct** waiter) {
    ASSERT(*waiter == NULL && waiter != NULL);
    *waiter = running_thread();
    thread_block(TASK_BLOCKED);
}

/// @brief 唤醒waiter
/// @param waiter
static void wakeup(struct task_struct** waiter) {
    ASSERT(*waiter != NULL);
    thread_unblock(*waiter);
    *waiter = NULL;
}

/// @brief 消费者从ioq队列中获取一个字符
/// @param ioq 环形队列
/// @return
char ioq_getchar(struct ioqueue* ioq) {
    ASSERT(intr_get_status() == INTR_OFF);

    // 如果队列为空,1.阻塞自己,并把pcb给到环形队列的消费者
    while (ioq_empty(ioq)) {
        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;
}

/// @brief 生产者往ioq队列中写入一个字符byte
/// @param ioq
/// @param byte
void ioq_putchar(struct ioqueue* ioq, char byte) {
    ASSERT(intr_get_status() == INTR_OFF);

    // 如果缓冲区满了,阻塞自己,并把pcb给到环形队列的生产者
    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); }
}

/// @brief 返回环形缓冲区中的数据长度
/// @param ioq
/// @return
uint32_t ioq_length(struct ioqueue* ioq) {
    uint32_t len = 0;
    if (ioq->head >= ioq->tail) {
        len = ioq->head - ioq->tail;
    } else {
        len = bufsize - (ioq->tail - ioq->head);
    }
    return len;
}

keyboard.c

// 文件: keyboard.c
// 时间: 2024-07-30
// 来自: ccj
// 描述: 键盘初始化。注册键盘中断,读取端口缓冲区,键码转换打印

#include "keyboard.h"
#include "print.h"
#include "interrupt.h"
#include "io.h"
#include "global.h"

// 定义键盘缓冲区
struct ioqueue kbd_buf;

...
        // 0不处理
        if (cur_char) {
            put_char(cur_char);
            ioq_putchar(&kbd_buf, cur_char);
            return;
        }

init.c

// 文件: init.c
// 时间: 2024-07-22
// 来自: ccj
// 描述: 内核所有初始化操作

#include "init.h"
#include "print.h"
#include "interrupt.h"
#include "timer.h"
#include "memory.h"
#include "thread.h"
#include "keyboard.h"

/// @brief 内核所有初始化
void init_all() {
    put_str("init all\n");

    idt_init();       // 初始化中断
    timer_init();     // 调快时钟、注册时钟中断来调度线程
    mem_init();       // 初始化内存管理系统
    thread_init();    // 初始化线程
    console_init();   // 控制台初始化最好放在开中断之前
    keyboard_init();  // 键盘初始化
}

main.c

// 文件: main.c
// 时间: 2024-07-19
// 来自: ccj
// 描述: 内核从此处开始

#include "print.h"
#include "init.h"
#include "thread.h"
#include "interrupt.h"
#include "console.h"

void k_thread(void*);

int main(void) {
    put_str("I am kernel\n");

    init_all();

    intr_enable();  // 打开中断,使时钟中断起作用
    while (1) {};
    return 0;
}

void k_thread(void* arg) {
    char* para = arg;
    while (1) { console_put_str(para); }
}

在这里插入图片描述

  • 10
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值