资源管理
Linux提供通用的构架,用于在内存中构建数据结构。这些结构描述了系统中可用的资源,使得内核代码能够管理和分配资源。
其中关键的数据结构resource如下:
用于连接parent, child, sibling
成员规则如下:
1、每个子结点只有一个父结点;
2、一个父结点可以有任意数目的子结点;
3、同一个父结点的所有子结点,会连接到兄弟结点链表上面。
在内存中表示数据结构时,必须要注意几个问题:
1、尽管每个子结点都有一个指针指向父结点,但父结点只有一个指针指向第一个子结点,所有其它子结点都通过兄弟结点链表访问;
2、指向父结点的指针同样可以为NULL。
为确保可靠地配置资源(无论何种类型),内核必须提供一种机制来分配和释放资源。一旦资源已经分配,则不能由任何其他驱动程序使用。
请求资源:内核提供了__request_resource
函数,用于请求一个资源区域。
该函数用于分配 reqeust
实例,在此函数当中也会进行检査(起始地址大于结束地址,请求无法使用),则放弃操作。
request_resource
这个函数负责必要的锁操作,主要工作还是由 __request_resource
。它连接地扫描现存的资源,将新资源添加到正确的位置,或发现与已经分配区域的冲突。完成所有操作之后,需要遍历兄弟结点的链表。如果所需的资源区域是空闲的,则插入新的 resource
实例,这样就可以完成资源分析。如果区域不是空闲的,则分配失败。
在扫描特定父结点的子结点时,只会在一个层次上扫描兄弟结点链表。内核不会扫描更底层子结点的链表。
释放资源:调用release_resource
函数释放使用中的资源。
I/O 内存
资源管理还有一个很重要的方面是 I/O 内存的分配方式,因为在所有平台上这都是与外设通信的主要方法( IA-32除外,其中 I/O 端口更为重要)。
所有分配的I/O内存地址,它们都要通过一棵资源树管理,树的根结点是全局内核变量iomem_resource
。
I/O 内存不仅包括与扩展设备通信直接使用的内存区域,还包括系统中可用的物理内存和 ROM 存储器,以及包含在资源列表中的内存(可以使用 proc 文件系统中的 iomem 文件,显示所有的 I/O 内存)。
使用 I/O 内存时,分配内存区域并不是所需唯一操作。主要取决于总线系统和处理器类型,可能必需将扩展设备的地址空间映射到内核地址空间之后,才可以访问此设备(称为软件 I/O 映射)。这是通过使用ioremap
内核函数适当设备系统页表而实现的。同样也可以使用体系结构当中的iounmap
函数解除映射操作。
将一个物理地址映射到处理器的虚拟地址空间中,使得内核可以使用该地址,就设备驱动程序而言,意味着扩展总线的地址空间映射到 CPU 的地址空间中,使得能够用普通内存访问函数来操作总线/设备。
I/O 端口
I/O 端口是一种与设备和总线通信的流行方法,特别是在IA-32平台上。类似于I/O内存,按良好范例编写的驱动程序在访问所需的区域之前,相应的区域必须已经注册。糟糕的是,处理器无法检查注册是否已经完成。
kernel/resource.c
中的 ioport_resource
充当资源树的根结点。 proc
文件系统中的 ioports
文件可以显示已经分配的端口地址。
总线系统
现代总线系统在布局和结构的细节上可能有所不同,但也有许多共同之处,内核的数据结构即反映了这个事实。结构中的许多成员用于所有的总线(以及相关设备的数据结构中)。
在内核版本2.6开发期间,一个通用驱动程序模型(设备模型, device model)并入内核,以防止不必要的复制。
所有总线共有的属性封装到特殊的、可以用通用方法处理的数据结构中,再关联到总线相关的成员。
设备的表示:驱动程序模型采用一种特殊数据结构来表示几乎所有总线类型通用的设备属性。 该结构直接嵌入到特定于总线的数据结构中,而不是通过指针引用。
通用驱动程序模型也为设备驱动程序单独设计一种数据结构:
总线的表示:通用驱动程序模型不仅表示了设备,还用另一个数据结构表示了总线。
注册过程:注册总线 —> 注册设备 —> 注册设备驱动程序。
-
注册总线
-
注册设备:初始化设备的数据结构,并将它加入到数据结构的网络当中。
-
注册设备驱动程序:在进行一些检查和初始化工作之后,
driver_register
调用bus_add_driver
将一个新驱动程序添加到一个总线来,驱动程序要有名字,然后注册到通用数据结构框架。
如果总线支持自动探测,调用driver_attach
,该函数迭代总线上所有设备,使用驱动程序的match
函数进行检测,确定是否有某些设备可使用该驱动程序管理。最后将该驱动程序添加到总线上注册的所有驱动程序的链表中。
PCI 总线
PCI 是 peripheral component interconnect 的缩写,是英特尔公司开发的一种标准总线。
-
设计目标
1.支持高传输带宽,以适合具有大数据流的多媒体应用;
2.简单且易于自动化配置外设;
3.平台独立性,即不绑定到特定的处理器类型或系统平台, -
PCI系统布局
1.设备标识:系统某个 PCI 总线上的每个设备,都由一组 3 个编号标识;
2.总线编号:设备所有总线的编号,编号从 0 开始,PCI 规范准许每个系统最多 255 个总线;
3.插槽编号:总线内核的一个唯一标识编号,一个总线最多能够附接 32 个设备;
4.功能编号:用于在一个扩展卡上,实现包括多个扩展设备的设备。 -
地址空间
有 3 个地址空间支持与 PCI 设备的通信;
I/O 空间通过 32 个比特位描述。对用于与设备通信端口地址,提供了最大 4GB 空间;
取决于处理器类型,数据空间由 32 或 64 个比特位描述;
配置空间包含各个设备的类型和特征的详细信息;
内核提供几个数据结构类型来管理系统的 PCI 结构。
Linux内核提供了几个数据结构来管理系统的PCI结构。这些结构声明在<pci.h>
中,通过一个由指针构成的网络互相连接。
-
总线的表示:在内存中,每个PCI总线都通过
pci_bus
数据结构的一个实例表示; -
设备管理:
struct pci_dev
是一个关键的数据结构,用于表示系统中的各个PCI设备。
-
驱动程序函数:PCI 层中最后一个基本的数据结构是
pci_driver
。它用于实现 PCI 驱动程序,表示了通用内核代码和设备的底层硬件驱动程序之间的接口。
每个 PCI 驱动程序都必须将其函数填到该接口中,使得内核能够一致地控制可用的驱动程序。PCI 驱动程序通过pci_register_driver
注册:
USB 总线
USB( Universal Serial Bus,通用串行总线)是一种外部总线,用于满足不断发展的PC的需求,并用于建立针对新类型计算机的解决方案,如手持设备、PDA等。
作为一种通用的外部总线,在用于连接中低数据传输速率的设备时(如鼠标、网络摄像头、键盘),USB 很有优势。但带宽要求更高的设备如外部硬盘、光驱、 CD 刻录机也可以通过USB 总线运行。
USB 1.1的最大传输速率限于12 兆比特/秒,该标准的2.0版本最高速率提升到 480 兆比特/秒。
所有USB设备都划分到不同类型当中,在内核源代码中,我们可以看到这样划分,各个驱动程序源代码按照所属类型归纳到不同的目录:
USB标准定义 4 种不同传输模式:控制传输、块传输、中断传输及同步传输;
USB子系统有4种主要任务:
(1)注册和管理现存的设备驱动程序
(2)为USB设备查找适当的驱动程序,以及初始化和配置;
(3)在内核内存中表示设备树;
(4)与设备通信(交换数据);
usb_driver
是 USB 设备驱动程序和内核其余部分(特别是USB层)之间协作的起始点。
USB 驱动
设备树的表示:下面的数据结构描述USB设备树以及内核中各种设备的特征。