日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux 设备文件的创建和mdev

發布時間:2023/12/20 linux 53 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux 设备文件的创建和mdev 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

內容轉載于http://blog.csdn.net/yueqian_scut/article/details/46771595。有格式的調整和內容的刪改,如有侵權,請告知刪除 。


本文將從代碼級去理解Linux設備類和設備文件的創建過程。


一、設備類相關知識

  • 設備類是虛擬的,并沒有直接對應的物理實物,只是為了更好地管理同一類設備導出到用戶空間而產生的目錄和文件。
  • 整個過程涉及到sysfs文件系統,該文件系統是為了展示Linux設備驅動模型而構建的文件系統,是基于ramfs,linux根目錄中的/sysfs即掛載了sysfs文件系統。
  • Struct kobject數據結構是sysfs的基礎,kobject在sysfs中代表一個目錄;
  • linux的驅動(struct?driver)、設備(struct?device)、設備類(struct?class)均是從kobject進行派生的,因此他們在sysfs中都對應于一個目錄。
  • 數據結構中附屬的struct?device_attribute、driver_attribute、class_attribute等屬性數據結構在sysfs中則代表一個普通的文件。
  • Struct kset是struct kobject的容器,即Struct kset可以成為同一類struct?kobject的父親,而其自身也有kobject成員,因此其又可能和其他kobject成為上一級kset的子成員。

二、兩種創建設備文件的方式

  • 在設備驅動中,cdev_add將struct file_operations和設備號注冊到系統后,為了能夠自動產生驅動對應的設備文件,需要調用class_create和device_create,并通過uevent機制調用mdev(嵌入式linux由busybox提供)來調用mknod創建設備文件。
  • 當然也可以不調用這兩個接口,那就手工通過命令行mknod來創建設備文件。

三、設備類和設備相關數據結構

(1)include/linux/kobject.h [cpp]?view plaincopy
  • struct?kobject???
  • {??
  • ??const?char?*name;//名稱??
  • ??struct?list_head?entry;//kobject鏈表??
  • ??struct?kobject?*parent;//即所屬kset的kobject??
  • ??struct?kset?*kset;//所屬kset??
  • ??struct?kobj_type?*ktype;//屬性操作接口??
  • ??…??
  • };??
  • ??
  • struct?kset???
  • {??
  • ??struct?list_head?list;//管理同屬于kset的kobject??
  • ??struct?kobject?kobj;//可以成為上一級父kset的子目錄??
  • ??const?struct?kset_uevent_ops?*uevent_ops;//uevent處理接口??
  • };??
    • 假設kobject A代表一個目錄,kset B代表幾個目錄(包括A)的共同的父目錄。則A.kset=B;A.parent=B.kobj.
    (2)include/linux/device.h [cpp]?view plaincopy
  • struct?class???
  • {//設備類??
  • ??const?char?*name;?//設備類名稱??
  • ??struct?module?*owner;//創建設備類的module??
  • ??struct?class_attribute?*class_attrs;//設備類屬性??
  • ??struct?device_attribute?*dev_attrs;//設備屬性??
  • ??struct?kobject?*dev_kobj;//kobject再sysfs中代表一個目錄??
  • ??…??
  • ??struct?class_private?*p;//設備類得以注冊到系統的連接件??
  • };??
  • (3)drivers/base/base.h [cpp]?view plaincopy
  • struct?class_private???
  • {??
  • ??//該設備類同樣是一個kset,包含下面的class_devices;同時在class_subsys填充父kset??
  • ??struct?kset?class_subsys;??
  • ??struct?klist?class_devices;//設備類包含的設備(kobject)??
  • ??…??
  • ??struct?class?*class;//指向設備類數據結構,即要創建的本級目錄信息??
  • };??
  • (4)include/linux/device.h

    [cpp]?view plaincopy
  • struct?device???
  • {//設備??
  • ??struct?device?*parent;//sysfs/devices/中的父設備??
  • ??struct?device_private?*p;//設備得以注冊到系統的連接件??
  • ??struct?kobject?kobj;//設備目錄??
  • ??const?char?*init_name;//設備名稱??
  • ??struct?bus_type?*bus;//設備所屬總線??
  • ??struct?device_driver?*driver;?//設備使用的驅動??
  • ??struct?klist_node?knode_class;//連接到設備類的klist??
  • ??struct?class?*class;//所屬設備類??
  • ??const?struct?attribute_group?**groups;??
  • ??…??
  • }??
  • (5)drivers/base/base.h

    [cpp]?view plaincopy
  • struct?device_private???
  • {??
  • ??struct?klist?klist_children;//連接子設備??
  • ??struct?klist_node?knode_parent;//加入到父設備鏈表??
  • ??struct?klist_node?knode_driver;//加入到驅動的設備鏈表??
  • ??struct?klist_node?knode_bus;//加入到總線的鏈表??
  • ??struct?device?*device;//對應設備結構??
  • };??
  • (6)解釋

    • class_private是class的私有結構,class通過class_private注冊到系統中;
    • device_private是device的私有結構,device通過device_private注冊到系統中。
    • 所謂注冊到系統中,即把相應的數據結構加入到系統已經存在的鏈表中,但是這些鏈接的細節并不希望暴露給用戶,所以才有private的結構。
    • 而class和device則通過sysfs向用戶層提供信息。

    四、創建設備類目錄文件

    1、在驅動通過cdev_add將struct?file_operations接口集和設備注冊到系統后,即利用class_create接口來創建設備類目錄文件。

    led_class = class_create(THIS_MODULE, "led_class");

    __class_create(owner, name, &__key);

    cls->name = name;//設備類名

    cls->owner = owner;//所屬module

    retval = __class_register(cls, key);

    struct class_private *cp;

    //將類的名字led_class賦值給對應的kset

    kobject_set_name(&cp->class_subsys.kobj, "%s", cls->name);

    //填充class_subsys所屬的父kset:ket:sysfs/class.

    cp->class_subsys.kobj.kset = class_kset;

    //填充class屬性操作接口

    cp->class_subsys.kobj.ktype = &class_ktype;

    cp->class = cls;//通過cp可以找到class

    cls->p = cp;//通過class可以找到cp

    //創建led_class設備類目錄

    kset_register(&cp->class_subsys);

    //在led_class目錄創建class屬性文件

    add_class_attrs(class_get(cls));


    2、繼續展開kset_register

    kset_register(&cp->class_subsys);

    kobject_add_internal(&k->kobj);

    // parent即class_kset.kobj,即/sysfs/class對應的目錄

    parent = kobject_get(kobj->parent);

    create_dir(kobj);

    //創建一個led _class設備類目錄

    sysfs_create_dir(kobj);

    //該接口是sysfs文件系統接口,代表創建一個目錄,不再展開。


    3.上述提到的class_kset在class_init被創建

    class_kset = kset_create_and_add("class", NULL, NULL);

    第三個傳參為NULL,代表默認在/sysfs/創建class目錄。


    五、創建設備目錄和設備屬性文件

    1、利用class_create接口來創建設備類目錄文件后,再利用device_create接口來創建具體設備目錄和設備屬性文件。

    led_device = device_create(led_class, NULL, led_devno, NULL, "led");

    device_create_vargs

    dev->devt = devt;//設備號

    dev->class = class;//設備類led_class

    dev->parent = parent;//父設備,這里是NULL

    kobject_set_name_vargs(&dev->kobj, fmt, args)//設備名”led”

    device_register(dev)注冊設備


    2、繼續展開device_register(dev)

    device_initialize(dev);

    dev->kobj.kset = devices_kset;//設備所屬/sysfs/devices/

    device_add(dev)

    device_private_init(dev)//初始化device_private

    dev_set_name(dev, "%s", dev->init_name);//賦值dev->kobject的名稱
    setup_parent(dev, parent);//建立device和父設備的kobject的聯系
    //kobject_add在/sysfs/devices/目錄下創建設備目錄led,kobject_add是和kset_register相似的接口,只不過前者針對kobject,后者針對kset。
    kobject_add(&dev->kobj, dev->kobj.parent, NULL);
    kobject_add_varg
    kobj->parent = parent;
    kobject_add_internal(kobj)
    create_dir(kobj);//創建設備目錄
    //在剛創建的/sysfs/devices/led目錄下創建uevent屬性文件,名稱是”uevent”
    device_create_file(dev, &uevent_attr);
    //在剛創建的/sysfs/devices/led目錄下創建dev屬性文件,名稱是”dev”,該屬性文件的內容就是設備號
    device_create_file(dev, &devt_attr);
    //在/sysfs/class/led_class/目錄下建立led設備的符號連接,所以打開/sysfs/class/led_class/led/目錄也能看到dev屬性文件,讀出設備號。
    device_add_class_symlinks(dev);
    //創建device屬性文件,包括設備所屬總線的屬性和attribute_group屬性
    device_add_attrs()
    bus_add_device(dev) //將設備加入總線
    //觸發uevent機制,并通過調用mdev來創建設備文件。
    kobject_uevent(&dev->kobj, KOBJ_ADD);
    //匹配設備和總線的驅動,匹配成功就調用驅動的probe接口,不再展開

    bus_probe_device(dev);


    3、展開kobject_uevent(&dev->kobj, KOBJ_ADD);

    kobject_uevent_env(kobj, action, NULL);
    kset = top_kobj->kset;?
    uevent_ops = kset->uevent_ops; //即device_uevent_ops
    // subsystem即設備所屬的設備類的名稱”led_class”
    subsystem = uevent_ops->name(kset,
    ?kobj);
    //devpath即/sysfs/devices/led/
    devpath = kobject_get_path(kobj,
    ?GFP_KERNEL);
    //添加各種環境變量
    add_uevent_var(env, "ACTION=%s",
    ?action_string);
    add_uevent_var(env, "DEVPATH=%s",
    ?devpath);
    add_uevent_var(env, "SUBSYSTEM=%s",
    ?subsystem);
    uevent_ops->uevent(kset, kobj,
    ?env);
    add_uevent_var(env, "MAJOR=%u",
    ?MAJOR(dev->devt));
    add_uevent_var(env, "MINOR=%u",
    ?MINOR(dev->devt));
    add_uevent_var(env, "DEVNAME=%s",
    ?name);
    add_uevent_var(env, "DEVTYPE=%s",
    ?dev->type->name);
    //還會增加總線相關的一些屬性環境變量等等。
    #if defined(CONFIG_NET)//如果是PC的linux會通過socket的方式向應用層發送uevent事件消息,但在嵌入式linux中不啟用該機制。
    #endif
    argv [0] = uevent_helper;//即/sbin/mdev
    argv [1] = (char *)subsystem;//”led_class”
    argv [2] = NULL;
    add_uevent_var(env, "HOME=/");
    add_uevent_var(env,"PATH=/sbin:/bin:/usr/sbin:/usr/bin");
    call_usermodehelper(argv[0],
    ?argv,env->envp, UMH_WAIT_EXEC);

    4、上述提到的devices_kset在devices_init被創建
    devices_kset = kset_create_and_add("devices",
    ?&device_uevent_ops, NULL);

    //第三個傳參為NULL,代表默認在/sysfs/創建devices目錄


    5、上述設備屬性文件

    static struct device_attribute devt_attr =
    __ATTR(dev,
    ?S_IRUGO, show_dev, NULL);


    static ssize_t show_dev(struct device *dev, struct device_attribute
    ?*attr,char *buf){{
    return
    ?print_dev_t(buf, dev->devt);//即返回設備的設備號

    }


    6、devices設備目錄響應uevent事件的操作

    static const struct kset_uevent_ops device_uevent_ops = {
    .filter =dev_uevent_filter,
    .name = dev_uevent_name,
    .uevent = dev_uevent,

    };


    7、call_usermodehelper是從內核空間調用用戶空間程序的接口。

    8、對于嵌入式系統來說,busybox采用的是mdev,在系統啟動腳本rcS中會使用命令“echo /sbin/mdev > /proc/sys/kernel/hotplug”。

    uevent_helper[]數組即讀入/proc/sys/kernel/hotplug文件的內容,即“/sbin/mdev” .


    六、創建設備文件

    • 以上描述都是在sysfs文件系統中創建目錄或者文件,而應用程序訪問的設備文件則需要創建在/dev/目錄下。該項工作由mdev完成
    • Mdev的原理:解釋/etc/mdev.conf文件定義的命名設備文件的規則,并在該規則下根據環境變量的要求來創建設備文件。
    • Mdev.conf由用戶層指定,因此更具靈活性。
    • 下面是mdev配置腳本,最終我們會跟蹤到mknod在/dev/目錄下創建了設備文件。
    [cpp]?view plaincopy
  • Busybox/util-linux/mdev.c??
  • int?mdev_main(int?argc?UNUSED_PARAM,?char?**argv)??
  • xchdir("/dev");??
  • if?(argv[1]?&&?strcmp(argv[1],?"-s")//系統啟動時mdev??
  • ?–s才會執行這個分支??
  • else??
  • action=?getenv("ACTION");??
  • env_path=?getenv("DEVPATH");??
  • G.subsystem=?getenv("SUBSYSTEM");??
  • snprintf(temp,PATH_MAX,?"/sys%s",?env_path);//到/sysfs/devices/led目錄??
  • make_device(temp,/*delete:*/?0);??
  • strcpy(dev_maj_min,"/dev");?//讀出dev屬性文件,得到設備號??
  • open_read_close(path,dev_maj_min?+?1,?64);??
  • ….??
  • mknod(node_name,rule->mode?|?type,?makedev(major,?minor)) ?
  • 總結

    以上是生活随笔為你收集整理的Linux 设备文件的创建和mdev的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。