OS- -I/O之I/O软件原理及层次结构

文章目录

  • OS- -I/O之I/O软件原理及层次结构
    • 一、I/O软件原理
      • 1.I/O软件目标
        • 设备独立性
        • 错误处理
        • 同步和异步传输
        • 缓冲
        • 共享和独占
      • 2.使用程序控制I/O
      • 3.使用中断驱动I/O
      • 4.使用DMA的I/O
    • 二、I/O层次结构
      • 1.中断处理程序
      • 2.设备驱动程序
      • 3.与设备无关的I/O软件
        • 缓冲
        • 错误处理
        • 设备驱动程序统一接口
        • 分配和释放
        • 设备无关的块
      • 4.用户空间的I/O软件

一、I/O软件原理

1.I/O软件目标

设备独立性

  • 现在让我们转向对I/O软件的研究,I/O软件设计一个很重要的目标就是设备独立性(device independence)。
  • 啥意思呢?这意味着我们能够编写访问任何设备的应用程序,而不用事先指定特定 的设备。
  • 比如你编写了一个能够从设备读入文件的应用程序,那么这个应用程序可以从硬盘、DVD或者USB进行读入,不必再为每个设备定制应用程序。
  • 这其实就体现了设备独立性的概念。

再比如说你可以输入一条下面的指令

sort 输入输出
  • 那么上面这个 输入 就可以接收来自任意类型的磁盘或者键盘,并且 输出 可以写入到任意类型的磁 盘或者屏幕。
  • 计算机操作系统是这些硬件的媒介,因为不同硬件它们的指令序列不同,所以需要操作系统来做指令间 的转换。
  • 设备独立性密切相关的一个指标就是统一命名(uniform naming) ,设备的代号应该是一个整数或 者是字符串,它们不应该依赖于具体的设备
  • 在UNIX中,所有的磁盘都能够被集成到文件系统中,所 以用户不用记住每个设备的具体名称,直接记住对应的路径即可,如果路径记不住,也可以通过Is 等指令找到具体的集成位置。
  • 举个例子来说,比如一个USB磁盘被挂载到了 /usr/cxuan/backup下,那么你把文件复制到/usr/cxuan/backup/device下,就相当于是把文件复制到了磁盘中,通
    过这种方式,实现了向任何磁盘写入文件都相当于是向指定的路径输出文件。

错误处理

  • 除了设备独立性外,I/O软件实现的第二个重要的目标就是错误处理(error handling)
  • 通常情况 下来说,错误应该交给硬件层面去处理。如果设备控制器发现了读错误的话,它会尽可能的去修复这 个错误。如果设备控制器处理不了这个问题,那么设备驱动程序应该进行处理
  • 设备驱动程序会再次尝 试读取操作,很多错误都是偶然性的,如果设备驱动程序无法处理这个错误,才会把错误向上抛到硬件 层面(上层)进行处理,很多时候,上层并不需要知道下层是如何解决错误的。
  • 这就很像项目经理不用 把每个决定都告诉老板;程序员不用把每行代码如何写告诉项目经理。这种处理方式不够透明。

同步和异步传输

  • I/O软件实现的第三个目标就是同步(synchronous)和异步(asynchronous),即中断驱动)传输。
  • 这里先说一下同步和异步是怎么回事吧。
  • 同步传输中数据通常以块或帧的形式发送。
  • 发送方和接收方在数据传输之前应该具有同步时钟。
  • 而在异步传输中,数据通常以字节或者字符的形式发送,异步传输则不需要同步时钟,但是会在传输之前向 数据添加奇偶校验位。

下面是同步和异步的主要区别:

  • 大部分物理IO(physical I/O)是异步的。物理I/O中的CPU是很聪明的,CPU传输完成后会转而做其他事情,它和中断心灵相通,等到中断发生后,CPU才会回到传输这件事情上来。
  • I/O分为两种:物理I/O和 逻辑I/O(Logical I/O)
  • 物理I/O通常是从磁盘等存储设备实际获取数据。逻辑I/O是对存储器(块,缓冲区)获取数 据。

缓冲

  • I/O软件的最后一个问题是缓冲(buffering)。
  • 通常情况下,从一个设备发出的数据不会直接到达最 后的设备。其间会经过一系列的校验、检查、缓冲等操作才能到达
  • 举个例子来说,从网络上发送一个 数据包,会经过一系列检查之后首先到达缓冲区,从而消除缓冲区填满速率和缓冲区过载。

共享和独占

  • I/O软件引起的最后一个问题就是共享设备和独占设备的问题
  • 有些I/O设备能够被许多用户共同使 用。一些设备比如磁盘,让多个用户使用一般不会产生什么问题,但是某些设备必须具有独占性,即只 允许单个用户使用完成后才能让其他用户使用。
  • 下面,我们来探讨一下如何使用程序来控制I/O设备。一共有三种控制I/O设备的方法
  • •使用程序控制I/O
  • •使用中断驱动I/O
  • •使用DMA驱动I/O

2.使用程序控制I/O

  • 使用程序控制I/O又被称为 可编程I/O ,它是指由CPU在驱动程序软件控制下启动的数据传输,来访问设备上的寄存器或者其他存储器
  • CPU会发出命令,然后等待I/O操作的完成。由于CPU的速度 比I/O模块的速度快很多,因此可编程I/O的问题在于,CPU必须等待很长时间才能等到处理结果
  • CPU在等待时会采用轮询(polling)或者 忙等(busy waiting)的方式,结果,整个系统的性能 被严重拉低。可编程I/O十分简单,如果需要等待的时间非常短的话,可编程I/O倒是一个很好的方 式
  • 一个可编程的I/O会经历如下操作
  • •CPU请求I/O操作
  • •I/O模块执行响应
  • •I/O模块设置状态位
  • •CPU会定期检查状态位
  • •I/O不会直接通知CPU操作完成
  • •I/O也不会中断CPU
  • •CPU可能会等待或在随后的过程中返回

3.使用中断驱动I/O

  • 鉴于上面可编程I/O的缺陷,我们提出一种改良方案,我们想要在CPU等待I/O设备的同时,能够做其他事情,等到I/O设备完成后,它就会产生一个中断,这个中断会停止当前进程并保存当前的状态

一个可能的示意图如下:

  • 尽管中断减轻了 CPU和I/O设备的等待时间的负担,但是由于还需要在CPU和I/O模块之前进行大量的逐字传输,因此在大量数据传输中效率仍然很低
  • 下面是中断的基本操作
  • •CPU进行读取操作
  • •I/O设备从外围设备获取数据,同时CPU执行其他操作
  • •I/O设备中断通知CPU
  • •CPU请求数据
  • •I/O模块传输数据

所以我们现在着手需要解决的就是CPU和I/O模块间数据传输的效率问题。

4.使用DMA的I/O

  • DMA的中文名称是直接内存访问,它意味着CPU授予I/O模块权限在不涉及CPU的情况下读取或写入内存。也就是DMA可以不需要CPU的参与。这个过程由称为DMA控制器(DMAC)的芯片管 理。
  • 由于DMA设备可以直接在内存之间传输数据,而不是使用CPU作为中介,因此可以缓解总线上 的拥塞。DMA通过允许CPU执行任务,同时DMA系统通过系统和内存总线传输数据来提高系统并发 性。

二、I/O层次结构

  • I/O软件通常组织成四个层次,它们的大致结构如下图所示
  • 每一层和其上下层都有明确的功能和接口。下面我们采用和计算机网络相反的套路,即自下而上的了解 一下这些程序。
  • 下面是另一幅图,这幅图显示了输入/输出软件系统所有层及其主要功能

1.中断处理程序

  • 在计算机系统中,中断就像女人的脾气一样无时无刻都在产生,中断的出现往往是让人很不爽的。
  • 中断 处理程序又被称为中断服务程序或者是ISR(Interrupt Service Routines),它是最靠近硬件的 一层。中断处理程序由硬件中断、软件中断或者是软件异常启动产生的中断,用于实现设备驱动程序或 受保护的操作模式(例如系统调用)之间的转换。
  • 中断处理程序负责处理中断发生时的所有操作,操作完成后阻塞,然后启动中断驱动程序来解决阻塞
  • 通常会有三种通知方式,依赖于不同的具体实现:
  • •信号量实现中:在信号量上使用up进行通知;
  • •管程实现:对管程中的条件变量执行signal操作
  • •还有一些情况是发送一些消息

不管哪种方式都是为了让阻塞的中断处理程序恢复运行。

  • 中断处理方案有很多种,下面是一些方案:
  • •非嵌套的中断处理程序按照顺序处理各个中断,非嵌套的中断处理程序也是最简单的中断处理
  • •嵌套的中断处理程序会处理多个中断而无需分配优先级
  • •可重入的中断处理程序可使用优先级处理多个中断
  • •简单优先级中断处理程序可处理简单的中断
  • •标准优先级中断处理程序比低优先级的中断处理程序在更短的时间能够处理优先级更高的中断
  • •高优先级中断处理程序在短时间能够处理优先级更高的任务,并直接进入特定的服务例程。
  • •优先级分组中断处理程序能够处理不同优先级的中断任务
  • 下面是一些通用的中断处理程序的步骤,不同的操作系统实现细节不一样
  • •保存所有没有被中断硬件保存的寄存器
  • •为中断服务程序设置上下文环境,可能包括设置TLB、MMU和页表
  • •为中断服务程序设置栈
  • •对中断控制器作出响应,如果不存在集中的中断控制器,则继续响应中断
  • •把寄存器从保存它的地方拷贝到进程表中
  • •运行中断服务程序,它会从发出中断的设备控制器的寄存器中提取信息
  • •操作系统会选择一个合适的进程来运行。如果中断造成了一些优先级更高的进程变为就绪态,则选 择运行这些优先级高的进程
  • •为进程设置MMU±下文,可能也会需要TLB,根据实际情况决定
  • •加载进程的寄存器,包括PSW寄存器
  • •开始运行新的进程

上面我们罗列了一些大致的中断步骤,不同性质的操作系统和中断处理程序能够处理的中断步骤和细节也不尽相同,下面是一个嵌套中断的具体运行步骤:

2.设备驱动程序

  • 在上面我们知道了设备控制器所做的工作。我们知道每个控制器其内部都会有寄存器用来和设 备进行沟通,发送指令,读取设备的状态等
  • 因此,每个连接到计算机的I/O设备都需要有某些特定设备的代码对其进行控制,例如鼠标控制器需要 从鼠标接受指令,告诉下一步应该移动到哪里,键盘控制器需要知道哪个按键被按下等。这些提供I/O 设备到设备控制器转换的过程的代码称为 设备驱动程序(Device driver)
  • 为了能够访问设备的硬件,实际上也就意味着,设备驱动程序通常是操作系统内核的一部分,至少现在 的体系结构是这样的。
  • 但是也可以构造用户空间的设备驱动程序,通过系统调用来完成读写操作。这 样就避免了一个问题,有问题的驱动程序会干扰内核,从而造成崩溃。
  • 所以,在用户控件实现设备驱动 程序是构造系统稳定性一个非常有用的措施。MINIX 3就是这么做的。

下面是MINI 3的调用过程:

  • 然而,大多数桌面操作系统要求驱动程序必须运行在内核中。操作系统通常会将驱动程序归为字符设备和块设备
  • 块设备是一个能存储固定大小块信息的设备,它支持以固定大小的块,扇区或群集读取和(可选)写入数据。
  • 每个块都有自己的物理地址。
  • 通常块的大小在512 - 65536之间。
  • 所有传输的信息都会以连续的块为单位。块设备的基本特征是每个块都较为对立,能够独立的进行读写。
  • 常见的块设备有硬盘、蓝光光盘、USB盘
  • 另一类I/O设备是字符设备。
  • 字符设备以字符为单位发送或接收一个字符流,而不考虑任何块结构。
  • 字符设备是不可寻址的,也没有任何寻道操作。
  • 常见的字符设备有打印机、网络设备、鼠标、以及大多数与磁盘不同的设备。
  • 在UNIX系统中,操作系统是一个二进制程序,包含需要编译到其内部的所有驱动程序,如果你要对 UNIX添加一个新设备,需要重新编译内核,将新的驱动程序装到二进制程序中。
  • 然而随着大多数个人计算机的出现,由于I/O设备的广泛应用,上面这种静态编译的方式不再有效,因 此,从MS-DOS开始,操作系统转向驱动程序在执行期间动态的装载到系统中。
  • 设备驱动程序具有很多功能,比如接受读写请求,对设备进行初始化、管理电源和日志、对输入参数进 行有效性检查等
  • 设备驱动程序接受到读写请求后,会检查当前设备是否在使用,如果设备在使用,请求被排入队列中, 等待后续的处理。如果此时设备是空闲的,驱动程序会检查硬件以了解请求是否能够被处理。
  • 在传输开 始前,会启动设备或者马达。等待设备就绪完成,再进行实际的控制。控制设备就是对设备发出指令
  • 发出命令后,设备控制器便开始将它们写入控制器的设备寄存器。在将每个命令写入控制器后,会检 查控制器是否接受了这条命令并准备接受下一个命令。
  • 一般控制设备会发出一系列的指令,这称为指令 序列,设备控制器会依次检查每个命令是否被接受,下一条指令是否能够被接收,直到所有的序列发出 为止
  • 发出指令后,一般会有两种可能出现的情况。在大多数情况下,设备驱动程序会进行等待直到控制器完 成它的事情
  • 这里需要了解一下设备控制器的概念
  • 设备控制器的主要主责是控制一个或多个I/O设备,以实现I/O设备和计算机之间的数据交换
  • 设备控制器接收从CPU发送过来的指令,继而达到控制硬件的目的
  • 设备控制器是一个可编址的设备,当它仅控制一个设备时,它只有一个唯一的设备地址;
    如果设备控制器控制多个可连接设备时,则应含有多个设备地址,并使每一个设备地址对应一个设备。
  • 设备控制器主要分为两种:字符设备和块设备
  • 设备控制器的主要功能有下面这些:
  • 接收和识别命令:设备控制器可以接受来自CPU的指令,并进行识别。设备控制器内部也会有寄存器,用来存放指令和参数
  • 进行数据交换:CPU.控制器和设备之间会进行数据的交换,CPU通过总线把指令发送给控制器,或从控制器中并行地读出数据;控制器将数据写入指定设备。
  • 地址识别:每个硬件设备都有自己的地址,设备控制器能够识别这些不同的地址,来达到控制硬件的目的,此外,为使CPU能向寄存器中写入或者读取数据,这些寄存器都应具有唯一的地址
  • 差错检测:设备控制器还具有对设备传递过来的数据进行检测的功能
  • 在这种情况下,设备控制器会阻塞,直到中断来解除阻塞状态
  • 还有一种情况是操作是可以无延迟的完 成,所以驱动程序不需要阻塞。在第一种情况下,操作系统可能被中断唤醒;第二种情况下操作系统不 会被休眠。
  • 设备驱动程序必须是可重入的,因为设备驱动程序会阻塞和唤醒然后再次阻塞。驱动程序不允许进行 系统调用,但是它们通常需要与内核的其余部分进行交互

3.与设备无关的I/O软件

  • I/O软件有两种,一种是我们上面介绍过的基于特定设备的,还有一种是设备无关性的,设备无关性也 就是不需要特定的设备
  • 设备驱动程序与设备无关的软件之间的界限取决于具体的系统。

下面显示的功 能由设备无关的软件实现:

  • 与设备无关的软件的基本功能是对所有设备执行公共的I/O功能,并且向用户层软件提供一个统一的接 口

缓冲

无论是对于块设备还是字符设备来说,缓冲都是一个非常重要的考量标准

  • 下面是从ADSL(调制解调 器)读取数据的过程,调制解调器是我们用来联网的设备。
  • 用户程序调用read系统调用阻塞用户进程,等待字符的到来,这是对到来的字符进行处理的一种方 式。每一个到来的字符都会造成中断
  • 中断服务程序会给用户进程提供字符,并解除阻塞。将字符提 供给用户程序后,进程会去读取其他字符并继续阻塞,这种模型如下:
  • 这一种方案是没有缓冲区的存在,因为用户进程如果读不到数据会阻塞,直到读到数据为止,这种情况 效率比较低,而且阻塞式的方式,会直接阻止用户进程做其他事情,这对用户来说是不能接受的
  • 还有 一种情况就是每次用户进程都会重启,对于每个字符的到来都会重启用户进程,这种效率会严重降低, 所以无缓冲区的软件不是一个很好的设计。
  • 作为一个改良点,我们可以尝试在用户空间中使用一个能读取n个字节缓冲区来读取n个字符。这样 的话,中断服务程序会把字符放到缓冲区中直到缓冲区变满为止,然后再去唤醒用户进程 这种方案要 比上面的方案改良很多。
  • 但是这种方案也存在问题,当字符到来时,如果缓冲区被调出内存会出现什么问题
  • 解决方案是把缓冲 区锁定在内存中,但是这种方案也会出现问题,如果少量的缓冲区被锁定还好,如果大量的缓冲区被锁 定在内存中,那么可以换进换出的页面就会收缩,造成系统性能的下降。
  • 一种解决方案是在内核中内部创建一块缓冲区,让中断服务程序将字符放在内核内部的缓冲区中
  • 当内核中的缓冲区要满的时候,会将用户空间中的页面调入内存,然后将内核空间的缓冲区复制到用户 空间的缓冲区中
  • 这种方案也面临一个问题就是假如用户空间的页面被换入内存,此时内核空间的缓冲 区已满,这时候仍有新的字符到来,这个时候会怎么办?因为缓冲区满了,没有空间来存储新的字符 了。
  • 一种非常简单的方式就是再设置一个缓冲区就行了,在第一个缓冲区填满后,在缓冲区清空前,使用第 二个缓冲区

这种解决方式如下:

  • 当第二个缓冲区也满了的时候,它也会把数据复制到用户空间中,然后第一个缓冲区用于接受新的字 符。这种具有两个缓冲区的设计被称为双缓冲(double buffering)。
  • 还有一种缓冲形式是 循环缓冲(circular buffer) ,它由一个内存区域和两个指针组成。一个指针 指向下一个空闲字,新的数据可以放在此处。另外一个指针指向缓冲区中尚未删除数据的第一个字。
  • 在 许多情况下,硬件会在添加新的数据时,移动第一个指针;而操作系统会在删除和处理无用数据时会移 动第二个指针。两个指针到达顶部时就回到底部重新开始。
  • 缓冲区对输出来说也很重要。对输出的描述和输入相似缓冲技术应用广泛,但它也有缺点。如果数据被缓冲次数太多,会影响性能

考虑例如如下这种情况:

  • 数据经过用户进程-> 内核空间-> 网络控制器,这里的网络控制器应该就相当于是socket缓冲区,然 后发送到网络上,再到接收方的网络控制器-> 接收方的内核缓冲-> 接收方的用户缓冲,一条数据包被 缓存了太多次,很容易降低性能

错误处理

  • 在I/O中,出错是一种再正常不过的情况了。当出错发生时,操作系统必须尽可能处理这些错误。有一 些错误是只有特定的设备才能处理,有一些是由框架进行处理,这些错误和特定的设备无关
  • I/O错误的一类是程序员编程错误,比如还没有打开文件前就读流,或者不关闭流导致内存溢出等 等。这类问题由程序员处理;
  • 另外一类是实际的I/O错误,例如向一个磁盘坏块写入数据,无论怎么写 都写入不了。这类问题由驱动程序处理,驱动程序处理不了交给硬件处理,这个我们上面也说过。

设备驱动程序统一接口

  • 我们在操作系统概述中说到,操作系统一个非常重要的功能就是屏蔽了硬件和软件的差异性,为硬件和 软件提供了统一的标准,这个标准还体现在为设备驱动程序提供统一的接口,因为不同的硬件和厂商编 写的设备驱动程序不同,所以如果为每个驱动程序都单独提供接口的话,这样没法搞,所以必须统一。

分配和释放

  • 一些设备例如打印机,它只能由一个进程来使用,这就需要操作系统根据实际情况判断是否能够对设备 的请求进行检查,判断是否能够接受其他请求,一种比较简单直接的方式是在特殊文件上执行open 操作。
  • 如果设备不可用,那么直接open会导致失败。还有一种方式是不直接导致失败,而是让其阻 塞,等到另外一个进程释放资源后,在进行open打开操作。
  • 这种方式就把选择权交给了用户,由用户 判断是否应该等待。

注意:阻塞的实现有多种方式,有阻塞队列等

设备无关的块

  • 不同的磁盘会具有不同的扇区大小,但是软件不会关心扇区大小,只管存储就是了。一些字符设备可以 —次一个字节的交付数据,而其他的设备则以较大的单位交付数据,这些差异也可以隐藏起来。

4.用户空间的I/O软件

  • 虽然大部分I/O软件都在内核结构中,但是还有一些在用户空间实现的I/O软件,凡事没有绝对。一些 I/O软件和库过程在用户空间存在,然后以提供系统调用的方式实现。

OS- -I/O之I/O软件原理及层次结构相关推荐

  1. 【梅哥的Ring0湿润插入教程】第一课Windows内核/驱动编程概述及应用、商业驱动保护软件原理分析...

    [梅哥的Ring0湿润插入教程] Email:mlkui@163.com 转载请注明出处,谢绝喷子记者等,如引起各类不适请自觉滚J8蛋! 第一课Windows内核/驱动编程概述及应用. 商业驱动保护软 ...

  2. linux用c语言模拟抢票系统,C语言-抢火车票软件原理及笔记

    原标题:C语言-抢火车票软件原理及笔记 //今晚内容:C语言-抢火车票软件原理 //讲课老师:范志军 QQ:208824435 #include #include int a=50;//初始化50张火 ...

  3. mac vi快速删除_Mac OS X下应该如何卸载软件及mac终端命令大全(转)

    一.Mac OS X下应该如何卸载软件 Mac OS X的软件安装方式有很多种,而软件卸载的情况也很不同.在Mac OS X拆除软件往往不是把软件拉到废止篓里那么简单.通常情况下要具体问题具体分析.无 ...

  4. 借助nat123软件快速发布网站做网站服务,解决80端口被屏蔽被封,nat123软件原理分析

    概述 nat123软件主要是为没有公网IP或80端口被屏蔽被封的做网站用户而开放自由的平台软件. 使用nat123,可以在内网一步发布网站,做网站服务: 使用nat123,可以解决80端口被屏蔽被封的 ...

  5. 抢票软件原理_手机部应届生软件大赛 | SHOW MI YOUR CODE

    手机部软件开发大赛,倒计时30天! 2020年7月31日,风和日丽,在小米科技园阶梯教室,北京.南京.深圳.上海4地联动一起共同密谋了有趣的事情,为期2个月的手机部软件比赛线上线下一起开营啦! 来自相 ...

  6. Windows 技巧篇-电脑蓝光过滤,颜色校准调节蓝光,电脑源头过滤蓝光,保护眼睛,护眼软件原理

    蓝光的危害已经越来越被大家所知晓,很容易让眼睛疲惫. 无需买防护罩,只需在颜色校准调一下就能在源头过滤蓝光. 护眼软件的原理也是一样的. 下面为大家演示怎么设置: 第一步: 在"外观和个性化 ...

  7. WebServer 软件原理简介

    一. B/S结构  和 C/S结构简介 1.1 C/S结构 所谓C/S 就是 Client - Server , 在Client端有个界面程序.   User会通过这个Client端程序来读写  存放 ...

  8. Mac(OS X)使用brew安装软件

    在Linux下,常用的软件包管理工具有RedHat系的yum和Debian系的apk-get,对于Mac的OS X系统来说,同样有这样的形式的软件包管理工具,即:brew. brew 又叫Homebr ...

  9. seo模拟点击软件_百度快排软件原理分析

    很多朋友都在怀疑,一个新网站,没有几篇文章,排名却可以在百度首页,这是很不可思议的事情,对于大多数seo来说,白帽优化是没有捷径可走的,如果想要走捷径就是所谓的黑帽手法就要承担一定的风险. ​seo排 ...

最新文章

  1. Nature:麻省理工人造「巨型原子」问世,量子处理和量子通信合二为一
  2. div中的图像在图像下方有多余的空间
  3. 【leetcode】258. Add Digits
  4. javaee校园信息服务器,基于javaEE服务器 学生考勤管理系统的设计与实现开题报告...
  5. 网易云课堂计算机体系,计算机系统结构 (三) CPU及其结构分析
  6. 【转】mysql-status和variables区别
  7. 公司间采购的后台配置备忘录
  8. 【数据库实验课堂】实验一:数据库的管理
  9. python列表添加元组_python 列表与元组的操作简介
  10. linux jenkins自动部署,【linux】【jenkins】自动化部署一 安装jenkins
  11. java类 家族成员 姓氏_中国史上十大家族,占据中国九大姓,看看有没有你的姓氏...
  12. 2022年贵州大学计算机考研(初试+复试+实验室经验贴)
  13. 图像特征计算——纹理特征
  14. php 字典树,关于PHP字典树的定义与实现方法
  15. excel两列数据对比找不同_莫斯科地标百年前后对比图,快来“找不同”
  16. 【JavaEE】简单了解操作系统、进程内存管理
  17. 惊喜盲盒中奖iPhone 13已收到货
  18. 一加7t人脸识别_90Hz新品,一加7T系列国内发布日期官宣
  19. AutoDesk CAD如何彻底卸载/不影响二次安装
  20. C语言-结构体函数(录入书本的信息)

热门文章

  1. 【报错笔记】数据类型转换时报错:Request processing failed;nested exception is java.lang.NumberFormatException:...
  2. Java多线程之一:进程与线程
  3. android手机卫士、3D指南针、动画精选、仿bilibli客户端、身份证银行卡识别等源码...
  4. Java集合--TreeMap
  5. android 官方教程中文版
  6. Hermes与开源的Solr、ElasticSearch的不同
  7. winform下 PictureBox 显示网络图片
  8. flex 3与flex 4的不同
  9. 超经典解释什么叫网关
  10. 用PHP做负载均衡指南