Loading... 关于键盘输入我不想写,就是一个和硬件交互的过程,这里主要还是说一下输入输出缓冲区。 代码已经打好 tag,[链接](https://github.com/chujDK/chuj-elephant-os/releases/tag/basic_input_output),比较重要的就是 ioqueue。 获取键盘输入后,可以直接打出到屏幕上,但是这样除了给用户看看之外没有任何的用处,如果想要让输入有效,就必然需要把输入暂存到一个地方,然后让需要从用户读取的线程读取输入,这就需要一个缓冲区来处理这个问题,*Dijkstra* 提出了一个生产者-消费者模型,基于该模型的思想可以有效地解决这个问题。 ### 生产者-消费者模型 说到底来,其实这就是一个循环队列。以一个 shell 为例,shell 需要获取用户的输入,那么 shell 就是消费者,消费用户的输入,而用户的输入(通过键盘中断处理程序来输入)就是原料,键盘中断处理程序就是生产者。如果让生产者直接和消费者对接,由于两者是异步的,特别的,用户输入甚至是不确定的,就随时可能会出现供不应求或供过于求的问题。 于是我们就在两者之间放一个缓冲区,这个缓冲区应该可以做到:消费者随时可以从缓存区中取数据,取不到就阻塞自己,生产者随时可以向缓冲区中放数据,放不进了就阻塞自己;相应的,一旦消费者发现缓冲区里面可以放数据了,就唤醒被阻塞的生产者,生产者一旦发现缓冲区里面可以取数据了,就唤醒被阻塞的消费者。 为了实现这样的缓存区,我们需要维护一个循环队列(简单的,可以用数组模拟)和被阻塞的消费者和生产者指针,就是这样一个结构 ```cpp struct ioqueue { struct lock lock; PCB* sleeping_producer; PCB* sleeping_consumer; char buf[bufsize]; size_t head; size_t tail; }; ``` 这里的 head 和 tail 维护的是下标。 然后我们提供这样几个方法 ```cpp /* get one byte from the buf */ char ioqueue_getchar(struct ioqueue* queue); /* put one byte to the buf */ void ioqueue_putchar(struct ioqueue* queue, char byte); void ioqueueInit(struct ioqueue* queue); uint8_t ioqueueFull(struct ioqueue* queue); uint8_t ioqueueEmpty(struct ioqueue* queue); ``` 就可以实现这个缓冲区了。 getchar 就是一个消费过程 ```cpp char ioqueue_getchar(struct ioqueue* queue) { ASSERT(GetIntStatus() == INT_OFF); while (ioqueueEmpty(queue)) { sys_lock_lock(&queue->lock); /* make current thread (consumer) blocked, and record it */ ioqueueBlock(&queue->sleeping_consumer); sys_lock_unlock(&queue->lock); } char byte = queue->buf[queue->tail]; queue->tail = ptr_next_pos(queue->tail); if (queue->sleeping_producer != NULL) { ioqueueWakeup(&queue->sleeping_producer); } return byte; } ``` putchar 就是生产过程 ```cpp void ioqueue_putchar(struct ioqueue* queue, char byte) { ASSERT(GetIntStatus() == INT_OFF); while (ioqueueFull(queue)) { sys_lock_lock(&queue->lock); /* make current thread (producer) blocked, and record it */ ioqueueBlock(&queue->sleeping_producer); sys_lock_unlock(&queue->lock); } queue->buf[queue->head] = byte; queue->head = ptr_next_pos(queue->head); if (queue->sleeping_consumer != NULL) { ioqueueWakeup(&queue->sleeping_producer); } return; } ``` 修改 main.c 后就可以进行输入输出了 ```cpp #include "print.h" #include "init.h" #include "debug.h" #include "memory.h" #include "thread.h" #include "interrupt.h" #include "console.h" #include "keyboard.h" #include "ioqueue.h" void KThreadTest(); int _start() { sys_putstr("this is kernel!\n"); InitAll(); /* this thread output the input from the keyboard */ ThreadStart("KThreadTestA", 31, KThreadTest, ""); EnableInt(); while(1); return 0; } void KThreadTest() { while(1) { enum int_status old_statu = DisableInt(); if (!ioqueueEmpty(&keyboard_IO_buf)) { console_putchar(ioqueue_getchar(&keyboard_IO_buf)); } SetIntStatus(old_statu); } } ``` 效果就是 <div style="text-align:center"><img src="https://www.cjovi.icu/usr/uploads/2021/06/2732042757.png "></div> 这里总体比较简单,就不再多说了。 最后修改:2021 年 06 月 06 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 0 如果觉得我的文章对你有用,那听听上面我喜欢的歌吧