一篇带你了解Linux的输入输出

系统 Linux
输入输出设备的设备厂商很多。因为设备厂商复杂多变,设备厂商也同样复杂多变,需要层层屏蔽差异化的部分,给上层提供标准化的部分,最终到用户态,给用户提供了基于文件系统的统一的接口

 [[407125]]

总线

Intel采用双独立总线(英语:Dual Independent Bus,DIB),使用外部的前端总线到主系统存储器,和内部的后端总线于一个或多个中央处理器、CPU缓存间。CPU 里面的内存接口,直接和系统总线通信,然后系统总线再接入一个 I/O 桥接器(I/O Bridge)。这个 I/O 桥接器,一边接入了我们的内存总线,使得我们的 CPU 和内存通信;另一边呢,又接入了一个 I/O 总线,用来连接 I/O 设备。


在物理层面,其实我们完全可以把总线看作一组“电线”。一般有五类线路。

  • 数据总线(Data Bus):在CPU与RAM之间来回传送需要处理或是需要储存的数据。
  • 地址总线(Address Bus):用来指定在RAM(Random Access Memory)之中储存的数据的地址。
  • 控制总线(Control Bus):将微处理器控制单元(Control Unit)的信号,传送到周边设备,一般常见的为USB Bus和1394 Bus。
  • 扩展总线(Expansion Bus):可连接扩展槽和电脑。
  • 局部总线(Local Bus):取代更高速数据传输的扩展

I/O设备

输入输出设备,并不只是一个设备。大部分的输入输出设备,都有两个组成部分。第一个是它的接口(Interface),第二个才是实际的 I/O 设备(Actual I/O Device)。我们的硬件设备并不是直接接入到总线上和 CPU 通信的,而是通过接口,用接口连接到总线上,再通过总线和 CPU 通信。比如可以把硬盘分为 IDE(Integrated Drive Electronics)、SCSI(Small Computer System Interface) 、SAS(Serial Attached SCSI) 、SATA(Serial ATA) 、FC(Fibre Channel)都是计算机主板上内置的各个接口。硬件只需要提供对应接口的设备驱动程序。

设备控制器

接口本身就是设备控制器。CPU 其实不是和实际的硬件设备打交道,而是和设备控制器打交道。设备控制器除了要将设备与计算机连接外,还有很多重要任务。

  • 随时监控设备所处的状态,实现对设备的控制与操作。
  • 设备控制器中都有三类寄存器:分别是状态寄存器(Status Register)、 命令寄存器(Command Register)以及数据寄存器(Data Register)。每个控制寄存器被分配一个 I/O 端口,我们可以通过特殊的汇编指令(例如 in/out 类似的指令)操作这些寄存器。状态寄存器,可以通过检测状态标志位,来确定输入或者输出操作是否完成。
  • 有些设备还有数据缓冲区。如打印机等。可内存映射 I/O,可以分配一段内存空间给它,就像读写内存一样读写数据缓冲区。
  • 设备控制器还监管对由I/O设备传送来的数据进行差错检测。若发现传送中出现了错误,通常是将差错检测码置位,并向CPU报告,于是CPU将本次传送来的数据作废,并重新进行一次传送。这样便可保证数据输入的正确性。

设备驱动程序

用于实现设备对具体设备的管理与操作。要让设备工作,必选访问设备控制器中的各种寄存器,这部分通过编写特定的程序代码来实现程序,就是“设备驱动程序”。主要有以下功能:

  • 对设备进行初始化
  • 使设备投入运行和退出服务
  • 从设备接收数据并将它们送回内核
  • 将数据从内核送到设备
  • 检测和处理设备出现的错误

DMA控制器

有的设备需要读取或者写入大量数据。如果所有过程都让 CPU 协调的话,就需要占用 CPU 大量的时间,比方说,磁盘就是这样的。这种类型的设备需要支持 DMA 功能,也就是说,允许设备在 CPU 不参与的情况下,能够自行完成对内存的读写。实现 DMA 机制需要有个 DMA 控制器帮你的 CPU 来做协调,就像下面这个图中显示的一样。

  • CPU 只需要对 DMA 控制器下指令,说它想读取多少数据,放在内存的某个地方就可以了。
  • 接下来 DMA 控制器会发指令给磁盘控制器,请求数据传送到内存。磁盘驱动器读取磁盘上的数据到磁盘控制器的内核缓冲区,磁盘控制器进行差错校验,保证没有发生读错误发生。磁盘控制器的寄存器,CPU与DMA都可以修改。
  • 磁盘控制器从其内部缓冲区中读取数据的时候知道这个数据该写到什么地方。然后通过内存总线将数据写到内存。
  • 当写操作完成时,磁盘控制器在总线上发出一个确认成功的信号到DMA控制器。
  • DMA 控制器发中断通知 CPU 指令完成,CPU 就可以直接用内存里面现成的数据了。

中断控制器

硬件的中断控制器,当设备完成任务后触发中断到中断控制器,中断控制器就通知 CPU,一个中断产生了,CPU 需要停下当前手里的事情来处理中断。一般的流程是,一个设备驱动程序初始化的时候,要先注册一个该设备的中断处理函数。中断的时候,触发的函数是 do_IRQ。这个函数是中断处理的统一入口。在这个函数里面,我们可以找到设备驱动程序注册的中断处理函数 Handler,然后执行它进行中断处理。

磁盘驱动的实现

在Linux中,设备驱动程序是一组相关函数的集合。它包含设备服务子程序和中断处理程序。设备服务子程序包含了所有与设备相关的代码,每个设备服务子程序只处理一种设备或者紧密相关的设备。其功能就是从与设备无关的软件中接受抽象的命令并执行之。当执行一条请求时,具体操作是根据设备控制器对驱动程序提供的接口(指的是控制器中的各种寄存器),并利用中断机制去调用中断服务子程序配合设备来完成这个请求。设备驱动程序利用结构 file_operations 与文件系统联系起来,即设备的各种操作的入口函数存在file_operation中。对于特定的设备来说有一些操作是不必要的,其入口置为NULL。

Linux 内核中虽存在许多不同的设备驱动程序但它们具有一些共同的特性:

  • 驱动程序属于内核代码,设备驱动程序是内核的一部分,它象内核中其它代码一样运行在内核模式,驱动程序如果出错将会使操作系统受到严重破坏,甚至能使系统崩溃并导致文件系统的破坏和数据丢失。
  • 为内核提供统一的接口,设备驱动程序必须为 Linux 内核或其它子系统提供一个标准的接口。例如终端驱动程序为Linux 内核提供了一个文件 I/O 接口。
  • 驱动程序的执行是属于内核机制并且使用内核服务 。设备驱动可以使用标准的内核服务如内存分配、中断发送和等待队列等等。
  • 动态可加载,多数 Linux 设备驱动程序可以在内核模块发出加载请求时加载,而不再使用时将其卸载。这样内核能有效地利用系统资源。
  • 可配置,Linux 设备驱动程序可以连接到内核中。当内核被编译时,被连入内核的设备驱动程序是可配置的。

这样Linux的输入输出就很明朗了

输入输出设备的设备厂商很多。因为设备厂商复杂多变,设备厂商也同样复杂多变,需要层层屏蔽差异化的部分,给上层提供标准化的部分,最终到用户态,给用户提供了基于文件系统的统一的接口。

 

责任编辑:姜华 来源: 运维开发故事
相关推荐

2021-02-02 18:39:05

JavaScript

2021-05-20 06:57:16

RabbitMQ开源消息

2021-07-08 06:30:03

Linux CPULinux 系统

2021-08-11 07:02:21

npm包管理器工具

2021-12-01 11:40:14

Python 输入输出

2022-02-18 08:54:21

docker操作系统Linux

2022-05-05 07:40:07

maskCSS

2023-05-12 08:19:12

Netty程序框架

2021-07-28 10:02:54

建造者模式代码

2021-07-14 08:24:23

TCPIP 通信协议

2021-06-30 00:20:12

Hangfire.NET平台

2021-08-02 06:34:55

Redis删除策略开源

2021-11-24 08:51:32

Node.js监听函数

2021-11-08 08:42:44

CentOS Supervisor运维

2021-12-15 11:52:34

GPLLinuxGNU

2022-04-13 21:19:56

Prometheusruler 组件

2021-01-29 18:41:16

JavaScript函数语法

2021-06-04 09:56:01

JavaScript 前端switch

2020-11-10 10:48:10

JavaScript属性对象

2021-08-14 10:01:43

Python条件语句Python基础
点赞
收藏

51CTO技术栈公众号