linux热插拔

来源:互联网 发布:html中js用函数传值 编辑:程序博客网 时间:2024/05/01 00:03

当用户向系统添加或删除设备时,内核会产生一个热插拔事件,并在/proc/sys/kernel/hotplug文件里查找处理设备连接的用户空间程序,这个用户空间程序主要有/sbin/hotplug与/sbin/mdev.
echo /sbin/hotplug > /proc/sys/kernel/hotplug
或者
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s


hotplug

是一个bash脚本具有如下类似的代码:

DIR="/etc/hotplug.d"for I in "${DIR}/$1"*.hotplug "${DIR}/"default/*.hotplug ; do    if [-f $I]; then        test -x $I && $I $1;    fidoneexit 1

1) 当driver执行kobject_uevent会调用hotplughelper,从而调用这个/sbin/hotplug脚本。
2) 该脚本在/etc/hotplug.d目录搜索所有以hotplug为后缀的程序并调用,
3) 传递给被调用的程序的参数就是事件的名字,
4) 被调用的程序还可以读取大量的环境变量,包括ACTION、DEVPATH、SUBSYSTEM等。
5) 被调用的程序根据这些环境变量在/lib/module/KERNEL_VERSION/modules.*map文件找到对应需要加载的模块并加载。
(*.map是当驱动程序使用MODULE_DEVICE_TABLE宏时,depmod程序使用这些信息并创建了/lib/module/KERNEL_VERSION/modules.*map文件。)


udev/mdev/vold

为用户空间提供使用固定设备名的动态/dev目录的解决办法。
mdev是简化的udev,是busybox所带的程序,适合嵌入式系统的使用。/sbin/mdev是一个链接,指向/bin/busybox
android系统中的vold机制与udev一样,android的源码NetlinkManager.cpp同样是监听基于netlink的套接字,并解析接收到的消息。

udev创建每个设备的名字和权限由/etc/udev/rules.d目录下的文件指定规则来设置,如果udev找不到所创建设备的权限文件,就将其缺省的权限设置为660,所有者root:root

热插拔设备
由于启动的时候运行了命令: echo /sbin/mdev > /proc/sys/kernel/hotplug,那么当有热插拔事件产生时,/sbin/mdev就会被调用,mdev根据环境变量中的ACTION和DEVPATH来确定此次热插拔事件的动作以及影响了/sys中的哪个目录,接着查看这个目录中是否有dev文件,如果有,就用这些信息在/dev目录下创建设备节点文件。

冷插拔设备
冷插拔的设备在开机时就存在,在udev启动前已经被插入了,对于冷插拔的设备,linux内核提供了sysfs下面的一个uevent节点,可以往该节点写入一个add,导致内核重新发送netlink,之后udev就可以收到冷插拔的netlink的消息了。

mdev -s
在/sys/class和/sys/block目录树中查找一个称为dev的文件,根据dev文件中记录的设备节点的主次设备号,从而在/dev目录下创建相应的设备节点。
/sys/bus/和/sys/class目录下的devices、drivers都是对/sys/devices目录下的文件的符号连接。

解决使用mdev时“cannot create /proc/sys/kernel/hotplug:nonexistent directory”错误
确保编译内核时编译如下选项:
CONFIG_PROC_FS=y
CONFIG_PROC_SYSCTL=y
CONFIG_HOTPLUG=y
CONFIG_NET=y
如果CONFIG_HOTPLUG和CONFIG_NET不选或没全选上的话,/proc/sys/kernel下将不会创建hotplug文件.(参见kernel/sysctl.c)


原理

struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...)int device_register(struct device *dev)    device_initialize(dev)    device_add(dev)        kobject_add(&dev->kobj, dev->kobj.parent, NULL);        kobject_uevent(&dev->kobj, KOBJ_ADD);

上面device_create和device_register都可以用来添加struct device,其实在device_create中就是调用的device_register。
在kobject_uevent中既可以通过netlink向用户空间程序udevd发送uevent事件,也可以直接调用用户空间程序hotplug_helper

需要注意的是,
如果在device_add中dev_t = 0,则最后udevd不会在/dev目录下创建相应的设备节点
如果在device_add中dev_t != 0,则mdev或者hotplug根据dev中的内容(major:minor)来生成设备节点。
device_add是在/sys/devices/目录下添加设备,/sys/bus/和/sys/class目录下的devices、drivers都是对/sys/devices目录下的文件的符号连接。

Udev完全工作在用户态,利用设备加入或者移除时内核所发送的热插拔事件来工作
在热插拔的时候,设备的详细信息会由内核通过netlink套接字发送出来,发出来的事情叫做的uevent,udev的命名策略、权限控制和事件处理都是在用户态下完成的,他利用从内核收到的信息来创建设备文件节点等工作。
下面这段程序就是用来接收内核netlink发出来的信息,将设备插到系统中会打印从内核中接收到的信息。
Udev就是用这种方式接收netlink的消息,并根据他的内容和用户设置的udev的规则做匹配来进行工作的。

#include <linux/netlink.h>Static void die(char *s){    Write(2, s, strlen(s));    Exit(1);}Int main(int argc, char *argv[]){    Struct sockaddr_nl nls;    Struct pollfd pfd;    Char buf[512];    //open hotplug event netlilnk socket    Memset(nls, 0, sizeof(struct sockaddr_nl));    Nls.nl_family = AF_NETLINK;    Nls.nl_pid = getpid();    Nls.nl_group = -1;    Pfd.event = POLLIN;    Pfd.fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);    If (pfd. Fd == -1)        Die(“no root\n”);    //listen to netlink socket    If (bind(pfd.fd, (void *)&nls, sizeof(struct sockaddr_nl));        Die(“bind failed\n”);    While(-1 != poll(&pfd, 1, -1)) {        Int i, len = recv(pfd.fd, buf, sizeof(buf), MSG_DONTWAIT);        If (len == -1)            Die(“recv\n”);        I = 0;        //print the data to stdout        While(i < len) {            Printf(“%s\n”, buf + i);            I = i + strlen(buf + i) + 1        }    }     Die(“poll\n”);    Return 0;}

可以借助udev的工具udevadm info查找规则文件所能利用的内核信息和sysfs属性信息,
如运行”udevadm info -a -p /sys/devices/platform/serial8250/tty/ttyS0”
如果/dev目录下的节点已经被创建,但是不知道他对应的/sys/具体节点路径,
Udevadm info -q path -n /dev/节点名


参考文章:
1. Linux设备模型(热插拔、mdev 与 firmware)
2. mdev hotplug设备
3. Linux设备模型、sysfs文件系统与udev设备文件

1 0
原创粉丝点击