(五)Linux下热插拔设备机制
3.8.4内核
o3.8内核
n新增针对固态硬盘的文件系统F2FS
n真正的CPU热插拔——普通PC无法实现
n安装新的内核时间长,步骤多。而且各种小疏忽都能导致失败
n查看内核版本:unme -r
设备热插拔的流程
o将可移动设备连入系统时,系统的后台中会依次发生如下事件
n外设插入;
n总线发现新设备,通知hotplug产生热插拔事件,调用device_add,添加新设备到设备管理系统;
ndevice_add中调用kobject_uevent(, KOBJ_ADD),向用户空间广播新设备加入事件通知;这里发出通知的方式,就是netlink;
n用户空间运行的daemon(udev)收到event事件广播;
n udev根据消息和环境变量,查询sysfs中的/sys的变化,按照规则(/etc/udev/rules.d/*),在/dev目录下自动创建设备节点;
热插拔驱动关联情况
n1. 若直接编译进内核或在启动时加载,则无需在udev中加载驱动模块,在bus_probe_device()中会为其找到相应的驱动;
n 2. 若驱动需要动态加载,没有固定的先有device还是先有driver。内核层面,手动加入的驱动register函数中,找到相应device进行关联。用户层面,udev(目前的情况是这样,以前也有其他方式如/sbin/hotplug等)中,动态加载相应脚本程序。
热插拔机制的三个方面
1. 内核空间机制
2. 通信机制
3.用户空间机制
1.内核空间处理机制
oHotplug包含两部分内容
1.Hotplug包是用来处理内核产生的hotplug事件。
2.内核里hotplug模块,如内核里的pci_hotplug.ko
oHotplug的文件
n/etc/hotplug/*.rc这些脚本用于冷插拔(检测和**在系统启动时已经存在的硬件)。被hotplug初始化脚本调用。*.rc脚本会尝试恢复系统引导时丢失的热插拔事件。
n/etc/hotplug/*.agent这些脚本将被hotplug调用以响应内核产生的各种不同的热插拔事件,导致插入相应的内核模块和调用用户预定义的脚本。
n/sbin/hotplug 内核默认情况下在内核态某些事件发生变化时调用此脚本。——相当于Udev机制发送热插拔事件的子系统包括总线驱动(USB、PCI)和一些设备的抽象层(网络接口、磁盘分区)。第一个参数来识别。
oMODULE_DEVICE_TABLE
n注册热插拔设备列表,设备驱动需要在代码中设置MODULE_DEVICE_TABLE,指向驱动程序感兴趣的设备ID列表。
n第一个参数是设备的类型,后面一个参数是设备表(usb, skel_table)
设备接入系统时,会检查该设备类型以及设备ID值是否与设备驱动匹配。若匹配,则调用此模块作为相应驱动。
内核空间处理机制——关键数据结构
oKobject
定义于头文件<linux/kobject.h>,可创建呈层次结构排列的对象。
在内核中注册的每个Kobject对象对应sysfs文件系统中的一个目录
注意:Kobject通常是嵌入到其他结构中,其单独意义并不大。如Cdev结构成员包含kobj,则拥有了其标准功能。可成为对象层次架构一部分
oKtype
n定义于头文件<linux/kobject.h>中。
nKtype是为了描述一族kobject所具有的特性。将这些普遍特性在ktype结构中一次定义。然后kobject都能共享一样特性
release指针指向在kobject引用计数至0时要调用的析构函数。该函数负责释放Kobject使用的内存和其他相关清理工作。
sysfs_ops变量指向sysfs_ops结构体。描述了sysfs文件读写特性。
default_attrs指向一个attribute结构体数组。定义了kobject相关的默认属性。
oKset
nKset是kobject对象的集合体。可看做一个容器,将相关kobject对象置于同一位置。定义于<linux/kobject.h>
n具有相同ktype的kobject可以分组到不同的kset中。
Subsys指针指向该结构体相关的struct subsystem结构体
ktype指针指向集合(kset)中kobject对象的类型(ktype)。
list变量连接该集合kset中所有的kobject对象。
Kobj代表了该集合的基类。
Hotplug_ops(Uevent_ops)指向一个用于处理集合中kobject对象的热插拔操作的结构体
o三者关系
在Kset当中添加Kobject
内核空间处理机制——热插拔事件
o热插拔事件
当系统配置发生变化时,如:添加kset到系统;移动kobject, 一个通知会从内核空间发送到用户空间,这就是热插拔事件
okset_uevent_ops
n对热插拔事件的实际控制,是由保存在kset_uevent_ops结构中的函数完成的。
o当kobject和kset状态变化时三个函数被调用
nFilter:决定是否将事件传递到用户空间。如果filter返回0,将不传递事件。
nName:负责将相应的字符串传递用户空间的热插拔处理程序。
nUevent:将用户空间需要的参数添加到环境变量中。返回值正常是0,若返回非0则将终止热插拔事件的产生
热插拔事件模拟程序——添加新设备
内核空间处理机制——高级数据结构
oDevice·Bus·Driver
设备驱动程序一般不会和底层的kobject/kset打交道,因为更高层次的device,bus和driver把kobject/kset那一层的细节实现都给封装了起来。
设备热插拔的uevent事件最终的源头来自于device_add。
2.内核与用户空间通信机制
oNetlink
nNetlink是一种特殊的socket,它是Linux特有的,目前很多应用在最新的Linux内核中使用Netlink对内核与用户之间通信。
路由(NETLINK_ROUTE)、防火墙(NETLINK_FIREWALL)等
nNetlink是一种在内核与用户应用进行双向数据传输的有效方式。用户态应用使用标准的socket API即可使用。内核态需使用专门的内核API调用。
nNetlink是一种异步通信机制。在内核与用户态应用之间传递的消息保存在socket缓存队列中,发送消息只是把消息保存在接受者的socket的接受队列。而不需要等待接受者收到消息。
n使用Netlink的内核部分可以采用模块的方式实现,使用Netlink的应用部分和内核部分没有编译时依赖,系统调用有依赖,新的系统调用必须静态连接到内核中,使用新系统调用的应用在编译时需要依赖内核。
Netlink程序——监听hotplug
3.用户空间处理机制
oUdev
nudev在2.6.15以后的内核上通过netlink接听设备事件。
nudev 完全在用户空间工作。利用设备加入或移除时内核所发送的hotplug 事件来工作。
n设备的详细信息是由内核输出 (export) 到位于 /sys 的 sysfs 文件系统的。
n所有的设备命名策略、权限控制和事件处理都是在用户态下完成的
oUdev作用——相关文档
nDynamic replacement for devfs。传统devfs不能动态分配主从设备号,Udev类似于DHCP动态分配IP一样分配Major和Minor。
nDevice naming。提供设备命名持久化的机制。传统设备命名方式不具直观性,Udev能够像DNS解析域名一样给设备指定名称。
nAPI to access info about current system devices。提供了一组易用的API去操作sysfs。避免重复实现同样代码。
o对sysfs支持
nUdev的设备命名策略、权限控制和事件处理都是在用户态下完成的。热插拔时设备的详细信息会由内核输出到位于/sys的sysfs文件系统
o用户自定义的规则
n位于/etc/udev/rules.d目录中,可以根据对应设备特点设置规则
o一套用户空间实用工具
nudevd和udevinfo,这些工具用来查看相关设备信息
oUdev的工作过程
nUdev包括三部分:namedev、libsysfs、udev。Namedev是设备命名子系统,libsysfs提供访问sysfs文件系统从中获取信息的标准接口,udev提供/dev设备结点文件的动态创建和删除策略。
n当内核检测在系统中出现新设备后,内核将会在sysfs文件系统中为新设备生成新的记录并导出一些设备特定信息及发生的事件。
nudev获取内核导出的信息,调用namedev决定应该给该设备指定的名称。如果是新插入设备,udev将调用libsysfs决定应该为该设备文件指定的主/次设备号,并用分析获得的设备名称和主/次设备号创建/dev中的设备文件;如果设备移除,则之前已被创建的/dev文件将被删除。
oUdev热插拔事件处理流程
o识别热插拔设备
n内核识别的设备名,如sda1,sda2…
n子系统,如块设备,scsi设备
n属性、权限、环境变量、脚本路径等等
跳转到脚本文件,执行相关脚本指令
oUdev规则配置文件
udev_rules=“/etc/udev/rules.d/”
前两位数字代表优先级,一般数字越小优先级越高
o确定自己的u盘设备号
osudo fdisk –l 找到自己的设备
o输入ls /sys/block查看块设备
o输入udevadm info -a -p /sys/block/sdb 查看U盘信息
o输入udevadm info -q path -n /dev/sdb1查看USB分区sdb1所在位置
o输入udevadm info -a -p $(udevadm info -q path -n /dev/sdb1)查看sdb1具体信息
o编写U盘的rules文件
o输入sudo gedit /etc/udev/rules.d/10-local.rules编辑U盘热插拔时触发规则
osudo udevadm test/sys/class/block/sdb1更新规则使其生效
查看设备命名情况
o进入/dev查看设备:
o查看sdb分区
会发现原来的sdb1已经不存在了。
以后我们就可以使用改变的U盘名称或者链接名来操作我们的U盘,而不是/dev/sdb1
脚本运行效果
o当U盘热插拔事件启动时,自动运行rules中指令
o用lsmod查看加载驱动:
成功将字符设备驱动加载
当拔出U盘时,用lsmod查看刚才加载驱动:driver_insmod 模块已不存在
总结
总结——设备插入的流程
总结——设备拔出的流程