x
x
xv6-sifive文档
Search…
⌃K

输入输出

初始化
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. 1.
    根据系统调用的特性进入到内核态,根据函数指针数组的标记, 跳转到sys_write函数.
  2. 2.
    sys_write函数获取用户传递的参数,并调用filewrite.
  3. 3.
    filewrite函数中, 由于文件是FD_DEVICE类型, 所以会调用对应设备的写函数将数据写入设备中.
  4. 4.
    跳转到consolewrite中, 把用户进程的每一个字符读取到内核态, 然后输出即可.
printf
对于输出函数printf, 基本的思路还是一致的, 输入参数, 将字符串中的%*部分转化成字符串, 然后按字符输出即可.
  • 内核态的printf可以直接调用consoleputc接口将字符显示在显示器上.
  • 用户态的printf通过设置一个buf数组暂存待输出的内容, 然后再调用write将数据写入到显示器设备中从而可以进行显示.
一般的输入
shell在执行的时候会一直运行getcmd来获取命令,下面来梳理一下shell是如何获取命令的:
  1. 1.
    首先执行getcmd来获取一条指令.
  2. 2.
    getcmd是通过调用gets函数获取一行元素来进行的.
  3. 3.
    gets函数中,我们会一直循环执行read(0,text,length)函数来从标准输入中获取长度为length的数据,放入text对应的地址中.
  4. 4.
    同样的,read是一个系统调用,同样需要跳转到sys_read中,和输出流程一样,然后跳转到fileread中,由于是FD_DEVICE类型的,所以说还是会跳入consoleread中:
  5. 5.
    consoleread函数会调用consoleintr和sbi相关函数.从输入中取一个字符,放入buf中.
  6. 6.
    然后consoleread读取buf中的数据,放入用户态空间中.
bug调试
1、键盘输入失效.
我们进入中断处理状态,每一次中断都会输出中断来源,发现我们进行键盘输入,中断的来源只有5(时钟中断),我们推测UART设备不能工作,或者是不能正确地引起中断,我们配置了中断相关的context,未果,故采用sbi的形式.
2、只能输入一个字符.
函数错误调用,导致了强行要求每从buf读出一个字符,buf就要从机器那端读入一行,这种对读出读入速率的强行要求是不合理的,修改之后可以正常运行.