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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux 设备文件的创建和mdev

發(fā)布時(shí)間:2023/12/20 linux 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux 设备文件的创建和mdev 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

內(nèi)容轉(zhuǎn)載于http://blog.csdn.net/yueqian_scut/article/details/46771595。有格式的調(diào)整和內(nèi)容的刪改,如有侵權(quán),請(qǐng)告知?jiǎng)h除 。


本文將從代碼級(jí)去理解Linux設(shè)備類和設(shè)備文件的創(chuàng)建過程。


一、設(shè)備類相關(guān)知識(shí)

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

二、兩種創(chuàng)建設(shè)備文件的方式

  • 在設(shè)備驅(qū)動(dòng)中,cdev_add將struct file_operations和設(shè)備號(hào)注冊(cè)到系統(tǒng)后,為了能夠自動(dòng)產(chǎn)生驅(qū)動(dòng)對(duì)應(yīng)的設(shè)備文件,需要調(diào)用class_create和device_create,并通過uevent機(jī)制調(diào)用mdev(嵌入式linux由busybox提供)來調(diào)用mknod創(chuàng)建設(shè)備文件。
  • 當(dāng)然也可以不調(diào)用這兩個(gè)接口,那就手工通過命令行mknod來創(chuàng)建設(shè)備文件。

三、設(shè)備類和設(shè)備相關(guān)數(shù)據(jù)結(jié)構(gòu)

(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;//可以成為上一級(jí)父kset的子目錄??
  • ??const?struct?kset_uevent_ops?*uevent_ops;//uevent處理接口??
  • };??
    • 假設(shè)kobject A代表一個(gè)目錄,kset B代表幾個(gè)目錄(包括A)的共同的父目錄。則A.kset=B;A.parent=B.kobj.
    (2)include/linux/device.h [cpp]?view plaincopy
  • struct?class???
  • {//設(shè)備類??
  • ??const?char?*name;?//設(shè)備類名稱??
  • ??struct?module?*owner;//創(chuàng)建設(shè)備類的module??
  • ??struct?class_attribute?*class_attrs;//設(shè)備類屬性??
  • ??struct?device_attribute?*dev_attrs;//設(shè)備屬性??
  • ??struct?kobject?*dev_kobj;//kobject再sysfs中代表一個(gè)目錄??
  • ??…??
  • ??struct?class_private?*p;//設(shè)備類得以注冊(cè)到系統(tǒng)的連接件??
  • };??
  • (3)drivers/base/base.h [cpp]?view plaincopy
  • struct?class_private???
  • {??
  • ??//該設(shè)備類同樣是一個(gè)kset,包含下面的class_devices;同時(shí)在class_subsys填充父kset??
  • ??struct?kset?class_subsys;??
  • ??struct?klist?class_devices;//設(shè)備類包含的設(shè)備(kobject)??
  • ??…??
  • ??struct?class?*class;//指向設(shè)備類數(shù)據(jù)結(jié)構(gòu),即要?jiǎng)?chuàng)建的本級(jí)目錄信息??
  • };??
  • (4)include/linux/device.h

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

    [cpp]?view plaincopy
  • struct?device_private???
  • {??
  • ??struct?klist?klist_children;//連接子設(shè)備??
  • ??struct?klist_node?knode_parent;//加入到父設(shè)備鏈表??
  • ??struct?klist_node?knode_driver;//加入到驅(qū)動(dòng)的設(shè)備鏈表??
  • ??struct?klist_node?knode_bus;//加入到總線的鏈表??
  • ??struct?device?*device;//對(duì)應(yīng)設(shè)備結(jié)構(gòu)??
  • };??
  • (6)解釋

    • class_private是class的私有結(jié)構(gòu),class通過class_private注冊(cè)到系統(tǒng)中;
    • device_private是device的私有結(jié)構(gòu),device通過device_private注冊(cè)到系統(tǒng)中。
    • 所謂注冊(cè)到系統(tǒng)中,即把相應(yīng)的數(shù)據(jù)結(jié)構(gòu)加入到系統(tǒng)已經(jīng)存在的鏈表中,但是這些鏈接的細(xì)節(jié)并不希望暴露給用戶,所以才有private的結(jié)構(gòu)。
    • 而class和device則通過sysfs向用戶層提供信息。

    四、創(chuàng)建設(shè)備類目錄文件

    1、在驅(qū)動(dòng)通過cdev_add將struct?file_operations接口集和設(shè)備注冊(cè)到系統(tǒng)后,即利用class_create接口來創(chuàng)建設(shè)備類目錄文件。

    led_class = class_create(THIS_MODULE, "led_class");

    __class_create(owner, name, &__key);

    cls->name = name;//設(shè)備類名

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

    retval = __class_register(cls, key);

    struct class_private *cp;

    //將類的名字led_class賦值給對(duì)應(yīng)的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

    //創(chuàng)建led_class設(shè)備類目錄

    kset_register(&cp->class_subsys);

    //在led_class目錄創(chuàng)建class屬性文件

    add_class_attrs(class_get(cls));


    2、繼續(xù)展開kset_register

    kset_register(&cp->class_subsys);

    kobject_add_internal(&k->kobj);

    // parent即class_kset.kobj,即/sysfs/class對(duì)應(yīng)的目錄

    parent = kobject_get(kobj->parent);

    create_dir(kobj);

    //創(chuàng)建一個(gè)led _class設(shè)備類目錄

    sysfs_create_dir(kobj);

    //該接口是sysfs文件系統(tǒng)接口,代表創(chuàng)建一個(gè)目錄,不再展開。


    3.上述提到的class_kset在class_init被創(chuàng)建

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

    第三個(gè)傳參為NULL,代表默認(rèn)在/sysfs/創(chuàng)建class目錄。


    五、創(chuàng)建設(shè)備目錄和設(shè)備屬性文件

    1、利用class_create接口來創(chuàng)建設(shè)備類目錄文件后,再利用device_create接口來創(chuàng)建具體設(shè)備目錄和設(shè)備屬性文件。

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

    device_create_vargs

    dev->devt = devt;//設(shè)備號(hào)

    dev->class = class;//設(shè)備類led_class

    dev->parent = parent;//父設(shè)備,這里是NULL

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

    device_register(dev)注冊(cè)設(shè)備


    2、繼續(xù)展開device_register(dev)

    device_initialize(dev);

    dev->kobj.kset = devices_kset;//設(shè)備所屬/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和父設(shè)備的kobject的聯(lián)系
    //kobject_add在/sysfs/devices/目錄下創(chuàng)建設(shè)備目錄led,kobject_add是和kset_register相似的接口,只不過前者針對(duì)kobject,后者針對(duì)kset。
    kobject_add(&dev->kobj, dev->kobj.parent, NULL);
    kobject_add_varg
    kobj->parent = parent;
    kobject_add_internal(kobj)
    create_dir(kobj);//創(chuàng)建設(shè)備目錄
    //在剛創(chuàng)建的/sysfs/devices/led目錄下創(chuàng)建uevent屬性文件,名稱是”uevent”
    device_create_file(dev, &uevent_attr);
    //在剛創(chuàng)建的/sysfs/devices/led目錄下創(chuàng)建dev屬性文件,名稱是”dev”,該屬性文件的內(nèi)容就是設(shè)備號(hào)
    device_create_file(dev, &devt_attr);
    //在/sysfs/class/led_class/目錄下建立led設(shè)備的符號(hào)連接,所以打開/sysfs/class/led_class/led/目錄也能看到dev屬性文件,讀出設(shè)備號(hào)。
    device_add_class_symlinks(dev);
    //創(chuàng)建device屬性文件,包括設(shè)備所屬總線的屬性和attribute_group屬性
    device_add_attrs()
    bus_add_device(dev) //將設(shè)備加入總線
    //觸發(fā)uevent機(jī)制,并通過調(diào)用mdev來創(chuàng)建設(shè)備文件。
    kobject_uevent(&dev->kobj, KOBJ_ADD);
    //匹配設(shè)備和總線的驅(qū)動(dòng),匹配成功就調(diào)用驅(qū)動(dòng)的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即設(shè)備所屬的設(shè)備類的名稱”led_class”
    subsystem = uevent_ops->name(kset,
    ?kobj);
    //devpath即/sysfs/devices/led/
    devpath = kobject_get_path(kobj,
    ?GFP_KERNEL);
    //添加各種環(huán)境變量
    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);
    //還會(huì)增加總線相關(guān)的一些屬性環(huán)境變量等等。
    #if defined(CONFIG_NET)//如果是PC的linux會(huì)通過socket的方式向應(yīng)用層發(fā)送uevent事件消息,但在嵌入式linux中不啟用該機(jī)制。
    #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被創(chuàng)建
    devices_kset = kset_create_and_add("devices",
    ?&device_uevent_ops, NULL);

    //第三個(gè)傳參為NULL,代表默認(rèn)在/sysfs/創(chuàng)建devices目錄


    5、上述設(shè)備屬性文件

    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);//即返回設(shè)備的設(shè)備號(hào)

    }


    6、devices設(shè)備目錄響應(yīng)uevent事件的操作

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

    };


    7、call_usermodehelper是從內(nèi)核空間調(diào)用用戶空間程序的接口。

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

    uevent_helper[]數(shù)組即讀入/proc/sys/kernel/hotplug文件的內(nèi)容,即“/sbin/mdev” .


    六、創(chuàng)建設(shè)備文件

    • 以上描述都是在sysfs文件系統(tǒng)中創(chuàng)建目錄或者文件,而應(yīng)用程序訪問的設(shè)備文件則需要?jiǎng)?chuàng)建在/dev/目錄下。該項(xiàng)工作由mdev完成
    • Mdev的原理:解釋/etc/mdev.conf文件定義的命名設(shè)備文件的規(guī)則,并在該規(guī)則下根據(jù)環(huán)境變量的要求來創(chuàng)建設(shè)備文件。
    • Mdev.conf由用戶層指定,因此更具靈活性。
    • 下面是mdev配置腳本,最終我們會(huì)跟蹤到mknod在/dev/目錄下創(chuàng)建了設(shè)備文件。
    [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")//系統(tǒng)啟動(dòng)時(shí)mdev??
  • ?–s才會(huì)執(zhí)行這個(gè)分支??
  • 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屬性文件,得到設(shè)備號(hào)??
  • open_read_close(path,dev_maj_min?+?1,?64);??
  • ….??
  • mknod(node_name,rule->mode?|?type,?makedev(major,?minor)) ?
  • 總結(jié)

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

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。