咱们以前了解过了 Linux 的进程和线程、Linux 内存管理,那么下面咱们就来认识一下 Linux 中的 I/O 管理。缓存
Linux 系统和其余 UNIX 系统同样,IO 管理比较直接和简洁。全部 IO 设备都被看成文件
,经过在系统内部使用相同的 read 和 write 同样进行读写。网络
Linux 中也有磁盘、打印机、网络等 I/O 设备,Linux 把这些设备看成一种 特殊文件
整合到文件系统中,通常一般位于 /dev
目录下。可使用与普通文件相同的方式来对待这些特殊文件。异步
特殊文件通常分为两种:socket
块特殊文件是一个能存储固定大小块
信息的设备,它支持以固定大小的块,扇区或群集读取和(可选)写入数据。每一个块都有本身的物理地址
。一般块的大小在 512 - 65536 之间。全部传输的信息都会以连续
的块为单位。块设备的基本特征是每一个块都较为对立,可以独立的进行读写。常见的块设备有 硬盘、蓝光光盘、USB 盘与字符设备相比,块设备一般须要较少的引脚。async
块特殊文件的缺点基于给定固态存储器的块设备比基于相同类型的存储器的字节寻址要慢一些,由于必须在块的开头开始读取或写入。因此,要读取该块的任何部分,必须寻找到该块的开始,读取整个块,若是不使用该块,则将其丢弃。要写入块的一部分,必须寻找到块的开始,将整个块读入内存,修改数据,再次寻找到块的开头处,而后将整个块写回设备。ide
另外一类 I/O 设备是字符特殊文件
。字符设备以字符
为单位发送或接收一个字符流,而不考虑任何块结构。字符设备是不可寻址的,也没有任何寻道操做。常见的字符设备有 打印机、网络设备、鼠标、以及大多数与磁盘不一样的设备。工具
每一个设备特殊文件都会和 设备驱动
相关联。每一个驱动程序都经过一个 主设备号
来标识。若是一个驱动支持多个设备的话,此时会在主设备的后面新加一个 次设备号
来标识。主设备号和次设备号共同肯定了惟一的驱动设备。性能
咱们知道,在计算机系统中,CPU 并不直接和设备打交道,它们中间有一个叫做 设备控制器(Device Control Unit)
的组件,例如硬盘有磁盘控制器、USB 有 USB 控制器、显示器有视频控制器等。这些控制器就像代理商同样,它们知道如何应对硬盘、鼠标、键盘、显示器的行为。spa
绝大多数字符特殊文件都不能随机访问,由于他们须要使用和块特殊文件不一样的方式来控制。好比,你在键盘上输入了一些字符,可是你发现输错了一个,这时有一些人喜欢使用 backspace
来删除,有人喜欢用 del
来删除。为了中断正在运行的设备,一些系统使用 ctrl-u
来结束,可是如今通常使用 ctrl-c
来结束。操作系统
I/O 的另一个概念是网络
, 也是由 UNIX 引入,网络中一个很关键的概念就是 套接字(socket)
。套接字容许用户链接到网络,正如邮筒容许用户链接到邮政系统,套接字的示意图以下
套接字的位置如上图所示,套接字能够动态建立和销毁。成功建立一个套接字后,系统会返回一个文件描述符(file descriptor)
,在后面的建立连接、读数据、写数据、解除链接时都须要使用到这个文件描述符。每一个套接字都支持一种特定类型的网络类型,在建立时指定。通常最经常使用的几种
可靠的面向链接的字节流会使用管道
在两台机器之间创建链接。可以保证字节从一台机器按照顺序到达另外一台机器,系统可以保证全部字节都能到达。
除了数据包之间的分界以外,第二种类型和第一种类型是相似的。若是发送了 3 次写操做,那么使用第一种方式的接受者会直接接收到全部字节;第二种方式的接受者会分 3 次接受全部字节。除此以外,用户还可使用第三种即不可靠的数据包来传输,使用这种传输方式的优势在于高性能,有的时候它比可靠性更加剧要,好比在流媒体中,性能就尤为重要。
以上涉及两种形式的传输协议,即 TCP
和 UDP
,TCP 是 传输控制协议
,它可以传输可靠的字节流。UDP
是 用户数据报协议
,它只可以传输不可靠的字节流。它们都属于 TCP/IP 协议簇中的协议,下面是网络协议分层
能够看到,TCP 、UDP 都位于网络层上,可见它们都把 IP 协议 即 互联网协议
做为基础。
一旦套接字在源计算机和目的计算机创建成功,那么两个计算机之间就能够创建一个连接。通讯一方在本地套接字上使用 listen
系统调用,它就会建立一个缓冲区,而后阻塞直到数据到来。另外一方使用 connect
系统调用,若是另外一方接受 connect 系统调用后,则系统会在两个套接字之间创建链接。
socket 链接创建成功后就像是一个管道,一个进程可使用本地套接字的文件描述符从中读写数据,当链接再也不须要的时候使用 close
系统调用来关闭。
Linux 系统中的每一个 I/O 设备都有一个特殊文件(special file)
与之关联,什么是特殊文件呢?
在操做系统中,特殊文件是一种在文件系统中与硬件设备相关联的文件。特殊文件也被称为
设备文件(device file)
。特殊文件的目的是将设备做为文件系统中的文件进行公开。特殊文件为硬件设备提供了借口,用于文件 I/O 的工具能够进行访问。由于设备有两种类型,一样特殊文件也有两种,即字符特殊文件和块特殊文件
对于大部分 I/O 操做来讲,只用合适的文件就能够完成,并不须要特殊的系统调用。而后,有时须要一些设备专用的处理。在 POSIX 以前,大多数 UNIX 系统会有一个叫作 ioctl
的系统调用,它用于执行大量的系统调用。随着时间的发展,POSIX 对其进行了整理,把 ioctl 的功能划分为面向终端设备的独立功能调用,如今已经变成独立的系统调用了。
下面是几个管理终端的系统调用
Linux 中的 IO 是经过一系列设备驱动实现的,每一个设备类型对应一个设备驱动。设备驱动为操做系统和硬件分别预留接口,经过设备驱动来屏蔽操做系统和硬件的差别。
当用户访问一个特殊的文件时,由文件系统提供此特殊文件的主设备号和次设备号,并判断它是一个块特殊文件仍是字符特殊文件。主设备号用于标识字符设备仍是块设备,次设备号用于参数传递。
每一个驱动程序
都有两部分:这两部分都是属于 Linux 内核,也都运行在内核态下。上半部分运行在调用者上下文而且与 Linux 其余部分交互。下半部分运行在内核上下文而且与设备进行交互。驱动程序能够调用内存分配、定时器管理、DMA 控制等内核过程。可被调用的内核功能都位于 驱动程序 - 内核接口
的文档中。
I/O 实现指的就是对字符设备和块设备的实现
系统中处理块特殊文件 I/O 部分的目标是为了使传输次数尽量的小。为了实现这个目标,Linux 系统在磁盘驱动程序和文件系统之间设置了一个 高速缓存(cache)
,以下图所示
在 Linux 内核 2.2 以前,Linux 系统维护着两个缓存:页面缓存(page cache)
和 缓冲区缓存(buffer cache)
,所以,存储在一个磁盘块中的文件可能会在两个缓存中。2.2 版本之后 Linux 内核只有一个统一的缓存一个 通用数据块层(generic block layer)
把这些融合在一块儿,实现了磁盘、数据块、缓冲区和数据页之间必要的转换。那么什么是通用数据块层?
通用数据块层是一个内核的组成部分,用于处理对系统中全部块设备的请求。通用数据块主要有如下几个功能
将数据缓冲区放在内存高位处,当 CPU 访问数据时,页面才会映射到内核线性地址中,而且此后取消映射
实现
零拷贝
机制,磁盘数据能够直接放入用户模式的地址空间,而无需先复制到内核内存中管理磁盘卷,会把不一样块设备上的多个磁盘分区视为一个分区。
利用最新的磁盘控制器的高级功能,例如 DMA 等。
cache 是提高性能的利器,无论以什么样的目的须要一个数据块,都会先从 cache 中查找,若是找到直接返回,避免一次磁盘访问,可以极大的提高系统性能。
若是页面 cache 中没有这个块,操做系统就会把页面从磁盘中调入内存,而后读入 cache 进行缓存。
cache 除了支持读操做外,也支持写操做,一个程序要写回一个块,首先把它写到 cache 中,而不是直接写入到磁盘中,等到磁盘中缓存达到必定数量值时再被写入到 cache 中。
Linux 系统中使用 IO 调度器
来保证减小磁头的反复移动从而减小损失。I/O 调度器的做用是对块设备的读写操做进行排序,对读写请求进行合并。Linux 有许多调度器的变体,从而知足不一样的工做须要。最基本的 Linux 调度器是基于传统的 Linux 电梯调度器(Linux elevator scheduler)
。Linux 电梯调度器的主要工做流程就是按照磁盘扇区的地址排序并存储在一个双向链表
中。新的请求将会以链表的形式插入。这种方法能够有效的防止磁头重复移动。由于电梯调度器会容易产生饥饿现象。所以,Linux 在原基础上进行了修改,维护了两个链表,在 最后日期(deadline)
内维护了排序后的读写操做。默认的读操做耗时 0.5s,默认写操做耗时 5s。若是在最后期限内等待时间最长的链表没有得到服务,那么它将优先得到服务。
和字符设备的交互是比较简单的。因为字符设备会产生并使用字符流、字节数据,所以对随机访问的支持意义不大。一个例外是使用 行规则(line disciplines)
。一个行规能够和终端设备相关联,使用 tty_struct
结构来表示,它表示与终端设备交换数据的解释器,固然这也属于内核的一部分。例如:行规能够对行进行编辑,映射回车为换行等一系列其余操做。
什么是行规则?
行规是某些类 UNIX 系统中的一层,终端子系统一般由三层组成:上层提供字符设备接口,下层硬件驱动程序与硬件或伪终端进行交互,中层规则用于实现终端设备共有的行为。
网络设备的交互是不同的,虽然 网络设备(network devices)
也会产生字符流,由于它们的异步(asynchronous)
特性是他们不易与其余字符设备在同一接口下集成。网络设备驱动程序会产生不少数据包,经由网络协议到达用户应用程序中。
UNIX 设备驱动程序是被静态加载
到内核中的。所以,只要系统启动后,设备驱动程序都会被加载到内存中。随着我的电脑 Linux 的出现,这种静态连接完成后会使用一段时间的模式被打破。相对于小型机上的 I/O 设备,PC 上可用的 I/O 设备有了数量级的增加。绝大多数用户没有能力去添加一个新的应用程序、更新设备驱动、从新链接内核,而后进行安装。
Linux 为了解决这个问题,引入了 可加载(loadable module)
机制。可加载是在系统运行时添加到内核中的代码块。
当一个模块被加载到内核时,会发生下面几件事情:第一,在加载的过程当中,模块会被动态的从新部署。第二,系统会检查程序程序所需的资源是否可用。若是可用,则把这些资源标记为正在使用。第三步,设置所需的中断向量。第四,更新驱动转换表使其可以处理新的主设备类型。最后再来运行设备驱动程序。
在完成上述工做后,驱动程序就会安装完成,其余现代 UNIX 系统也支持可加载机制。
你好,我是 cxuan,我本身手写了四本 PDF,分别是 Java基础总结、HTTP 核心总结、计算机基础知识,操做系统核心总结,我已经整理成为 PDF,能够关注公众号 Java建设者 回复 PDF 领取优质资料。
做者:cxuan 本文版权归做者全部,未经做者容许不能转载,转载须要联系,不然追究法律责任的权利。 若是文中有什么错误,欢迎指出。以避免更多的人被误导。 |