zoukankan      html  css  js  c++  java
  • linux下udev简介【转】

    本文转载自:http://blog.csdn.net/skyflying2012/article/details/9364555

    一.关于Udev

    u即user space,dev是device,通过它的名字,我们就可以简单了解到,它是一个和用户态相关的驱动设备管理机制。udev是一个针对2.6内核的文件系统。提供一种基于用户空间的动态设备节点管理和命名的解决方案。用于取代落后的devfs

    udev与硬件平台无关,属于用户空间的进程,是一个后台程序,它脱离驱动层的关联,而建立在操作系统之上,只要修改配置文件使之生效,无需重启操 作系统,它需要sysfs的支持,当底层设备发生插拔的时候,底层驱动通过netlink发送事件(uevent)给udev后台程序,udev监听这些 事件,并在上层做相应的设备节点的创建,命名,权限控制等。

    它有以下优点:

    1.动态管理:当设备添加/删除时,udev的守护进程侦听到来自内核的uevent,以此添加或者删除/dev下的设备文件,所以,udev只为 已经连接的设备产生设备文件,而不会在/dev/下产生大量虚无的设备文件.在发生热插拔时,设备的变化的相关信息会输出到内核的/sys(sysfs文 件系统),udev利用sysfs的信息来进行相应的设备节点的管理

    2.自定义命名规则:通过规则文件,udev在/dev/下为所有的设备定义了内核设备名称,比如/dev/sda,/dev/hda,/dev /fd(这些都是驱动层定义的设备名)等等。由于udev是在用户空间运行,Linux用户可以自己定义规则文件,产生标识性强的设备文件,比如/dev /boot_disk,/dev/root_disk,/dev/color_printer等等

    3.设定设备的权限和所有者/组。同样在规则文件中,可以自己定义设备相关的权限和所有者/组

    工作流程:

    二.uevent的交互

    如之前提到过的,udev必须要有sysfs的支持,sysfs是一个建立在内存基础上的文件系统,它把连接在系统上的设备和总线组织成一个分级的文件,它们可以由用户空间获取,向用户空间导出内核数据结构以及它们的属性,它建立在内核对象kobject的基础上。

    在内核空间,当系统启动加载驱动或设备发生热插拔的时候,驱动自身需要做相应的硬件探测方面的工作,探测到设备后,会去加载相应的设备驱动,在 sysfs下创建添加内核对象,会调用到kobject_add()来完成该内核对象的添加注册,再调用kobject_uevent()来通知系统,该 对象已经添加进来了。kobject_uevent()函数是uevent的关键函数,它将通过netlink socket把对象相应的信息,属性等发 给上层用户空间。

    int device_add(struct device *dev)

    {

    struct device *parent = NULL;

    struct class_interface *class_intf;

    int error = -EINVAL;

    dev = get_device(dev);

    if (!dev)

    goto done;

       .......

    /* first, register with generic layer. */

    error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev_name(dev));

    if (error)

    goto Error;

    .......

    kobject_uevent(&dev->kobj, KOBJ_ADD);

    bus_attach_device(dev);

    if (parent)

    klist_add_tail(&dev->knode_parent, &parent->klist_children);

       .......

     attrError:

    kobject_uevent(&dev->kobj, KOBJ_REMOVE);

    kobject_del(&dev->kobj);

     Error:

    cleanup_device_parent(dev);

    if (parent)

    put_device(parent);

    goto done;

    }

    上面的代码段为我们常见的添加注册设备时的会调用到的接口,device_add()函数,删除了一些无关代码,可以看出,是先调用了 kobject_add()创建添加该内核对象,然后调用kobject_uevent()来通知系统uevent的变化,这里的action是 KOBJ_ADD,相对应还有

    enum kobject_action {

    KOBJ_ADD,

    KOBJ_REMOVE,

    KOBJ_CHANGE,

    KOBJ_MOVE,

    KOBJ_ONLINE,

    KOBJ_OFFLINE,

    KOBJ_MAX

    };

    在kobject_uevent()里面采用的就是linux中比较经典的内核空间和用户空间的一种通信机制netlink socket,这个不 是udev的重点,我也不做过多的解释,总之相信它能让内核空间和用户空间进行通信就行了。在udev也会有相应的socket来接受底层的消息。如下为 参照udev源码写的一个简单的uevent消息侦听程序:

    #define UEVENT_BUFFER_SIZE      2048

    static int init_hotplug_sock(void)

    {

        struct sockaddr_nl snl;

        const int buffersize = 16 * 1024 * 1024;

        int retval;

        memset(&snl, 0x00, sizeof(struct sockaddr_nl));

        snl.nl_family = AF_NETLINK;

        snl.nl_pid = getpid();

        snl.nl_groups = 1;

        int hotplug_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);

        if (hotplug_sock == -1) {

            printf("error getting socket: %s", strerror(errno));

            return -1;

        }

        /* set receive buffersize */

        setsockopt(hotplug_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize));

        retval = bind(hotplug_sock, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl));

        if (retval < 0) {

            printf("bind failed: %s", strerror(errno));

            close(hotplug_sock);

            hotplug_sock = -1;

            return -1;

        }

        return hotplug_sock;

    }

    int main(int argc, char* argv[])

    {

             int hotplug_sock       = init_hotplug_sock();

              while(1)

              {

    //printf("sunqidong debug ");

                       char buf[UEVENT_BUFFER_SIZE*2] = {0};

                       recv(hotplug_sock, &buf, sizeof(buf), 0); 

                       printf("%s ", buf);

              }

             return 0;

    }

    这也是一个后台服务程序,循环的执行接受底层的消息过来,当发生U盘的插拔时,会产生如下的log:

    [root@localhost test]# ./hotplug

    add@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-1

    add@/class/usb_endpoint/usbdev1.5_ep00

    add@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-1/1-1:1.0

    add@/class/scsi_host/host6

    add@/class/usb_endpoint/usbdev1.5_ep81

    add@/class/usb_endpoint/usbdev1.5_ep02

    add@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-1/1-1:1.0/host6/target6:0:0/6:0:0:0

    add@/class/scsi_disk/6:0:0:0

    add@/block/sdb

    add@/block/sdb/sdb1

    add@/block/sdb/sdb2

    add@/block/sdb/sdb5

    add@/block/sdb/sdb6

    add@/block/sdb/sdb7

    add@/block/sdb/sdb8

    add@/class/scsi_device/6:0:0:0

    add@/class/scsi_generic/sg2

    add@/class/bsg/6:0:0:0

    remove@/class/usb_endpoint/usbdev1.5_ep81

    remove@/class/usb_endpoint/usbdev1.5_ep02

    remove@/class/bsg/6:0:0:0

    remove@/class/scsi_generic/sg2

    remove@/class/scsi_device/6:0:0:0

    remove@/class/scsi_disk/6:0:0:0

    remove@/block/sdb/sdb8

    remove@/block/sdb/sdb7

    remove@/block/sdb/sdb6

    remove@/block/sdb/sdb5

    remove@/block/sdb/sdb2

    remove@/block/sdb/sdb1

    remove@/block/sdb

    remove@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-1/1-1:1.0/host6/target6:0:0/6:0:0:0

    remove@/class/scsi_host/host6

    remove@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-1/1-1:1.0

    remove@/class/usb_endpoint/usbdev1.5_ep00

    remove@/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-1

    三.udev的规则文件     规则文件是udev中最重要的部分,默认是存放在/etc/udev/rules.d/下。所有的规则文件都必须以".rules"为后缀名。下面是一个简单的规则文件例子说明

          KERNEL=="sdb8" ,  NAME="mydisk",MODE="0660"

     KERNEL是匹配键,NAME和MODE是赋值键。"=="这是判断语句,"="是赋值语句,这条规则的意思是,如果有一个设备的内核设备名称为sdb8(我移动硬盘内的一个分区),则该条件生效,执行后面的赋值:在/dev/下产生名为mydisk的设备文件,并把该设备文件的权限设为0660.

         通过这条简单的规则,应该就可以对规则文件有了个基本的了解。每个规则文件被分成一个或多个匹配和赋值部分。匹配部分用匹配专用的关键字来表示,相应的赋值部分用赋值专用的字来表示。

         1.常见的匹配关键字: ACTION(用于匹配行为add/remove),KERNEL(内核中定义的设备名),BUS(用于匹配总 线类型),SYSFS(用于匹配从sysfs得到的信息,比如lable,vendor,USB序列号等),SUBSYSTEM(匹配子系统名)等

         2.常见的赋值关键字:

    NAME(创建的设备文件名),SYMLINK(符号创建链接名),OWNER(设置设备的所有者),GROUP(设置设备的组),IMPORT(调用外部程序),MODE(权限位)

    四.xx项目上自动挂载usb存储设备的应用

    在xx项目上,我们需要自动挂载usb存储设备,并且要支持常见的几种文件系统,fat32,ntfs,exfat等,其中fat32等fat系列,Linux下早就有支持,ntfs和exfat目前的内核自身还没支持,我们有关于这两个文件系统的内核模块文件tntfs,ko和texfat.ko,加载进去过后就能让我们的内核识别这两种文件系统,实现手动加载这两种格式的存储设备。但如果要支持自动加载还有问题,需要去修改相应的规则文件。

    在加载的时候,不同的格式的文件系统,加载的参数是不一样的,如exfat为

    mount -t texfat /dev/sda /mnt/udisk

    而ntfs为

    mount -t tntfs /dev/sda /mnt/udisk

    并且针对不同的格式,还有些其他挂载选项参数不一样,所以对不同的格式需要区别对待。

    在规则文件里面是通过blkid -o udev命令来获取文件系统的信息的,判断出该盘是哪种格式,再去执行不同的挂载命令。

    如下是blkid -o udev读出来的文件系统格式信息

    ID_FS_UUID=2EE054B8E054884B

    ID_FS_UUID_ENC=2EE054B8E054884B

    ID_FS_LABEL=disk3

    ID_FS_LABEL_ENC=disk3

    ID_FS_TYPE=ntfs

    ID_FS_LABEL=DISK4

    ID_FS_LABEL_ENC=DISK4

    ID_FS_UUID=B8CF-FF22

    ID_FS_UUID_ENC=B8CF-FF22

    ID_FS_TYPE=vfat

    ID_FS_UUID=3606-1B2C

    ID_FS_UUID_ENC=3606-1B2C

    ID_FS_TYPE=exfat

    ID_FS_LABEL=Disk5

    ID_FS_LABEL_ENC=Disk5

    可以看到上面的几个赋值项,在规则文件里面就会去读这些值。做相应的判断,实现不同文件系统的区别对待挂载

    如下为规则文件的一部分

    KERNEL!="sd[a-z][0-9]", GOTO="media_by_label_auto_mount_end"

    # Import FS infos

    IMPORT{program}="/sbin/blkid -o udev -p %N"

    ENV{ID_FS_LABEL}!="", ENV{dir_name}="%E{ID_FS_LABEL}"

    ENV{ID_FS_LABEL}=="", ENV{dir_name}="usb-%k"

    #vfat,fat

    ACTION=="add",ENV{ID_FS_TYPE}=="vfat|fat",RUN+="/bin/mkdir -p /mnt/udisk/%E{dir_name}",ENV{mount_options}="$env{mount_options},gid=100,umask=000",RUN+="/bin/mount -o $env{mount_options},iocharset=utf8 /dev/%k /mnt/udisk/%E{dir_name}"

    #ntfs

    ACTION=="add",ENV{ID_FS_TYPE}=="ntfs",RUN+="/bin/mkdir -p /mnt/udisk/%E{dir_name}",ENV{mount_options}="$env{mount_options},gid=100,umask=000",RUN+="/bin/mount -t tntfs -o $env{mount_options},iostreaming /dev/%k /mnt/udisk/%E{dir_name}"

    #exfat

    ACTION=="add",ENV{ID_FS_TYPE}=="exfat",RUN+="/bin/mkdir -p /mnt/udisk/%E{dir_name}",RUN+="/bin/mount -t texfat -o rw /dev/%k /mnt/udisk/%E{dir_name}"

    关于blkid,在我们目前的文件系统里面,blkid是不支持exfat格式的,通过命令查看磁盘的信息,根本找不到exfat格式的磁盘。所以之前在做自动挂载的时候没法实现挂exfat,后来在网上找了个util-linux-ng2.18源码包,里面包含了blkid的源码。修改编译,编译出一个新的blkid文件,使其可以在我们的系统上运行,能够识别出exfat文件

  • 相关阅读:
    我的大学(转作者袋袋鼠)
    Reporting Services使用问题
    爱的真谛(转)
    无题
    工作???
    带老弟做项目,凉了
    学妹问,学网站开发还是打 ACM?
    在字节跳动的实习经历分享 | 万字求职指南
    云开发是啥?看看它在编程导航项目的实践
    我删库跑路失败了
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/7363944.html
Copyright © 2011-2022 走看看