当前位置: 首页 > 科技观察

计算机最神奇的地方在于它可以感知你的想法

时间:2023-03-21 18:22:06 科技观察

我们之前的文章提到了操作系统的三个抽象,分别是进程、地址空间和文件。此外,操作系统还控制着所有的I/O设备。操作系统必须向设备发送命令、捕获中断并处理错误。它还应该在设备和操作系统的其余部分之间提供一个易于使用的界面。操作系统如何管理I/O是我们的下一个重点。不同的人对I/O硬件的理解不同。对于电子工程师来说,I/O硬件就是构成硬件的芯片、电线、电源和其他物理设备。在我们程序员眼里,I/O其实就是硬件提供给软件的接口,比如硬件接收到的命令,执行的操作,反馈的错误等。我们专注于如何对硬件进行编程,而不是它如何工作。I/O设备什么是I/O设备?I/O设备,也称为输入/输出设备,是人类用来与计算机通信的外部硬件。输入/输出设备能够向计算机发送数据(输出)并从计算机接收数据(输入)。I/O设备可以分为块设备和字符设备两种。块设备块设备是一种能够以固定大小的块存储信息的设备,它支持以固定大小的块、扇区或簇读取和(可选)写入数据。每个块都有自己的物理地址。通常块大小在512-65536之间。所有传输的信息将在连续的块中。块设备的基本特点是每个块都是相对对立的,可以独立读写。常见的块设备包括硬盘、蓝光光盘和USB驱动器。与字符设备相比,块设备通常需要更少的引脚。块设备的缺点基于给定固态存储器的块设备比基于相同类型存储器的字节寻址要慢一些,因为读取或写入必须从块的开头开始。因此,要读取块的任何部分,必须查找到块的开头,读取整个块,如果不使用则丢弃该块。要写入块的一部分,必须查找到块的开头,将整个块读入内存,修改数据,再次查找到块的开头,然后将整个块写回设备。字符设备另一种类型的I/O设备是字符设备。字符设备以字符为单位发送或接收字符流,与任何块结构无关。字符设备不可寻址,也没有任何查找操作。常见的字符设备有打印机、网络设备、鼠标和磁盘以外的大多数设备。一些常见设备的数据速率如下所示。设备控制器首先需要了解设备控制器的概念。设备控制器是处理传入和传出信号到CPU的系统。设备通过插头和插座连接到计算机,插座连接到设备控制器。设备控制器从连接的设备接收数据并将其存储在控制器内部称为本地缓冲区的专用寄存器中。?专用寄存器,顾名思义,就是专为一项任务而设计的寄存器。例如,cs、ds、gs和其他段寄存器是专用寄存器,因为它们的存在是为了保存段号。eax、ecx等是通用寄存器,因为您可以不受限制地使用它们。例如,您不能移动ds,但可以移动eax、ebx。通用寄存器如:eax、ecx、edx、ebx、esi、edi、ebp、esp专用寄存器如:cs、ds、ss、es、fs、gs、eip、flag”每个设备控制器都会有一个应用程序与之对应,设备控制器通过应用程序的接口通过中断与操作系统进行通信。设备控制器是硬件,设备驱动程序是软件。I/O设备通常由机械部件和电子元件元件(electroniccomponent)电子元件称为设备控制器(devicecontrollers)或适配器(adapters),在个人电脑上,通常使用主板上可插入(PCIe)扩展槽的芯片或印刷电路卡机械装置本身,它由控制器卡组成,通常会有一个连接器,可以将通往设备本身的电缆插入其中,许多控制器可以运行2、4设置8相同设备。控制器和设备之间的接口通常是低级接口。例如,磁盘可能被格式化为2,000,000个扇区,每个扇区512字节。然而,实际上从驱动器出来的是一个串行比特流,从一个前导码开始,然后是一个扇区中的4096位,最后是校验和或ECC(错误代码,Error-CorrectingCode)。序言是在磁盘格式化的时候写的。它包括柱面号和扇区号、扇区大小和类似数据,还包含同步信息。控制器的任务是将串行比特流转换成字节块。并进行必要的纠错。字节块通常在控制器内部的缓冲区中逐位组装,校验和无误后,将字节块复制到内存中。内存映射I/O每个控制器都有几个用于与CPU通信的寄存器。通过写入这些寄存器,操作系统可以命令设备发送数据、接收数据、打开或关闭设备等。通过读取这些寄存器的信息,操作系统可以知道设备的状态,是否是准备好接受新命令等。为了控制寄存器,很多设备都会有一个数据缓冲区(databuffer)供系统读写。例如,在屏幕上显示像素的传统方法是使用视频RAM,它基本上只是一个数据缓冲区,程序和操作系统将数据写入其中。那么问题来了,CPU是如何与设备寄存器和设备数据缓冲区进行通信的呢?有两种可选方式。在第一种方法中,每个控制寄存器都分配了一个I/O端口号,它是一个8位或16位整数。所有I/O端口的集合形成一个受保护的I/O端口空间,普通用户程序无法访问它(只有操作系统可以)。使用特殊的I/O指令,如INREG,PORTCPU可以读取控制寄存器PORT的内容并将结果放入CPU寄存器REG。同样,使用OUTPORT,REGCPU可以将REG的内容写入控制寄存器。大多数早期计算机,包括几乎所有的大型机,如IBM360及其所有后继者,都是以这种方式工作的。?控制寄存器是处理器寄存器,它改变或控制CPU或其他数字设备的一般行为。控制寄存器执行的常见任务包括中断控制、切换寻址模式、分页控制和协处理器控制。》在这个方案中,内存地址空间和I/O地址空间是不同的,如下图,指令INR0,4和MOVR0,4的设计完全不同,前者读取I/的内容Oport4andputitintoR0,它读取内存字4的内容并放入R0。这些例子中的4代表不同且无关的地址空间。第二种方法是PDP-11引入的,?WhatisPDP-11?”它将所有控制寄存器映射到内存空间,如下图所示。内存映射I/O是在CPU和它连接的外设之间一种交换数据和指令的方式,处理器和IO设备在同一内存位置共享内存,即处理器和IO设备使用内存地址进行映射。在大多数系统中,分配给控制寄存器位于地址顶部或附近。这是一种混合方法,它具有带内存映射I/O的数据缓冲区和带独立I/O端口的控制寄存器。x86使用此体系结构。在IBMPC兼容机中,在除了I/O端口0到64K-1之外,640K到1M-1的内存地址都预留给了设备的数据缓冲区,这些方案是如何工作的呢?当CPU要读入一个字时,是否它是从内存或I/O端口读取的,它必须把所需的地址在总线地址线上,然后在总线的控制线上调用READ信号。还有第二条信号线表示是否需要I/O空间或内存空间。如果是内存空间,内存会响应请求。如果是I/O空间,那么I/O设备就会响应请求。如果只有内存空间,那么每个内存模块和每个I/O设备都会将地址线与其服务的地址范围进行比较。如果地址落在这个范围内,它就会响应请求。一个地址绝对不可能分配给Memory分配给I/O设备,所以不存在歧义和冲突。内存映射I/O的优缺点这两种寻址控制器的方案各有优缺点。我们先来看看内存映射I/O的优势。首先,如果需要特殊的I/O指令来读写设备控制寄存器,那么访问这些寄存器就需要使用汇编代码,因为在C或C++中没有办法执行IN和OUT指令。调用这样的过程增加了在内存映射I/O中,控制寄存器只是内存中的一个变量,可以像C中的任何其他变量一样对其进行寻址。其次,对于内存映射I/O,不需要特殊的保护机制to可以阻止用户进程进行I/O操作。操作系统需要保证的是,禁止将控制寄存器的地址空间放在用户的虚拟地址中。第三,对于内存映射I/O,可以参考各个内存指令,也可以参考控制寄存器,方便参考。在计算机设计中,几乎所有事情都需要权衡。内存映射I/O也是如此,它有其自身的缺点。首先,今天的大多数计算机都有某种存储字的高速缓存。缓存设备控制寄存器的成本很高。为了避免这种内存映射I/O的情况,硬件必须有选择地禁用缓存,例如,禁用每个页面上的缓存。此功能为硬件和操作系统增加了额外的复杂性,因此必须有选择地进行管理。其次,如果只有一个地址空间,那么所有内存模块和所有I/O设备都必须检查所有内存引用以确定谁应该响应。?什么是内存模块?在计算中,内存模块是一块印刷电路板,上面安装了内存集成电路。“如果计算机采用单总线架构,那么让每个内存模块和I/O设备查看每个地址就很简单了,如下图所示。但是,现代个人计算机的趋势是朝着专用高速memorybuss,这种总线如下图所示配备,以优化内存访问速度。x86系统也可以有多种总线(内存,PCIe,SCSI和USB)。在内存上使用单独的内存总线的麻烦之一-mappedmachine如下图所示,问题是I/O设备无法通过内存总线看到内存地址,因此无法响应,另外,必须采取特殊措施才能使内存映射I/O工作在有多个总线的系统上。一种可能的方法是先将所有内存引用发送到内存,如果内存无法响应,CPU会尝试另一条总线。第二种设计是在内存总线上放置一个探测设备,让去所有指向I/O的潜在地址感兴趣的设备。这里的问题是I/O设备可能无法像内存一样快地处理请求。第三种可能的设计是内存控制器中的地址过滤,类似于上图中描述的匹配设计。在这种情况下,内存控制器芯片包含在启动时预加载的范围寄存器。这种设计的缺点是它需要在启动时确定哪些内存地址而不是真正的内存地址。因此,每一种设计都有赞成和反对的说法,妥协和取舍在所难免。直接内存访问无论CPU是否具有内存映射I/O,它都需要寻址设备控制器以便与它们交换数据。一个CPU可以从I/O控制器每次请求一个字节的数据,但是这样做会浪费CPU时间,所以经常使用一种叫做DirectMemoryAccess的方案。为简单起见,我们假设CPU通过一条连接CPU、内存和I/O设备的系统总线访问所有设备和内存,如下图所示。现代操作系统其实更复杂,但原理是一样的。如果硬件有DMA控制器,操作系统只能使用DMA。有时这个控制器被集成到磁盘控制器和其他控制器中,但这种设计需要在每个设备上有一个单独的DMA控制器。单个DMA控制器可用于传输到多个设备,通常是同时传输。不管DMA控制器的物理地址如何,它都可以独立于CPU访问系统总线,如上图所示。它包含几个可由CPU读写的寄存器,包括内存地址寄存器、字节计数寄存器和一个或多个控制寄存器。控制寄存器指定使用哪个I/O端口、传输方向(从I/O设备读取或写入)、传输单位(一次一个字节或一次一个字),以及突发传输要传输的字节数。为了解释DMA是如何工作的,让我们先看看如何在没有DMA的情况下进行磁盘读取。首先,控制器从磁盘驱动器中逐位串行读取一个块(一个或多个扇区),直到将整个信息块放入控制器的内部缓冲区。读取校验和以确保没有发生读取错误。然后控制器会产生一个中断,当操作系统开始运行时,它会重复地从控制器的缓冲区中读取块的信息,一次一个字节或一个字,并将其存储在内存中。DMA的工作原理当使用DMA时,过程会发生变化。首先,CPU通过设置寄存器对DMA控制器进行编程,这样DMA控制器就知道要将什么数据传输到哪里。DMA控制器还向磁盘控制器发出命令,指示它从磁盘读取数据到其内部缓冲区并检查校验和。当有效数据位于磁盘控制器的缓冲区中时,DMA可以开始。DMA控制器通过在总线上向磁盘控制器发出读取请求来启动DMA传输,这是第二步。这个读取请求就像任何其他读取请求一样,磁盘控制器不知道也不关心它是来自CPU还是DMA控制器。通常,要写入的内存地址在总线的地址线上,所以当磁盘控制器去匹配下一个字时,它知道该把那个字写到哪里。写入内存又是一个总线周期,也就是第三步。当写操作完成后,磁盘控制器在总线上向DMA控制器发送一个确认信号,这是第四步。DMA控制器然后递增内存地址并递减字节数。如果字节计数仍然大于0,则循环执行步骤2-4,直到字节计数变为0。此时,DMA控制器中断CPU并告诉它传输完成。当操作系统开始运行时,它不会将磁盘块复制到内存中,因为它已经存在了。不同的DMA控制器的复杂性差异很大。如上所述,最简单的DMA控制器一次处理一个传输。更复杂的情况是一次处理许多转账。这样的控制器内部有多组寄存器,每个通道一组。每个字被传输后,DMA控制器决定下一个要服务的设备。DMA控制器可以设置为使用循环算法,或者它可以具有优先级规划设计,以便某些设备比其他设备受到更多关注。如果有明确的方法来解析确认信号,则可以同时挂起对不同设备控制器的多个请求。许多总线能够以两种模式运行:一次一个字模式和块模式。一些DMA控制器也能够以两种方式运行。在前一种模式下,DMA控制器请求一个要传输的字并获取该字。如果CPU要使用总线,它必须等待。一个设备可能会潜入并从CPU窃取一个总线周期,从而稍微延迟CPU。这种机制称为周期窃取。在块模式下,DMA控制器告诉设备获取总线,执行一系列传输操作,然后释放总线。这种操作形式称为突发模式。这种模式比周期窃取更有效,因为获取总线需要时间,而获取总线的代价是多个字可以同时传输。缺点是如果此时进行长时间的突发传输,可能会导致CPU等设备长时间阻塞。在我们讨论的模型中,有时称为fly-by模式,DMA控制器告诉设备控制器将数据直接传递到内存。一些DMA控制器使用的另一种模式是让设备控制器将字发送到DMA控制器,然后发出第二个总线请求以尽可能写入字。采用这种方案,每个传输的字都需要一个额外的总线周期,但更灵活,因为它还可以进行设备到设备的复制,甚至内存到内存的复制(通过预先读取内存,然后写入内存).大多数DMA控制器使用物理地址进行传输。使用物理地址需要操作系统将目标内存缓冲区的虚拟地址转换为物理地址,并将该物理地址写入DMA控制器的地址寄存器。另一种解决方案是一些DMA控制器将虚拟地址写入DMA控制器。然后DMA控制器必须使用MMU来完成虚拟到物理的转换。只有当MMU是内存的一部分而不是CPU的一部分时,才可能将虚拟地址放在总线上。重温中断在个人计算机体系结构中,中断结构看起来是这样的:当一个I/O设备完成其工作时,它会产生一个中断(操作系统默认启用中断),该中断通过总线上声明分配的信号为了达成这个。主板上的中断控制器芯片会检测到这个信号,然后执行中断操作。如果在中断之前没有其他中断操作被阻塞,中断控制器将立即处理该中断。如果在中断之前还有其他中断操作正在执行,或者其他设备发出了更高级别的中断信号,那么这个设备就暂时不会被处理。在这种情况下,设备将继续在总线上断言中断,直到CPU提供服务为止。为了处理中断,中断控制器在地址行上放置一个数字,指定要关注的设备并断言中断CPU的信号。中断信号会导致CPU停止当前正在执行的操作并开始执行其他操作。地址线上会有一个中断向量表的索引,用于获取下一个程序计数器。这个新获取的程序计数器也意味着程序即将启动,它会指向程序的开头。一般来说,陷阱和中断在这一点上使用相同的机制,并且经常共享相同的中断向量。中断向量的位置可以硬连线到机器中,或者它可以在内存中的任何地方,CPU寄存器指向它的起点。中断服务程序开始运行后,中断服务程序通过向中断控制器的I/O端口写入一个值来确认中断。告诉它中断控制器可以发出另一个中断。通过延迟CPU的响应来实现多个中断同时到达CPU涉及竞争条件。一些较旧的计算机没有集中式中断控制器,通常每个设备都请求自己的中断。硬件通常在启动服务程序之前保存当前信息。需要保存什么信息,保存在什么地方,不同的CPU差别很大。不管其他信息是否保存,程序计数器都必须保存,这对所有CPU都是一样的,以便恢复被中断的进程。所有可见寄存器和大量内部寄存器也应保留。上面说到硬件要保存当前的信息,那么保存到哪里就是个问题了。一种选择是将其放入内部寄存器,操作系统可以在需要时读取这些内部寄存器。这种方法带来的问题是:设备在一段时间内无法响应,直到读取所有内部寄存器中存储的信息后才能恢复运行,以防止第二个内部寄存器改写内部寄存器的状态。第二种方式是将信息保存在堆栈上,这是大多数CPU使用的方式。但是,这种做法也有一个问题,因为使用的栈是未定义的,如果使用的是当前栈,很可能是用户进程的栈。堆栈指针甚至是不合法的,因此当硬件试图在它指向的地址处写入时,会导致致命错误。如果您正在使用内核堆栈,则堆栈指针很可能是合法的并指向固定页面。但是,切换到内核模式需要MMU上下文切换,并且可能会使缓存或TLB失效。静态或动态地重新加载这些东西会增加中断处理时间,浪费CPU时间。精确中断与不精确中断另一个问题是现代CPU是大量流水线,有时是超标量(内部并行)。在一些较旧的系统中,在执行每条指令后,微程序或硬件会检查挂起的中断。如果存在,程序计数器和PSW将被压入堆栈以开始中断序列。中断程序运行后,旧的PSW和程序计数器会被弹出堆栈,继续之前的流程。下面是一个流水线模型。当流水线满时发生中断会怎样?许多指令处于不同的执行阶段。当中断发生时,程序计数器的值可能无法正确反映已经执行的指令和尚未执行的指令。边界。事实上,许多指令可能会部分执行,不同的指令或多或少会完成。在这种情况下,程序计数器更有可能反映下一条要被取出并推入流水线的指令的地址,而不是执行单元刚刚处理的指令的地址。在超标量设计中,情况可能更糟。每条指令都可以分解成微操作,这些微操作可能会乱序执行,具体取决于内部资源(例如功能单元和寄存器)的可用性。当中断发生时,一些很久以前开始的指令可能还没有开始执行,而最近执行的指令可能即将完成。中断信号发出时可能有很多条指令处于不同的完成状态,与程序计数器关系不大。使机器保持良好状态的中断称为精确中断。这样的中断有四个属性:PC(程序计数器)被保存在一个已知的地方PC指向的指令之前的所有指令都已经完全执行PC指向的指令之后的所有指令都没有被执行执行状态众所周知。不满足上述要求的中断称为不精确中断。不精确的中断令人头疼。上图描述了不精确中断的现象。指令的执行时间和完成是不确定的并且恢复起来很麻烦。相关链接https://pineight.com/ds/block/https://www.computerhope.com/jargon/i/iodevice.htmhttps://en.wikipedia.org/wiki/Memory_module《现代操作系统》第4版https:///en.wikipedia.org/wiki/Preamblehttps://en.wikipedia.org/wiki/Word_(computer_architecture)