设备I/O与显示器
1.主要流程
外设驱动原理可以概括为三步:
- 形成文件视图
- out 指令调用设备处理
- 外设处理完毕后中断处理程序。
2.前置概念
首先先来看看文件视图是什么:
我们所熟知的printf()
,scanf()
这些I/O函数都是将open()
,read()
,write()
,close()
这些系统调用进行一个封装,而为什么我们调用printf()
函数就会将我们指定的字符串输出到屏幕上的原因,我们来看下图:
看了这张图我们就很容易理解,在/dev
目录下存放着许多设备块
通过每个I/O函数封装好的内部open()
参数指定的设备文件来完成所需要的需求,这一步很简单,接下来我们来继续探究一下整个过程。
3.探究 Hello world 如何跑到屏幕上?
我们通过prinf()
来举例,刚刚我们知道了调用printf()
函数实际上是在调用封装好的系统调用,在打开目标设备文件后,接着调用write()
函数向设备中写数据.
上图中open()
的作用是通过我们传入的filename
来找到对应的inode,并将这个inode保存到file
的结构体中
接下来的主要过程是通过我们传入的fd
(文件索引)在该进程的PCB表中寻找到其对应的文件,再通过找到file的结构体找出其存储的信息inode,也就是对应具体向何处输出的信息(/dev/xxx)
核心流程就是这个链表
ok,当我们找到这个inode
之后,就准备向屏幕输出信息了。
我们知道每个主设备号至少会对应一个次设备号,具体关系可以查看这篇博客,讲得很清楚
在sys_write()
中,我们可以看到我们找到这个inode后对其属性进行一个判断(是否是字符类型),之后调用相应的rw_char()
函数
接着我们可以看到在每个crw_table()
中存放着函数指针,通过传入的MAJOR(dev)
来找到字符设备(主设备号)所对应的(此设备号对应的)处理函数.
此时我们调用表中的处理函数来转到实现输出的核心函数
可知,这里维护了一个(缓冲区)队列数据结构,如果队列满了就睡眠等待,否则输出队列中的数据
此时开始最后处理数据的属性,代码很清楚
最后调用PUTCH()
来输出,最终实现了打印在屏幕上的Hello world!