Linux内核原理之通用块设备层

通用块设备层

I/O体系结构

与外设的通信通常称为输入输出,缩写为I/O。在实现外设的I/O时,内核需要处理好3个问题:

  • 根据具体的设备型号和模型,使用各种方法对硬件寻址
  • 内核必须向用户应用程序和系统工具提供访问各种设备的方法,应该采用统一的方案
  • 用户空间需要知道内核中有哪些设备可用

与外设的通信是层次化的,如下图

在这里插入图片描述

在层次系统的底部是设备本身,它通过总线系统连接到其它设备和系统CPU,设备与内核的通信经由该路径进行

硬件设备可以以多种方式连接到系统,主板上的扩展槽和外部连接器是最常用的方法

  1. 总线系统

    外设不直接连接到CPU,它们通过总线连接起来,总线负责设备与CPU之间以及各个设备之间的通信。下列是一些代表性的总线:

  • PCI:许多体系结构使用的主要系统总线,PCI设备插入到系统主板的扩展槽中
  • ISA:一种比较古老的总线
  • IEEE1394,:也成为了FireWire,是高端电脑中非常流行的一种外部总线
  • USB:广泛应用的外部总线
  • SCSI:过去称为专业人员的总线,主要用在服务器系统上寻址硬盘
  • 并口和串口:非常简单且速率很低,用于外部连接

系统一般是将一些总线组合在一起,当前的PC设计通常包括两个通过桥接器互连的PCI总线,一些总线(如USB和FireWire)无法作为主总线,需要经由另一个系统总线将数据传递到处理器。如下图是系统中不同总线的连接方式

在这里插入图片描述

  1. 与外设的交互

    与外设通信的方法如下

  • I/O端口:内核发送数据到I/O控制器,数据的目标设备通过唯一的端口号标识,数据被传输到设备进行处理。处理器管理了一个独立的虚拟地址空间,可用于管理所有I/O地址
  • I/O内存映射:现代处理器提供了对I/O端口进行内存映射的选项,将特定外设的端口地址映射到普通内存中,可以像操作普通内存那样操作外设,图形卡通常会使用这类操作
  • 轮询和中断:通过轮询或者中断来感知到某个设备的数据是否就绪
  1. 通过总线控制设备

    总线类型分为系统和扩展总线

    就系统总线而言(例如PCI总线),可以使用I/O语句和内存映射与总线本身和附接的设备通信,而扩展总线(如USE、SCSI等),通过明确定义的总线协议和附接的设备交换数据和命令

访问设备

设备文件用于访问扩展设备,它们不关联到硬盘或任何其他存储介质上的数据段,而是建立了与某个设备驱动程序的连接,以支持与扩展设备的通信

设备文件

设备不是通过文件名标识,而是通过文件的主、从设备号来标识

字符设备、块设备与其它设备

有些设备因为数据传输量低,适合于面向字符的数据交换,其它设备则更适合于处理包含固定数目字节的数据块,内核会区分字符设备和块设备,前一类包括串行接口和文本终端,后一类则包括硬盘、光驱等设备

  1. 标识设备文件
  • 访问权限之前的字母是b或c,分别代表块设备和字符设备
  • 设备文件没有文件长度,而是增加了另外两个值,分别是主设备号和从设备号,二者共同形成唯一的号码,内核可用于查找对应的设备驱动程序

在这里插入图片描述

主设备号用于寻址设备驱动程序本身,驱动程序管理的各个设备(如第1个和第2个硬盘)则通过不同的从设备号指定,如下所示连续的从设备号用于标识各个分区,硬盘的各个分区可以通过设备文件进行寻址(如/dev/vda、/dev/vda1),而/dev/vda*则代表整个硬盘。

在这里插入图片描述

系统可能包含几个同样类型的设备,由同一个设备驱动程序管理(避免多次将同样的代码加载到内核),其次可以将同类设备合并起来,便于插入到内核的数据结构中进行管理,一个驱动程序可以分配多个主设备号

注意:块设备和字符设备的主设备号可能是相同的,除非同时指定设备号和设备类型,否则找到的驱动程序可能不是唯一的

  1. 动态创建设备文件

    /dev的设备结点一般是基于磁盘的文件系统静态创建的,随着支持的设备越来越多,几乎所有的Linux设备版都将/dev内容的管理工作切换到udevd,这是一个守护进程,允许从用户层动态创建设备文件

在这里插入图片描述

每当内核检测到一个设备时,都会创建一个内核对象kobject,该对象借助sysfs导出到用户层,同时内核向用户空间发送一个热插拔消息,热插拔消息包含了驱动程序为设备分配的主从设备号,udevd守护进程的工作就是监听这些消息。在注册新设备时,会在/dev中创建对应的项

由于引入了udev机制,/dev不再放置到基于磁盘的文件系统中,而是使用tmpfs(内存文件系统),非持久性

使用ioctl进行设备寻址

ioctl可以用于对设备I/O通道进行管理,设置设备的配置选项,它基于ioctl系统调用实现,由内核的sys_ioctl处理

网卡:网卡比较特殊,它没有设备文件,用户必须使用套接字和网卡通信,套接字是一个抽象层,对所有网卡提供一个抽象视图

设备数据库

内核中用于跟踪所有可用设备的数据库是相同的,由于字符设备和块设备都通过唯一的设备号标识

  • 每个字符设备表示为struct cdev的实例
  • struct genhd用于管理块设备的分区(作用类似于字符设备的cdev)

如下图是内核跟踪所有cdev和genhd实例的方式,有两个全局数组(bdev_map和cdev_map,都是kobj_map结构的实例)用来实现散列表,使用主设备号作为散列键,散列方式是:major % 255(由于很少有设备的主设备号大于255,因此哈希碰撞很少)

在这里插入图片描述

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值