操作系统之设备管理(键盘敲入A字母时,操作系统期间发生了什么)

server/2024/12/15 19:47:44/
键盘可以说是我们最常使⽤的输⼊硬件设备了,但身为程序员的你,你知道「 键盘敲⼊ A 字⺟时,操作系 统期间发生了什么吗 」?
那要想知道这个发⽣的过程,我们得先了解了解「操作系统是如何管理多种多样的的输⼊输出设备」的,等了解完这个后,我们再来看看这个问题,你就会发现问题已经被迎刃⽽解了。

设备控制器

我们的电脑设备可以接⾮常多的输⼊输出设备,⽐如键盘、⿏标、显示器、⽹卡、硬盘、打印机、⾳响等等,每个设备的⽤法和功能都不同,那操作系统是如何把这些输⼊输出设备统⼀管理的呢?
为了屏蔽设备之间的差异,每个设备都有⼀个叫 设备控制器( Device Control 的组件,⽐如硬盘有硬盘控制器、显示器有视频控制器等。

因为这些控制器都很清楚的知道对应设备的⽤法和功能,所以 CPU 是通过设备控制器来和设备打交道的。
设备控制器⾥有芯⽚,它可执⾏⾃⼰的逻辑,也有⾃⼰的寄存器,⽤来与 CPU 进⾏通信,⽐如:
  • 通过写⼊这些寄存器,操作系统可以命令设备发送数据、接收数据、开启或关闭,或者执⾏某些其他操作。
  • 通过读取这些寄存器,操作系统可以了解设备的状态,是否准备好接收⼀个新的命令等。
实际上,控制器是有三类寄存器,它们分别是 状态寄存器( Status Register 命令寄存器( Command Register 以及 数据寄存器( Data Register ,如下图:

这三个寄存器的作⽤:
  • 数据寄存器CPU I/O 设备写⼊需要传输的数据,⽐如要打印的内容是「Hello」,CPU 就要先发送⼀个 H 字符给到对应的 I/O 设备。
  • 命令寄存器CPU 发送⼀个命令,告诉 I/O 设备,要进⾏输⼊/输出操作,于是就会交给 I/O 设备去⼯作,任务完成后,会把状态寄存器⾥⾯的状态标记为完成。
  • 状态寄存器,⽬的是告诉 CPU ,现在已经在⼯作或⼯作已经完成,如果已经在⼯作状态,CPU 再发送数据或者命令过来,都是没有⽤的,直到前⾯的⼯作已经完成,状态寄存标记成已完成,CPU 才能发送下⼀个字符和命令。
CPU 通过读写设备控制器中的寄存器控制设备,这可⽐ CPU 直接控制输⼊输出设备,要⽅便和标准很多。
另外, 输⼊输出设备可分为两⼤类 : 块设备( Block Device 字符设备( Character Device
  • 块设备,把数据存储在固定⼤⼩的块中,每个块有⾃⼰的地址,硬盘、USB 是常⻅的块设备。
  • 字符设备,以字符为单位发送或接收⼀个字符流,字符设备是不可寻址的,也没有任何寻道操作,⿏标是常⻅的字符设备。
块设备通常传输的数据量会非常⼤,于是控制器设⽴了⼀个可读写的 数据缓冲区
  • CPU 写⼊数据到控制器的缓冲区时,当缓冲区的数据囤够了⼀部分,才会发给设备。
  • CPU 从控制器的缓冲区读取数据时,也需要缓冲区囤够了⼀部分,才拷⻉到内存。
这样做是为了,减少对设备的频繁操作。
CPU 是如何与设备的控制寄存器和数据缓冲区进⾏通信的?存在两个⽅法:
  • 端⼝ I/O,每个控制寄存器被分配⼀个 I/O 端⼝,可以通过特殊的汇编指令操作这些寄存器,⽐如in/out 类似的指令。
  • 内存映射 I/O,将所有控制寄存器映射到内存空间中,这样就可以像读写内存⼀样读写数据缓冲区

I/O控制方式

在前⾯我知道,每种设备都有⼀个设备控制器,控制器相当于⼀个⼩ CPU ,它可以⾃⼰处理⼀些事情,但有个问题是,当 CPU 给设备发送了⼀个指令,让设备控制器去读设备的数据,它读完的时候,要怎么通知CPU呢?
控制器的寄存器⼀般会有状态标记位,⽤来标识输⼊或输出操作是否完成。于是,我们想到第⼀种 轮询等 的⽅法,让 CPU ⼀直查寄存器的状态,直到状态标记为完成,很明显,这种⽅式⾮常的傻⽠,它会占⽤CPU 的全部时间。
那我们就想到第⼆种⽅法 —— 中断 ,通知操作系统数据已经准备好了。我们⼀般会有⼀个硬件的 中断控制 ,当设备完成任务后触发中断到中断控制器,中断控制器就通知 CPU ,⼀个中断产⽣了, CPU 需要停下当前⼿⾥的事情来处理中断。
另外,中断有两种,⼀种 软中断 ,例如代码调⽤ INT 指令触发,⼀种是 硬件中断 ,就是硬件通过中断控制器触发的。
但中断的⽅式对于频繁读写数据的磁盘,并不友好,这样 CPU 容易经常被打断,会占⽤ CPU ⼤量的时间。对于这⼀类设备的问题的解决⽅法是使⽤ DMA Direct Memory Access 功能,它可以使得设备在CPU 不参与的情况下,能够⾃⾏完成把设备 I/O 数据放⼊到内存。那要实现 DMA 功能要有 「 DMA 控制器」硬件的⽀持。
DMA 的⼯作⽅式如下:
  • CPU 需对 DMA 控制器下发指令,告诉它想读取多少数据,读完的数据放在内存的某个地⽅就可以了;
  • 接下来,DMA 控制器会向磁盘控制器发出指令,通知它从磁盘读数据到其内部的缓冲区中,接着磁盘控制器将缓冲区的数据传输到内存;
  • 当磁盘控制器把数据传输到内存的操作完成后,磁盘控制器在总线上发出⼀个确认成功的信号到DMA 控制器;
  • DMA 控制器收到信号后,DMA 控制器发中断通知 CPU 指令完成,CPU 就可以直接⽤内存⾥⾯现成的数据了;
可以看到, CPU 当要读取磁盘数据的时候,只需给 DMA 控制器发送指令,然后返回去做其他事情,当磁盘数据拷⻉到内存后,DMA 控制机器通过中断的⽅式,告诉 CPU 数据已经准备好了,可以从内存读数据了。仅仅在传送开始和结束时需要 CPU 干

设备驱动程序

虽然设备控制器屏蔽了设备的众多细节,但每种设备的控制器的寄存器、缓冲区等使⽤模式都是不同的,所以为了屏蔽「设备控制器」的差异,引⼊了 设备驱动程序
设备控制器不属于操作系统范畴,它是属于硬件,⽽设备驱动程序属于操作系统的⼀部分,操作系统的内核代码可以像本地调⽤代码⼀样使⽤设备驱动程序的接⼝,⽽设备驱动程序是⾯向设备控制器的代码,它发出操控设备控制器的指令后,才可以操作设备控制器。
不同的设备控制器虽然功能不同,但是 设备驱动程序会提供统⼀的接⼝给操作系统 ,这样不同的设备驱动程序,就可以以相同的⽅式接⼊操作系统。如下图:
前⾯提到了不少关于中断的事情,设备完成了事情,则会发送中断来通知操作系统。那操作系统就需要有⼀个地⽅来处理这个中断,这个地⽅也就是在设备驱动程序⾥,它会及时响应控制器发来的中断请求,并根据这个中断的类型调⽤响应的 中断处理程序 进⾏处理。
通常,设备驱动程序初始化的时候,要先注册⼀个该设备的中断处理函数。
我们来看看,中断处理程序的处理流程:
1. I/O 时,设备控制器如果已经准备好数据,则会通过中断控制器向 CPU 发送中断请求;
2. 保护被中断进程的 CPU 上下⽂;
3. 转⼊相应的设备中断处理函数;
4. 进⾏中断处理;
5. 恢复被中断进程的上下⽂;

通用块层

对于块设备,为了减少不同块设备的差异带来的影响, Linux 通过⼀个统⼀的 通⽤块层 ,来管理不同的块设备。
通⽤块层是处于⽂件系统和磁盘驱动中间的⼀个块设备抽象层,它主要有两个功能:
  • 第⼀个功能,向上为⽂件系统和应⽤程序,提供访问块设备的标准接⼝,向下把各种不同的磁盘设备抽象为统⼀的块设备,并在内核层⾯,提供⼀个框架来管理这些设备的驱动程序;
  • 第⼆功能,通⽤层还会给⽂件系统和应⽤程序发来的 I/O 请求排队,接着会对队列重新排序、请求合并等⽅式,也就是 I/O 调度,主要⽬的是为了提⾼磁盘读写的效率。
Linux 内存⽀持 5 I/O 调度算法,分别是:
  • 没有调度算法
  • 先⼊先出调度算法
  • 完全公平调度算法
  • 优先级调度
  • 最终期限调度算法
第⼀种,没有调度算法,是的,你没听错,它不对⽂件系统和应⽤程序的 I/O 做任何处理,这种算法常⽤在虚拟机 I/O 中,此时磁盘 I/O 调度算法交由物理机系统负责。
第⼆种,先⼊先出调度算法,这是最简单的 I/O 调度算法,先进⼊ I/O 调度队列的 I/O 请求先发⽣。
第三种,完全公平调度算法,⼤部分系统都把这个算法作为默认的 I/O 调度器,它为每个进程维护了⼀个I/O 调度队列,并按照时间⽚来均匀分布每个进程的 I/O 请求。
第四种,优先级调度算法,顾名思义,优先级⾼的 I/O 请求先发⽣, 它适⽤于运⾏⼤量进程的系统,像是桌⾯环境、多媒体应⽤等。
第五种,最终期限调度算法,分别为读、写请求创建了不同的 I/O 队列,这样可以提⾼机械磁盘的吞吐量,并确保达到最终期限的请求被优先处理,适⽤于在 I/O 压⼒⽐较⼤的场景,⽐如数据库等。

存储系统I/O软件分层

前⾯说到了不少东⻄,设备、设备控制器、驱动程序、通⽤块层,现在再结合⽂件系统原理,我们来看看Linux 存储系统的 I/O 软件分层。
可以把 Linux 存储系统的 I/O 由上到下可以分为三个层次,分别是⽂件系统层、通⽤块层、设备层。他们整个的层次关系如下图:

这三个层次的作⽤是:
  • ⽂件系统层,包括虚拟⽂件系统和其他⽂件系统的具体实现,它向上为应⽤程序统⼀提供了标准的⽂件访问接⼝,向下会通过通⽤块层来存储和管理磁盘数据。
  • 通⽤块层,包括块设备的 I/O 队列和 I/O 调度器,它会对⽂件系统的 I/O 请求进⾏排队,再通过 I/O调度器,选择⼀个 I/O 发给下⼀层的设备层。
  • 设备层,包括硬件设备、设备控制器和驱动程序,负责最终物理设备的 I/O 操作。
有了⽂件系统接⼝之后,不但可以通过⽂件系统的命令⾏操作设备,也可以通过应⽤程序,调⽤
read write 函数,就像读写⽂件⼀样操作设备,所以说设备在 Linux 下,也只是⼀个特殊的⽂件。
但是,除了读写操作,还需要有检查特定于设备的功能和属性。于是,需要 ioctl 接⼝,它表示输⼊输出
控制接⼝,是⽤于配置和修改特定设备属性的通⽤接⼝。
另外,存储系统的 I/O 是整个系统最慢的⼀个环节,所以 Linux 提供了不少缓存机制来提⾼ I/O 的效率。
为了提⾼⽂件访问的效率,会使⽤ ⻚缓存、索引节点缓存、⽬录项缓存 等多种缓存机制,⽬的是为了
减少对块设备的直接调⽤。
为了提⾼块设备的访问效率, 会使⽤ 缓冲区 ,来缓存块设备的数据。

键盘敲入字母时,期间发生了什么

看完前⾯的内容,相信你对输⼊输出设备的管理有了⼀定的认识,那接下来就从操作系统的⻆度回答开头
的问题「键盘敲⼊字⺟时,操作系统期间发⽣了什么?」
我们先来看看 CPU 的硬件架构图:

CPU ⾥⾯的内存接⼝,直接和系统总线通信,然后系统总线再接⼊⼀个 I/O 桥接器,这个 I/O 桥接器,另⼀边接⼊了内存总线,使得 CPU 和内存通信。再另⼀边,⼜接⼊了⼀个 I/O 总线,⽤来连接 I/O 设备,⽐如键盘、显示器等。
那当⽤户输⼊了键盘字符, 键盘控制器 就会产⽣扫描码数据,并将其缓冲在键盘控制器的寄存器中,紧接着键盘控制器通过总线给 CPU 发送 中断请求
CPU 收到中断请求后,操作系统 保存被中断进程的 CPU 上下⽂ ,然后调⽤键盘的 中断处理程序
键盘的中断处理程序是在 键盘驱动程序 初始化时注册的,那键盘 中断处理函数 的功能就是从键盘控制器的寄存器的缓冲区读取扫描码,再根据扫描码找到⽤户在键盘输⼊的字符,如果输⼊的字符是显示字符,那就会把扫描码翻译成对应显示字符的 ASCII 码,⽐如⽤户在键盘输⼊的是字⺟ A ,是显示字符,于是就会把扫描码翻译成 A 字符的 ASCII 码。
得到了显示字符的 ASCII 码后,就会把 ASCII 码放到「读缓冲区队列」,接下来就是要把显示字符显示屏幕了,显示设备的驱动程序会定时从「读缓冲区队列」读取数据放到「写缓冲区队列」,最后把「写缓冲区队列」的数据⼀个⼀个写⼊到显示设备的控制器的寄存器中的数据缓冲区,最后将这些数据显示在屏幕⾥。
显示出结果后, 恢复被中断进程的上下文

http://www.ppmy.cn/server/150422.html

相关文章

Linux基础 -- epoll监听Netlink并实现

使用epoll监听Netlink并实现高级用法 本文档主要介绍如何使用 epoll 监听 Netlink 消息,包括基础实现与高级用法。 epoll监听Netlink的基础实现 以下示例展示了如何通过 epoll 监听 Netlink 消息并处理收发。 功能说明 创建一个 Netlink 套接字。使用 epoll 监…

2024年全国仿真创新应用大赛 | MWORKS助力“复杂系统数字仿真”赛道,获奖名单公布

2024年全国仿真创新应用大赛全国总决赛于近日圆满落幕。大赛由工业和信息化部人才交流中心主办,以“创新引领,铸就未来”为主题,来自全国的参赛院校、企业、医学科学单位、军事科学单位及仿真领域的科研院所共计422家、近1300余人参加了此次总…

“深化国际合作,共绘深空蓝图” | 同元软控受邀为亚太空间合作组织进行协同仿真设计培训

2024年12月2-6日,由亚太空间合作组织(APSCO)、中国国家航天局探月与航天工程中心联合主办的“协同仿真设计”短期培训项目在北京举行。该培训旨在提高空间探索项目开发人员的数字建模和仿真能力,从而实现基于模型驱动系统工程的数…

jvm结构介绍

Java虚拟机(JVM)是Java平台的核心组件,它负责将Java字节码转换为机器码 1. 类加载子系统(Class Loading Subsystem): • 负责将Java类加载到JVM中。这包括从文件系统、网络或其他来源读取.class文件&#x…

linux在没网的情况下如何校验时间 超详细拿来即用

一、没有校时服务器的话 1、手动修改 sudo date --set"2024-06-17 13:44:00"二、有校时服务器的话 1、手动校时 ntpdate 14.193.73.22、自动校时 写一个校时服务脚本 14.193.73.2 是校验时间服务器 #!/bin/sh while true dontpdate 14.193.73.2sleep 5;hwclock…

HTML、CSS表格的斜表头样式设置title 画对角线

我里面有用到layui框架的影响&#xff0c;实际根据你自己的框架来小调下就可以 效果如下 上代码 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-wi…

C# 实现 10 位纯数字随机数

本文将介绍如何用 C# 实现一个生成 10 位纯数字随机数的功能。以下是完整的代码示例&#xff1a; using System; using System.Collections.Generic; using System.Linq; using System.Text;namespace RandomTset {class Program{// 使用GUID作为种子来创建随机数生成器static…

MAVEN--Maven的生命周期,pom.xml详解,Maven的高级特性(模块化、聚合、依赖管理)

目录 &#xff08;一&#xff09;Maven的生命周期 1.Maven的三套生命周期 2.Maven常用命令 &#xff08;二&#xff09;pom.xml详解 &#xff08;三&#xff09;Maven的高级特性&#xff08;模块化、聚合、依赖管理&#xff09; 1.Maven的依赖范围 2.版本维护 3.依赖传…