已完成实验
简介
实验 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); }
}