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

说了这么多次 I-O,可你知道其中的原理么

时间:2023-03-20 21:03:25 科技观察

I/O讲了那么多次,但是你知道原理吗?1.IO软件原理I/O软件目标设备独立性现在让我们转向对I/O软件的研究。I/O软件设计的一个非常重要的目标是设备独立性。这意味着什么?这意味着我们可以编写访问任何设备的应用程序,而无需事先指定特定设备。例如,如果你写了一个可以从设备读取文件的应用程序,那么这个应用程序就可以从硬盘、DVD或USB读取,不需要为每个设备自定义应用程序。这实际上体现了设备独立性的概念。再比如,你可以输入如下命令sortinputandoutput,那么上面的输入可以从任何类型的磁盘或键盘接收,输出可以写入任何类型的磁盘或屏幕。计算机操作系统是这些硬件的媒介,因为不同的硬件有不同的指令序列,所以需要操作系统在指令之间进行转换。与设备独立性密切相关的一个指标是统一命名。设备的代号应该是一个整数或一个字符串,它们不应该依赖于具体的设备。在UNIX中,所有的磁盘都可以集成到文件系统中,所以用户不需要记住每个设备的具体名称,只需要记住对应的路径即可。如果记不住路径,也可以通过ls等命令查找具体集成位置。比如U盘挂载在/usr/cxuan/backup下,那么你把文件复制到/usr/cxuan/backup/device下,相当于把文件复制到U盘里。通过这种方式,向任意磁盘写入一个文件,就相当于输出一个文件到指定路径。错误处理除了设备独立性之外,I/O软件实现的第二个重要目标是错误处理。通常,应该在硬件级别处理错误。如果设备控制器检测到读取错误,它会尽力修复错误。如果设备控制器不能处理这个问题,那么设备驱动程序应该处理它。设备驱动程序将尝试再次读取操作。许多错误是偶然的。如果设备驱动程序不能处理这个错误,它会抛出这个错误,直到在硬件层(上层)进行处理。在很多情况下,上层不需要知道下层是如何解决错误的。这很像项目经理不必告诉老板每一个决定;程序员不必告诉项目经理如何编写每一行代码。这种做法不够透明。同步和异步传输I/O软件实现的第三个目标是同步(synchronous)和异步(asynchronous,即中断驱动)传输。这里先说说什么是同步什么是异步。同步传输中的数据通常以块或帧的形式发送。发送方和接收方在数据传输之前应该有同步的时钟。在异步传输中,数据通常以字节或字符的形式发送。异步传输不需要同步时钟,只是在传输前给数据加上一个奇偶校验位。下面是同步和异步的主要区别回到主题。大多数物理IO(physicalI/O)都是异步的。物理I/O中的CPU非常聪明。CPU传输完成后,就会转而去做其他的事情。它与中断通信,直到中断发生时CPU才会返回传输。I/O有两种类型:物理I/O和逻辑I/O(逻辑I/O)。物理I/O通常是从磁盘等存储设备实际获取数据。逻辑I/O正在将数据提取到内存(块、缓冲区)。缓冲I/O软件的最后一个问题是缓冲。通常,从一个设备发送的数据不会直接到达最终设备。在此期间,它会经过一系列的验证、检查、缓冲等操作才能到达。例如,从网络发送的数据包在首先到达缓冲区之前将经过一系列检查,从而消除缓冲区填充率和缓冲区过载。共享和独占I/O软件提出的最后一个问题是共享设备与独占设备的问题。一些I/O设备可以由许多用户共享。有些设备,比如磁盘,一般来说多个用户使用是没有问题的,但是有些设备就必须是独占的,也就是只允许一个用户使用,其他用户才能使用。接下来,我们就来探讨一下如何使用程序来控制I/O设备。控制I/O设备的方法有3种:使用程控I/O、使用中断驱动I/O、使用DMA驱动I/O、使用程控I/O、使用程控I/O,也称为可编程I/O,是指CPU在驱动软件的控制下发起的数据传输,用于访问设备上的寄存器或其他内存。CPU发出命令,然后等待I/O操作完成。由于CPU比I/O模块快得多,可编程I/O的问题是CPU必须等待很长时间才能得到结果。当CPU在等待的时候,会使用轮询或者忙等待。结果,整个系统的性能严重下降。可编程I/O非常简单,如果需要等待的时间很短,可编程I/O是一个很好的方式。可编程I/O经历以下操作。CPU请求I/O操作。I/O模块执行响应。I/O模块设置状态位。CPU定期检查状态位。I/O不会直接通知CPU操作完成。不会中断CPUCPU在后续过程中可能会等待或返回使用中断驱动的I/O针对可编程I/O的上述缺陷,我们提出了一个改进的解决方案,我们要等待的I/O设备而CPU,可以做其他事情,等到I/O设备完成后,就会产生一个中断,这个中断会停止当前进程,保存当前状态。一个可能的示意图如下虽然中断减轻了CPU和I/O设备等待时间的负担,但是在大数据传输中仍然效率低下,因为CPU和I/O设备之间也需要大量的逐字传输输入/输出模块。下面是中断的基本操作。CPU执行读取操作。I/O设备从外围设备获取数据。同时,CPU进行其他操作。I/O设备中断通知CPU。CPU请求数据。I/O模块传输数据。所以我们现在需要解决的是CPU和I/O模块之间数据传输的效率。使用DMA的I/ODMA中文名称是DirectMemoryAccess,意思是CPU授予I/O模块读写内存的权限,不涉及CPU。即DMA不需要CPU的参与。这个过程由称为DMA控制器(DMAC)的芯片管理。由于DMA设备可以直接在存储器之间传输数据,而不是使用CPU作为中介,因此可以缓解总线上的拥塞。DMA通过允许CPU在DMA系统跨系统和内存总线传输数据时执行任务来提高系统并发性。2.I/O层次结构I/O软件通常分为四个层次,它们的一般结构如下图所示。每一层及其上下层都有明确的功能和接口。让我们对计算机网络采取相反的方法,即自下而上地理解这些程序。下面是另一张图,这张图显示了I/O软件系统的所有层及其主要功能。接下来,我们详细讨论一下上面的分层中断处理程序。在计算机系统中,中断就像女人的脾气一样无时无刻不在产生,而且中断的发生往往是非常不愉快的。中断处理程序也称为中断服务例程或ISR(InterruptServiceRoutines),是最接近硬件的层。中断处理程序是由硬件中断、软件中断或软件异常产生的中断,用于实现设备驱动程序或受保护操作模式(如系统调用)之间的转换。中断处理程序负责处理中断发生时的所有操作,操作完成后阻塞,然后启动中断驱动程序解决阻塞。通常有3种通知方式,根据具体实现而定。在信号量实现中:用完信号量进行通知;monitor实现:对monitor中的条件变量进行信号操作。在某些情况下,无论哪种方式发送一些消息都是为了让被阻塞的中断处理程序恢复运行。中断处理方案有很多种,下面列举其中的一些《ARM System Developer’s GuideDesigning and Optimizing System Software》非嵌套中断处理程序按顺序处理每个中断,非嵌套中断处理程序也是最简单的中断处理程序嵌套中断处理程序会处理多个中断而不用分配优先权。可重入中断处理程序可以使用优先级处理多个中断。简单优先级中断处理程序可以处理简单的中断。标准优先级中断处理程序比低优先级中断处理程序更快。高优先级中断处理时间短高优先级中断处理程序能够在短时间内处理高优先级任务,并直接进入特定的服务程序。优先级组中断处理程序可以处理不同优先级的中断任务。以下是一些常见的中断处理程序步骤。不同操作系统的实现细节不同。保存所有未被中断硬件保存的寄存器,以设置中断服务程序的上下文。它可能包括设置TLB、MMU和页表。如果你不知道这三个概念,请参考另一篇文章。为中断服务程序设置堆栈以响应中断控制器。如果没有集中中断控制器,继续响应中断从保存它的地方复制寄存器到进程表并运行中断服务例程,从发出中断的设备控制器的寄存器和操作系统选择合适的进程运行。如果中断导致一些高优先级进程就绪,选择运行这些高优先级进程为进程设置MMU上下文,这也可能需要TLB,根据实际情况决定加载进程的寄存器,包括PSW寄存器。运行一个新的进程上面我们列出了一些粗略的中断步骤,不同的操作系统和中断处理程序可以处理的中断步骤和细节是不一样的,下面是一个嵌套中断设备驱动程序在上面的具体操作步骤在我们的文章中看到了设备控制器的作用。我们知道每个控制器都有内部寄存器来与设备通信、发送指令、读取设备的状态等。因此,每个连接到计算机的I/O设备都需要有一些设备特定的代码来控制它,对于例如,鼠标控制器需要接收鼠标的指令,告诉下一步移动到哪里,键盘控制器需要知道按下了哪个A键等。提供将I/O设备转换为设备的过程的代码控制器称为设备驱动程序。为了能够访问设备的硬件,实际上意味着设备驱动程序通常是操作系统内核的一部分,至少现在的架构是这样的。但也可以在用户空间构造设备驱动程序,通过系统调用完成读写操作。这避免了有问题的驱动程序可能会干扰内核,从而导致崩溃的问题。因此,在用户控件中实现设备驱动程序是衡量系统稳定性的一项非常有用的措施。MINIX3就是这样做的。MINI3的调用流程如下。但是,大多数桌面操作系统都要求驱动程序必须在内核中运行。操作系统通常将驱动程序分为字符设备和块设备。我们也介绍过,在UNIX系统中,操作系统是一个二进制程序,包含了所有需要编译进去的驱动程序。如果你想添加一个新的设备,需要重新编译内核并将新的驱动程序加载到二进制文件中。但是,随着大多数个人电脑的出现,由于I/O设备的广泛应用,上述静态编译方式不再有效。因此,从MS-DOS开始,操作系统在执行过程中转向动态加载驱动程序。系统。设备驱动程序具有许多功能,例如接受读写请求、初始化设备、管理电源和日志记录以及检查输入参数的有效性。设备驱动程序收到读写请求后,会检查当前设备是否在使用中。如果设备正在使用中,请求将被排队等待后续处理。如果此时设备空闲,驱动程序检查硬件看是否可以处理请求。在传输开始之前,启动设备或电机。等待设备就绪后再进行实际控制。控制设备就是向设备发出指令。发出命令后,设备控制器开始将它们写入控制器的设备寄存器。每条命令写入控制器后,检查控制器是否已接受命令并准备好接受下一条命令。一般的控制设备会发出一系列的指令,称为指令序列,设备控制器会依次检查每条指令是否被接受,是否可以接收到下一条指令,直到所有的指令序列发出。发出指令后,一般有两种情况可能出现。在大多数情况下,设备驱动程序将等待控制器完成其工作。这里需要了解一下设备控制器的概念。设备控制器的主要职责是控制一个或多个I/O设备,实现I/O设备与计算机之间的数据交换。设备控制器接收CPU发来的指令,进而达到控制硬件的目的。设备控制器是一个可寻址的设备。当它只控制一台设备时,它只有一个唯一的设备地址;如果设备控制器控制多个可连接设备,则应包含多个设备地址,并使每个设备地址对应一个设备。设备控制器主要分为字符设备和块设备两种。设备控制器的主要功能包括以下接收和识别命令:设备控制器可以接受来自CPU的指令并识别它们。设备控制器内部也会有寄存器来存放指令和参数,用于数据交换:CPU、控制器和设备之间会交换数据,CPU通过总线向控制器发送指令,或者从控制器中并行读取数据;控制器向指定设备写入数据。地址识别:每个硬件设备都有自己的地址,设备控制器可以识别这些不同的地址,从而达到控制硬件的目的。此外,为了让CPU向寄存器写入或读取数据,这些寄存器应该有一个唯一的地址。错误检测:设备控制器还具有检测设备传递过来的数据的功能。在这种情况下,设备控制器会阻塞,直到中断解除阻塞状态。还有一种情况可以不延迟地完成操作,所以驱动程序不需要阻塞。第一种情况,操作系统可能被中断唤醒;在第二种情况下,操作系统不会休眠。设备驱动程序必须是可重入的,因为设备驱动程序会阻塞并唤醒,然后再次阻塞。驱动程序不允许进行系统调用,但它们通常需要与内核的其余部分进行交互。设备无关的I/O软件I/O软件有两种,一种是基于我们上面介绍的特定设备,另一种是设备无关的,也就是不需要特定的设备。设备驱动程序和与设备无关的软件之间的界限取决于特定的系统。下面显示的功能由与设备无关的软件实现。设备无关软件的基本功能是对所有设备执行通用的I/O功能,并为用户级软件提供统一的接口。缓冲无论是块设备还是字符设备,缓冲都是一个非常重要的考虑因素。下面是从ADSL(调制解调器)读取数据的过程,ADSL是我们用来联网的设备。用户程序调用read系统调用阻塞用户进程等待字符的到来,这是一种处理传入字符的方法。每个传入字符都会导致中断。中断服务程序将向用户进程提供字符并解除阻塞。字符提供给用户程序后,进程将读取其他字符并继续阻塞。这个模型如下。这种情况下是没有buffer的,因为用户进程如果读不到数据就会阻塞,直到它读取到数据,这种情况效率比较低,阻塞的方式会直接阻止用户进程做其他事情,这是用户无法接受的。还有一种情况是每次都会重启用户进程,每一个字符的到来都要重启用户进程,这样会严重降低效率,所以没有buffer的软件不是一个好的设计。作为一种改进,我们可以尝试使用读取n字节缓冲区来读取用户空间中的n个字符。在这种情况下,中断服务程序会将字符放入缓冲区,直到缓冲区变满,然后唤醒用户进程。这个方案比上面的方案改进了很多。但这种解决方案也存在问题。当字符来了,调用bufferoutofmemory会怎样?解决方法是将buffer锁在内存中,但是这种方法也会出问题。如果少量的buffer被锁住也没关系。如果大量缓冲区被锁在内存中,可换入换出的页面就会缩小,导致系统性能下降。一种解决方案是在内核内部创建一个缓冲区,让中断服务程序将字符放入内核内部的缓冲区中。当内核中的缓冲区即将满时,会将用户空间中的页面调入内存,然后将内核空间中的缓冲区复制到用户空间中的缓冲区中。这种方案还面临一个问题,如果用户空间的页被交换到内存中。这时候内核空间的缓冲区已经满了。这个时候,还有新的人物登场。这时候会发生什么?因为缓冲区已满,没有空间存储新字符。一个非常简单的方法是设置另一个缓冲区。第一个缓冲区填满后,缓冲区清空前,使用第二个缓冲区。这个解决方案如下,当第二个缓冲区也满了的时候,也把数据复制到用户空间,然后用第一个缓冲区来接受新的字符。这种具有两个缓冲区的设计称为双缓冲。另一种缓冲形式是循环缓冲区。它由一个内存区和两个指针组成。指向可以放置新数据的下一个空闲字的指针。另一个指针指向缓冲区中尚未删除的数据的第一个字。在很多情况下,硬件在添加新数据时会移动第一个指针;操作系统在删除和处理无用数据时移动第二个指针。当两个指针到达顶部时,它们回到底部并重新开始。缓冲区对于输出也很重要。将输出描述为类似于输入缓冲技术被广泛使用,但它也有缺点。如果数据缓冲太多次,性能将会受到影响。例如,考虑以下情况。数据经过用户进程->内核空间->网络控制器。这里的网络控制器应该相当于一个socket缓冲区,然后发送到网络,再到接收方的网络控制器->接收方的内核缓冲区->接收方的用户缓冲区,一个数据包缓冲太多次,很容易以降低性能。错误处理错误在I/O中是一种非常正常的现象。当错误发生时,操作系统必须尽可能地处理它们。有些错误只由特定设备处理,有些错误由框架处理,这些错误与特定设备无关。I/O错误的一类是程序员的编程错误,比如在打开文件之前读取了流,或者没有关闭流导致内存溢出等。这类问题由程序员处理;另一种是实际的I/O错误,比如写数据到一个坏的磁盘块,怎么写都写不出来。这种问题是由驱动来处理的,如果驱动处理不了,就交给硬件来处理,就像我们上面说的。设备驱动统一接口我们在操作系统概述中提到,操作系统的一个很重要的功能就是屏蔽硬件和软件的差异,为硬件和软件提供统一的标准。这个标准还体现在设备驱动上,提供统一的接口,因为不同的硬件,厂商写的设备驱动是不一样的,所以如果每个驱动都单独提供一个接口,这是做不到的,所以必须要统一。分配和释放一些设备如打印机,只能由一个进程使用,这就需要操作系统根据实际情况判断是否可以检查该设备的请求,判断是否可以接受其他请求。一种比较简单直接的方法是对特殊文件进行打开操作。如果设备不可用,直接打开将失败。另一种方式是不直接导致失败,而是让它阻塞,等到另一个进程释放资源后再执行打开操作。这样就把选择权交给了用户,由用户自己判断是否应该等待。注意:有很多方法可以实现阻塞。有设备无关的块,例如阻塞队列。不同的磁盘会有不同的扇区大小,但软件不会关心扇区大小,只是存储它。一些字符设备可以一次传送一个字节的数据,而另一些则以更大的单位传送数据,这些差异也可以被隐藏起来。用户空间的I/O软件虽然大部分I/O软件都在内核结构中,但还是有一些I/O软件是在用户空间实现的,凡事没有绝对。一些I/O软件和库程序存在于用户空间,然后通过提供系统调用来实现。