Ver código fonte

输出输出系统

simon 1 ano atrás
pai
commit
ec2d1ad596
7 arquivos alterados com 195 adições e 17 exclusões
  1. 103 0
      device/ioqueue.c
  2. 51 0
      device/ioqueue.h
  3. 11 7
      device/keyboard.c
  4. 3 0
      device/keyboard.h
  5. 2 1
      kernel/interrupt.c
  6. 21 8
      kernel/main.c
  7. 4 1
      makefile

+ 103 - 0
device/ioqueue.c

@@ -0,0 +1,103 @@
+#include "ioqueue.h"
+#include "../kernel/debug.h"
+#include "../kernel/interrupt.h"
+
+/// @brief 返回 pos 在缓冲区的下一个位置
+static int32_t next_pos(int32_t pos);
+
+/// @brief 初始化 ioqueue 队列
+void ioqueue_init(struct ioqueue *ioq)
+{
+    lock_init(&ioq->lock);
+    ioq->producer = NULL;
+    ioq->consumer = NULL;
+    ioq->head = 0;
+    ioq->tail = 0;
+}
+/// @brief 返回 pos 在缓冲区的下一个位置
+static int32_t next_pos(int32_t pos)
+{
+    return (pos + 1) % BUF_SIZE;
+}
+
+/// @brief 判断队列是否已满
+/// @param ioq
+/// @return TRUE OR FALSE
+bool ioq_full(struct ioqueue *ioq)
+{
+    ASSERT(intr_get_status() == INTR_OFF);
+    return next_pos(ioq->head) == ioq->tail;
+}
+/// @brief 判断队列是否为空
+/// @param ioq
+/// @return TRUE OR FALSE
+bool ioq_empty(struct ioqueue *ioq)
+{
+    ASSERT(intr_get_status() == INTR_OFF);
+    return ioq->head == ioq->tail;
+}
+/// @brief 使生产者或消费者在此缓冲区上等待
+/// @param waiter
+static void ioq_wait(struct task_struct **waiter)
+{
+    ASSERT(*waiter == NULL && waiter != NULL);
+    *waiter = running_thread();
+    thread_block(TASK_BLOCKED); // 阻塞自己
+}
+/// @brief 唤醒 waiter
+static void wakeup(struct task_struct **waiter)
+{
+    ASSERT(waiter != NULL);
+    thread_unblock(*waiter);
+    *waiter = NULL;
+}
+/// @brief 消费者从 ioq 队列中获取一个字符
+char ioq_getchar(struct ioqueue *ioq)
+{
+    ASSERT(intr_get_status() == INTR_OFF);
+    // 如果 ioq 为空, 使消费者在此缓冲区上等待
+    // 把 ioq->consumer 记为当前线程自己,
+    // 目的是让生产者生产后唤醒
+    while (ioq_empty(ioq))
+    {
+        lock_acquire(&ioq->lock);
+        ioq_wait(&ioq->consumer);
+        lock_release(&ioq->lock);
+    }
+
+    // 获取字符并从 ioq 队列中删除
+    char ret = ioq->buf[ioq->tail];
+    ioq->tail = next_pos(ioq->tail);
+
+    // 唤醒生产者
+    if (ioq->producer != NULL)
+    {
+        wakeup(&ioq->producer);
+    }
+
+    return ret;
+}
+/// @brief 生产者往 ioq 队列中添加一个字符
+void ioq_putchar(struct ioqueue *ioq, char byte)
+{
+    ASSERT(intr_get_status() == INTR_OFF);
+    // 如果 ioq 已满, 使生产者在此缓冲区上等待
+    // 把 ioq->producer 记为当前线程自己,
+    // 目的是让消费者消费后唤醒
+    while (ioq_full(ioq))
+    {
+        lock_acquire(&ioq->lock);
+        ioq_wait(&ioq->producer);
+        lock_release(&ioq->lock);
+    }
+
+    // 往 ioq 队列中添加字符并使消费者在此缓冲区上等待
+    ioq->buf[ioq->head] = byte;
+    ioq->head = next_pos(ioq->head);
+
+    // 唤醒消费者
+    if (ioq->consumer != NULL)
+    {
+        wakeup(&ioq->consumer);
+    }
+}

+ 51 - 0
device/ioqueue.h

@@ -0,0 +1,51 @@
+/**
+ * @file ioqueue.h
+ * @author simon (ynwdlxm@163.com)
+ * @brief 环形缓冲区队列
+ * @version 0.1
+ * @date 2025-01-11
+ *
+ * @copyright Copyright (c) 2025
+ *
+ */
+#ifndef __DEVICE_IOQUEUE_H
+#define __DEVICE_IOQUEUE_H
+#include "../lib/stdint.h"
+#include "../thread/thread.h"
+#include "../thread/sync.h"
+
+#define BUF_SIZE 64
+
+/* 环形队列 */
+struct ioqueue
+{
+    struct lock lock;             // 锁,用于同步
+    struct task_struct *producer; // 记录哪个生产者在此缓冲区睡眠
+    struct task_struct *consumer; // 记录哪个消费者在此缓冲区睡眠
+    char buf[BUF_SIZE];           // 缓冲区大小
+    int32_t head;                 // 队首,数据往队首处写入
+    int32_t tail;                 // 队尾,数据从队尾处读出
+};
+
+/// @brief 初始化 ioqueue 队列
+/// @param ioq
+void ioqueue_init(struct ioqueue *ioq);
+/// @brief 判断队列是否已满
+/// @param ioq
+/// @return TRUE OR FALSE
+bool ioq_full(struct ioqueue *ioq);
+/// @brief 判断队列是否为空
+/// @param ioq
+/// @return TRUE OR FALSE
+bool ioq_empty(struct ioqueue *ioq);
+/// @brief 使生产者或消费者在此缓冲区上等待
+/// @param waiter
+static void ioq_wait(struct task_struct **waiter);
+/// @brief 唤醒 waiter
+static void wakeup(struct task_struct **waiter);
+/// @brief 消费者从 ioq 队列中获取一个字符
+char ioq_getchar(struct ioqueue *ioq);
+/// @brief 生产者往 ioq 队列中添加一个字符
+void ioq_putchar(struct ioqueue *ioq, char byte);
+
+#endif //!__DEVICE_IOQUEUE_H

+ 11 - 7
device/keyboard.c

@@ -1,6 +1,7 @@
 #include "keyboard.h"
-#include "../kernel/global.h"
 #include "stdint.h"
+#include "ioqueue.h"
+#include "../kernel/global.h"
 #include "../lib/kernel/io.h"
 #include "../lib/kernel/print.h"
 #include "../kernel/interrupt.h"
@@ -35,6 +36,8 @@
 
 /*定义以下变量记录相应键是否按下的状态, ext_scancode 用于记录 makecode 是否以 0xe0 开头*/
 static bool ctrl_status, shift_status, alt_status, caps_lock_status, ext_scancode;
+/*定义键盘缓冲区*/
+struct ioqueue keyboard_buf;
 /*以通码 make_code 为索引的二维数组*/
 static char keymap[][2] = {
     /*扫描码未与 shift 组合*/
@@ -100,14 +103,11 @@ static char keymap[][2] = {
     /* 其它按键暂不处理 */
 };
 
-// 键盘中断处理程序
-static void intr_keyboard_handler(void);
-
 void keyboard_init(void)
 {
     put_str("keyboard init start\n");
-    // todo: 1. 打开 8042 键盘控制器
-
+    // 1. 初始化键盘缓冲区
+    ioqueue_init(&keyboard_buf);
     // 2. 注册键盘中断处理程序
     register_handler(0x21, intr_keyboard_handler);
     put_str("keyboard init done\n");
@@ -209,7 +209,11 @@ static void intr_keyboard_handler(void)
         /* 只处理 ASCII 码不为 0 的键 */
         if (cur_chr)
         {
-            put_char(cur_chr);
+            if (!ioq_full(&keyboard_buf))
+            {
+                put_char(cur_chr);                   // 打印字符
+                ioq_putchar(&keyboard_buf, cur_chr); // 加入缓冲区
+            }
             return;
         }
 

+ 3 - 0
device/keyboard.h

@@ -13,6 +13,9 @@
 
 #define KBD_STATUS_MASK 0x02
 
+// 键盘中断处理程序
+static void intr_keyboard_handler(void);
 void keyboard_init(void);
+extern struct ioqueue keyboard_buf;
 
 #endif // !__DEVICE_KEYBOARD_H

+ 2 - 1
kernel/interrupt.c

@@ -74,7 +74,8 @@ static void pic_init(void)
 
     /* 打开主片上 IR0, 也就是目前只接受时钟产生的中断*/
     // outb(PIC_M_DATA, 0xfe);
-    outb(PIC_M_DATA, 0xfd); // 测试键盘,只打开键盘中断,其他全部关闭
+    // outb(PIC_M_DATA, 0xfd); // 测试键盘,只打开键盘中断,其他全部关闭
+    outb(PIC_M_DATA, 0xfc); // 测试键盘,时钟,开键盘,时钟中断,其他全部关闭 1111 1100
     outb(PIC_S_DATA, 0xff);
 
     put_str("   pic_init done\n");

+ 21 - 8
kernel/main.c

@@ -5,6 +5,8 @@
 #include "../thread/thread.h"
 #include "interrupt.h"
 #include "../device/console.h"
+#include "../device/ioqueue.h"
+#include "../device/keyboard.h"
 
 void thread_a_func(void *);
 void thread_b_func(void *);
@@ -21,10 +23,10 @@ int main(void)
     // put_int((uintptr_t)addr);
     // put_str("\n");
 
-    // thread_start("thread_a", 31, thread_a_func, "argA ");
-    // thread_start("thread_b", 8, thread_b_func, "argB ");
+    thread_start("consumer_a", 31, thread_a_func, " A_");
+    thread_start("consumer_b", 31, thread_b_func, " B_");
 
-    intr_enable(); // 打开中断,使时钟中断起作用
+    intr_enable(); // 打开中断,使中断起作用
     // 主线程会一直执行, 直到被中断或被其他线程强制结束
     while (1)
         ;
@@ -37,18 +39,29 @@ int main(void)
 void thread_a_func(void *arg)
 {
     // 用 void 来表示通用类型,这样就可以传递任意类型的参数
-    char *para = arg;
     while (1)
     {
-        console_put_str(para);
+        enum intr_status old_status = intr_disable();
+        if (!ioq_empty(&keyboard_buf))
+        {
+            console_put_str(arg);
+            char byte = ioq_getchar(&keyboard_buf);
+            console_put_char(byte);
+        }
+        intr_set_status(old_status);
     }
 }
 void thread_b_func(void *arg)
 {
-    // 用 void 来表示通用类型,这样就可以传递任意类型的参数
-    char *para = arg;
     while (1)
     {
-        console_put_str(para);
+        enum intr_status old_status = intr_disable();
+        if (!ioq_empty(&keyboard_buf))
+        {
+            console_put_str(arg);
+            char byte = ioq_getchar(&keyboard_buf);
+            console_put_char(byte);
+        }
+        intr_set_status(old_status);
     }
 }

+ 4 - 1
makefile

@@ -15,7 +15,7 @@ OBJS = $(BUILD_O_DIR)/main.o $(BUILD_O_DIR)/init.o $(BUILD_O_DIR)/interrupt.o  \
 	$(BUILD_O_DIR)/debug.o $(BUILD_O_DIR)/string.o $(BUILD_O_DIR)/bitmap.o  \
 	$(BUILD_O_DIR)/memory.o $(BUILD_O_DIR)/thread.o $(BUILD_O_DIR)/list.o  \
 	$(BUILD_O_DIR)/switch.o $(BUILD_O_DIR)/sync.o $(BUILD_O_DIR)/console.o \
-	$(BUILD_O_DIR)/keyboard.o
+	$(BUILD_O_DIR)/keyboard.o $(BUILD_O_DIR)/ioqueue.o
 
 ################################ C 代码编译 ################################
 $(BUILD_O_DIR)/main.o: kernel/main.c lib/kernel/print.h lib/stdint.h kernel/init.h
@@ -57,6 +57,9 @@ $(BUILD_O_DIR)/console.o: device/console.c device/console.h
 $(BUILD_O_DIR)/keyboard.o: device/keyboard.c device/keyboard.h
 	$(CC) $(CFLAGS) $< -o $@
 
+$(BUILD_O_DIR)/ioqueue.o: device/ioqueue.c device/ioqueue.h
+	$(CC) $(CFLAGS) $< -o $@
+
 ################################ 汇编代码编译 ################################
 $(BUILD_O_DIR)/kernel.o: kernel/kernel.S
 	$(AS) $(ASFLAGS) $< -o $@