Linux内核一个的主要作用就是管理硬件,而像键盘、鼠标、显示器、网卡、硬盘、打印机、CD/DVD等等这么多设备,形状、用法、处理速度、功能都不一样,该怎么统一管理起来呢?
1. 一切皆文件
在linux中一切皆文件,设备也不例外,设备文件放在dev目录下。应用程序可以像访问普通文件一样访问设备,内核vfs下面的驱动程序层和硬件实现具体的功能。
ubuntu@VM-0-16-ubuntu:~$ ls -l /dev/
total 0
crw-r--r-- 1 root root 10, 235 Apr 19 22:07 autofs
crw-rw---- 1 root disk 10, 234 Apr 19 22:07 btrfs-control
lrwxrwxrwx 1 root root 3 Apr 19 22:07 cdrom -> sr0
crw------- 1 root root 5, 1 Apr 19 22:07 console
lrwxrwxrwx 1 root root 11 Apr 19 22:06 core -> /proc/kcore
crw------- 1 root root 10, 60 Apr 19 22:07 cpu_dma_latency
crw------- 1 root root 10, 203 Apr 19 22:07 cuse
lrwxrwxrwx 1 root root 3 Apr 19 22:07 dvd -> sr0
.....
brw-rw---- 1 root disk 252, 0 Apr 19 22:07 vda
brw-rw---- 1 root disk 252, 1 Apr 19 22:07 vda1
.....
2. 总线
外设各种各样,但它们不能直接连到CPU上,毕竟CPU就那么大的一块小板子,不过设备可以通过总线来和CPU连在一起。这里理解,总线有点像公路的意思,总线的英文为bus,公共汽车,寓意其实也差不多。总线有很多中,如下图所示,有PCI总线、USB、SCSI等.
那最常见的USB(Universal Serial Bus),它主要用于外部总线,U盘,鼠标,键盘,硬盘等,还支持热拔插. USB等外部总线会和系统总线通过PCI桥接器连在一起, 系统总线和CPU连在一起.通过外部总线,就可以扩展更多的外设.
3. 与外设的交互
我们现在知道CPU和外设通过各种总线连在一起,那么内核和外设是怎么进行交互的呢?
这个问题大致可以拆分为以下两个问题
1. 如何知道某个设备的数据已经就绪或者可以读取?
这个问题比较容易理解, 一种办法就是不断的轮询(polling), 重复问设备好了没有,显然这种方法很笨.另一种方法就是中断,CPU提供中断线,当就绪或者可写的时候,发送一个中断信号给内核. 不同的情景就对应不同的中断,中断通过中断向量来标识.
2. 如何区别各个设备以及如何访问这些设备?
这个问题比较麻烦,如果不了解一些基础概念,就直接读<深入Linux内核架构>这类书,会让人稀里糊涂的.
IO端口: 就是x86上用来访问特定外设地址的东西. 可以理解成x86的IO总线上的具体地址
IO空间: 是CPU读写外部设备是使用的地址,和IO总线对应
内存空间: 内存地址寻址的范围,比如32位操作系统的寻址空间位2^32 为4GB
统一编址: RISC指令系统的CPU(如ARM)只有一个物理地址空间, 外设IO端口的物理地址也被映射进来了,成了内存的一部分.这样CPU就可以像访问内存一样设备,这样指令就减少了很多,所以称为精简指令集计算机(RISC:Reduced Instruction Set Computing RISC)的来历.
独立编址: 相当于RISC,比如x86等复杂指令集计算机(CISC: Complex Instruction SetComputer), 外设有自己独立的地址空间,称为IO地址空间或者IO端口空间.CPU通过专门的IO指令来访问这个个空间的地址. 一般这种地址空间比较小,x86CPU只有64KB.An I/O port is usually used as a technical term for a specific address on the x86's IO bus. This bus provides communication with devices in a fixed order and size, and was used as an alternative to memory access. On many other architectures, there is no predefined bus for such communication and all communication with hardware is done via memory-mapped IO. This also increasingly happens on modern x86 hardware.
简单讲:
外设想要傍CPU这个大款,要么像x86那样毛遂自荐,直接和CPU交互,这些都想和CPU直接对话的人都被安排在IO空间里IO端口.
要么就从中像ARM体系一样, 让外设混在内存里,占内存一点逻辑上的空间, 让CPU像访问内存一样访问设备。
但对于Linux内核而言,它要兼容不同体系结构的CPU,就引入了IO region的概念,这个空间可以使IO映射过来的(x86),也可以是内存映射过来的(ARM),不管那种方式都要先申请IO region。内核提供了统一的接口
- request_resource() 申请IO region
- release_resource() 释放
[0] https://www.21ic.com/embed/hardware/can/201905/89958.html
[1] 深入Linux内核架构
[2] https://blog.csdn.net/ce123_zhouwei/article/details/7204458
[3] https://wiki.osdev.org/I/O_Ports
[4 ]https://blog.csdn.net/weixin_35867894/article/details/116623801