Linux 设备模型

内核中引入设备模型是在2.5内核版本之后,基于之前的内核版本没有将系统中的设备容器所共有的特性做统一处理的现状,由此2.6的内核提供了一种通用的抽象结构来描述这种“共有”,同时这种抽象结构被统称为“设备模型(device model)”,提供了以下功能:

(1)电源管理和系统下电

(2)与用户空间通信

(3)热插拔事件

(4)设备分类

(5)对象生命周期

广义的设备模型(device model)包含kobject,kset,以及抽象等级更低的bus_type,device,device_driver,linux内核正是依靠的这些抽象出来的设备模型,才能实现将各种总线(pci,usb,spi,i2c)和对应的设备以及驱动编织在/sys目录下,实现分层的有序管理。

一. Bus & Device & Device Driver

                                                                             图 1.1

如图1.1所示Linux内核对于任意一个设备都会为其配置三种结构:Bus & Device & Device Driver。其中将物理总线的概念抽象出来表现的形式就是struct bus_type,设备对应于struct device以及设备驱动抽象为struct device_driver,三种结构都需要在内核中“注册”才能使用:bus_register & device_register & driver_register;另外设备(device)和设备驱动(device driver)都挂载到总线(bus)下,内核中的实现:将struct device和struct device_driver分别链入bus_type->p的(struct klist)klist_devices和(struct klist)klist_drivers

1.1 bus_type & device & device_driver

1.1.1 bus_type

 struct bus_type在内核中的具体形式如下,dev_name意味着/sys/bus子系统下总线的representation;dev_root意味着bus从底层来看也是一个device,具备device的性质;总线操作函数最常见的是match,probe,remove,分别用于设备与设备模型的匹配判断,probe用于触发驱动中注册的真正的probe探针初始化函数,remove同理;pm挂载着电源管理的callback;iommu_ops用作IOMMU管理的DMA映射;subsys_private作为私有结构klist_devicesklist_drivers分别链接入device和device_driver

                                                                          图 1.2

                                                                        图 1.3

1.1.2 device

struct device在内核中的具体形式如下,kobj为内核对象(可以理解成device更进一步的抽象,是构成系统/sys目录树的基本单位);父设备device;Init_name决定了在/sys/bus/xxx/device/下的目录名;bus和driver分别用来挂在对应的总线和驱动;driver_data挂载着私有数据;msi_domain用来挂载通用中断域的信息;dma_ops用于挂载dma在各种场景下的映射函数(例如alloc,map_sg,如果IOMMU功能开启的话,实际是调用iommu_domainiommu_ops操作);dma_maskcoherent_dma_mask分别用于DMA流映射和一致性映射的DMA寻址能力;dma_pools装载一致性映射的小buffer;dma_io_tlb_mem只有在IOTLB使能的情况下挂载系统本身IO TLB 内存池信息;numa_node记录NUMA节点;

                                                                        图 1.4

                                                                              图 1.5

1.1.3 device_driver

struct device_driver在内核中的具体形式如下,name决定了在/sys/bus/xxx/drivers/下的目录名;设备驱动抽象操作函数中,probe和remove才是真正的初始化和卸载函数,struct bus_type的总线操作函数都会最终调用到设备驱动抽象操作函数中的对应操作;p中蕴藏着kobject内核对象,klist_devices作为probe结束后链入的匹配device(具体是设备的knode_driver链入到klist_devices),knode_bus通过klist_add_tail链入到总线的klist_drivers

                                                                            图 1.6

                                                                             图 1.7

1.2 bus & device & driver register

1.2.1 device_register

device_register=device_initialize + device_add,先来看device_initializ:

(1) device_initialize

devices_kset作为一个全局变量,在/sys/devices这个目录上得以视觉体现,所有的device对应的kset均指向devices_kset,在udev事件中(kobject_uevent)调用device_uevent_ops来初始化;kobject_init初始化device对应的内核对象kobject并将全局变量device_ktype给到kobject,device_ktype起到的作用包括但不限于设备资源的释放,/sys/devices目录的读写函数定义;各种链表的初始化以及内核锁的初始化。

                                                                           图 1.8

(2) device_add

get_device作为device_add函数调用device的计数值加一;device_private_init分配并初始化device_private内容;init_name和真正的资源释放函数(例如scsi_host_type-> scsi_host_dev_releaseshost_class-> scsi_host_cls_release必须要在device_add执行前初始化完成dev->parent会因为dev->kobj注册在dev->parent->kobj目录下形成一个新目录而计数值加一,最终dev->parent->kobj依然是dev->kobj->parent; device_add_class_symlinkbus_add_device分别针对于device注册了class,bus_type的场景在该设备目录下生成“链接”(bus_type注册的情况下设备的knode_bus会链入到bus的klist_devices);device_add_attrs函数会将device_type以及device的属性组attribute_group添加到该设备目录下生成文件;kobject_uevent(&dev->kobj, KOBJ_ADD)通知开启udev事件进而在用户空间做操作(比如生成/dev/sdx可执行文件);bus_probe_device开启设备和设备驱动做match并加载probe过程

                                                                        图 1.9

1.2.2 driver_register

driver_find会在总线的drivers_kset结构通过名字校验当前device_driver是否已经存在;bus_add_driver实现设备&驱动之间的匹配并加载probe函数,链接驱动和总线,驱动和设备;driver_add_groups添加驱动的属性文件;kobject_uevent触发udev事件通知用户空间

                                                                        图 1.10

二. sys文件系统

/sys文件系统是一种特殊的文件系统,与/proc文件系统类似。/proc文件系统首次被设计成允许用户态程序访问内核内部数据结构的一种文件系统;/sys文件系统本质上与/proc用相同的目的,但是它还提供内核数据结构的附加信息,此外/sys的组织结构相对来说更为有条理,其目标是要展现设备驱动程序模型组件间的层次关系。该文件系统的相应高层目录是:

block

块设备,独立于所连接的总线

bus

系统中用于连接设备的总线

class

系统中设备的类型(声卡,网卡,显卡等),同一类可能包含不同总线连接的设备,由不同的驱动程序驱动

devices

所有被内核识别的硬件设备

module

系统中被内核识别的模块目录

power

处理一些硬件设备电源状态的文件

三. Kobjects & Ksets

kobject,其被视为最顶层的设备抽象,定义于<linux/kobject.h>当中,内核基本不会单独创建一个kobject,而是嵌入在各个抽象等级较低的设备模型当中(bus_type,device,device_driver),需要注意的是bus_type,device,device_driver同样嵌入在各个具体的总线,设备,驱动中,比如device_driver是pci_driver的成员,device是scsi_host结构体的成员。

很多时候kset作为kobject的一种衍生;但事实上, kset是一个包含了很多同类型结构的kobject的集合体,其更加需要被关注的是聚合性。此处贴两张kset 和kobject的关系图(图2.1和2.2),需要说明的是两张图要表述的意义是一样的---即一个kset中内嵌的全是kobject,图2.3给出了更为复杂的结构。

                                                                        图 2.1

                                                                        图 2.2

                                                                        图 2.3

3.1 Kobject相应API

3.1.1 kobject_init

kobject_init在device_initialize函数中有调用:

                                                                        图 2.4   

3.1.2 kobject_set_name

kobject_set_namedev_set_name函数中调用用以设置kobject的名字:

3.1.3 kobject_get/put

kobject_get/put用以对kobject计数增/减一:

3.1.4 kobject_add

kobject_add会将当前kobject的父kobject引用计数增一,将当前kobject链入所指向的kset(如果kset存在,并将kset ->kobj作为父kobject ),在kobj->parent目录下通过sysfs_create_dir_ns创建一个与kobject->name一致名字的目录(比如scsi,pci,spi),并通过populate_dir创建该目录的属性(kobj_type->default_attrs);最终将state_in_sysfs位域置1表示已经注册入系统

3.2 Kset相应API

摘抄自LDD3” ch14_The Linux Device Model”

四. 设备文件

设备文件是指/dev目录下被分配的设备节点文件,有对应的设备明和主/次设备号对应,如下图:

根据设备驱动程序的基本特性,设备文件可以分为两种:块设备和字符设备。其两者有如下特征:

  1. 块设备的数据可以被随机访问,从人类用户的观点来看,传输任何数据块所需要的时间都是较少且大致相同的。块设备的经典例子是硬盘,软盘,CD-ROM驱动器等
  2. 字符设备的数据不可以被随机访问

4.1 主/副设备号

设备标识符由设备文件类型(字符或块)和一对参数组成。第一个参数成为主设备号(major number),它标识了设备的类型,通常具备相同主设备号和类型的所有设备文件共享相同的文件操作函数,即他们是由同一个设备驱动程序处理的。第二个设备参数成为次设备号(minor number),它标识了主设备号相同的设备组中一个特定的设备。比如相同磁盘控制器管理的一组磁盘聚集有相同的的主设备号和不同的次设备号。

4.1.1 设备号的动态分配

字符设备:

Minor可由ida_simple_get获取,major可由直接引用上层的结果

块设备:

块设备的设备号的分配主要体现在gendisk-> major, gendisk-> first_minor, major由宏来保证(也可自定义但要确保不能与其他设备冲突),first_minorblk_alloc_ext_minor分配

4.2 设备名在用户空间显示

Linux内核中如果想要在/dev目录下生成相应的设备节点,内核代码的操作要点有三:

(1)主/副设备号动态分配以后需要用MKDEV集成到device的dev_t中来

(2)device需要挂载class类

(3)class类成员devnode中需要实现打印函数,打印出对应的设备名,例如:

return kasprintf(GFP_KERNEL, "%s", dev_name(dev));

4.3 动态创建设备节点

通常来说,设备节点的创建需要内核程序和用户态程序共同作用,内核代码的部分使用kobject_uevent接口来通知用户态程序udev,扫描/sys/class目录来找寻设备(这也是为什么上一节强调device一定需要挂载class类),对于每一个找到的设备,udev都会在/dev目录下为它创建一个相应的设备节点文件

4.4 设备文件的VFS处理

虚拟文件系统能够处理众多系统调用,比如open(),close(),lseek(),read(),write(),对应于操作块/字符设备下的block_device_operationsfile_operations结构挂载的响应函数

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值