首先在将tty纳入文件系统后,它将处于核心位置。主要分为三个模块,tty.c(控制),keyboard.c(输入),console.c(输出)模块。
将tty纳入文件系统后,dev_tty1, dev_tty2, dev_tty3三个文件。
现在假设进程SHELL打开了dev_tty1, 把它当做文件一样读写.
具体读入的过程是这样的:
- SHELL进程调用read,传文件(dev_tty1)的fd和buf到文件系统。
- 文件系统发现此文件属性为tty设备,就向task_tty发消息准备让tty从外界读入信息。此时shell阻塞,文件系统继续运行,tty正在等待用户输入。
- task_tty是一个不断运行的进程。tty_dev_read(tty)[tty.c] ---> keyboard_read(tty)[keyboard.c] ---> in_process(tty, key)[tty.c]就将现阶段keyboard缓冲区的字符都读入了当前tty的缓冲区了。
- task_tty接着运行。tty_dev_write(tty)[tty.c] ---> out_char(tty->console, char)[console.c] && put to the buffer of SHELL
- 当遇到回车或者字节数足够的时候,tty告诉文件系统,去唤醒SHELL。当然这个过程中都是通过传消息,消息带着各种参数的。
- SHELL接收到用户输入的参数后,继续运行。
SHELL的运行过程是这样的:
- 它打开dev_tty1文件设备,然后进入一个while循环内部,
- 等待输入命令,并对命令做出反应,然后又等待命令。
[IMPORTANT]
最清晰的还是直接看代码阿,这里学到最多的几点:
- tty包括 tty.c(控制),keyboard.c(输入),console.c(输出)3模块,思路清晰,分工合理。
- tty完全为文件系统的一个文件了,为外界进程服务,行为受外界shell进程控制。
这两点要好好体会!
补充:tty模块的结构:
task-tty是主运行的进程,并且它有tty.c模块,keyboard.c模块,console.c模块,貌似每个模块都有自己的全局变量,虽然全局变量其他模块也能访问到,但是为了模块化,模块之间尽量不要访问其他模块的全局变量。
每次task-tty主运行进程去控制tty.c,因为tty.c被根据console分成3份,所以其他进程打开tty文件也是打开的这3个,然后操作这三个文件,就好像在操作这三个console。
3个tty负责控制-----从keyboard.c得到输入数据-----使用console.c模块向外输出。
tty负责把输入的数据传给正在阻塞等待的进程。