输入输出

初始化

void
consoleinit(void)
{
  initlock(&cons.lock, "cons");

  cons.e = cons.w = cons.r = 0;
  
  // connect read and write system calls
  // to consoleread and consolewrite.
  devsw[CONSOLE].read = consoleread;
  devsw[CONSOLE].write = consolewrite;
}

标记读写指针,并且制定读写函数.

一般的输出

在用户程序中调用write(1,text,length), 把text所指地址空间长度为length的内容送入到标准输出, 其中write就是一个系统调用.

  1. 根据系统调用的特性进入到内核态,根据函数指针数组的标记, 跳转到sys_write函数.

  2. sys_write函数获取用户传递的参数,并调用filewrite.

  3. filewrite函数中, 由于文件是FD_DEVICE类型, 所以会调用对应设备的写函数将数据写入设备中.

  4. 跳转到consolewrite中, 把用户进程的每一个字符读取到内核态, 然后输出即可.

printf

对于输出函数printf, 基本的思路还是一致的, 输入参数, 将字符串中的%*部分转化成字符串, 然后按字符输出即可.

  • 内核态的printf可以直接调用consoleputc接口将字符显示在显示器上.

  • 用户态的printf通过设置一个buf数组暂存待输出的内容, 然后再调用write将数据写入到显示器设备中从而可以进行显示.

一般的输入

shell在执行的时候会一直运行getcmd来获取命令,下面来梳理一下shell是如何获取命令的:

  1. 首先执行getcmd来获取一条指令.

  2. getcmd是通过调用gets函数获取一行元素来进行的.

  3. gets函数中,我们会一直循环执行read(0,text,length)函数来从标准输入中获取长度为length的数据,放入text对应的地址中.

  4. 同样的,read是一个系统调用,同样需要跳转到sys_read中,和输出流程一样,然后跳转到fileread中,由于是FD_DEVICE类型的,所以说还是会跳入consoleread中:

  5. consoleread函数会调用consoleintr和sbi相关函数.从输入中取一个字符,放入buf中.

  6. 然后consoleread读取buf中的数据,放入用户态空间中.

bug调试

1、键盘输入失效.

我们进入中断处理状态,每一次中断都会输出中断来源,发现我们进行键盘输入,中断的来源只有5(时钟中断),我们推测UART设备不能工作,或者是不能正确地引起中断,我们配置了中断相关的context,未果,故采用sbi的形式.

2、只能输入一个字符.

函数错误调用,导致了强行要求每从buf读出一个字符,buf就要从机器那端读入一行,这种对读出读入速率的强行要求是不合理的,修改之后可以正常运行.

Last updated