多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > 綜合技術 > Linux驅動之Kobject、Kset (二)uevent mdev

Linux驅動之Kobject、Kset (二)uevent mdev

來源:程序員人生   發布時間:2016-06-22 08:53:04 閱讀次數:3243次

LDD3中說,Kobject的作用為:

    1、sysfs 表述:在 sysfs 中出現的每一個對象都對應1個 kobject, 它和內核交互來創建它的可見表述。

    2、熱插拔事件處理 :kobject 子系統將產生的熱插拔事件通知用戶空間。
    3、數據結構關聯:整體來看, 裝備模型是1個極端復雜的數據結構,通過其間的大量鏈接而構成1個多層次的體系結構。kobject 實現了該結構并將其聚合在1起。 

    其中,第1條已在前1篇文章中介紹過了,如果不了解請移駕 http://blog.csdn.net/lizuobin2/article/details/51523693

    此文,將從裝備總線驅動模型里的裝備注冊進程, device_register函數入手,分析kobject、kset 在裝備這1層面的體系結構,同時主要是分析uevent機制和 mdev 如何自動創建裝備節點,實現自己想要的1些功能,比如U盤自動掛載。


全部裝備的起源,應當是/drives/base/core.c 在這里實現了1系列函數,并導出供我們使用。

        EXPORT_SYMBOL_GPL(device_for_each_child);
        EXPORT_SYMBOL_GPL(device_find_child);

        EXPORT_SYMBOL_GPL(device_initialize);
        EXPORT_SYMBOL_GPL(device_add);
        EXPORT_SYMBOL_GPL(device_register);

        EXPORT_SYMBOL_GPL(device_del);
        EXPORT_SYMBOL_GPL(device_unregister);
        EXPORT_SYMBOL_GPL(get_device);
        EXPORT_SYMBOL_GPL(put_device);

        EXPORT_SYMBOL_GPL(device_create_file);
        EXPORT_SYMBOL_GPL(device_remove_file);

 

在內核 do_base_setup 初始化的進程中調用driver_init函數,間接調用device_init函數,我們先來看看device_init函數。

    int __init devices_init(void)

    {
              devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);

        ……
    }

    創建kset 并 add 到內核里去,它的名字是devices,parent==NULL,devices_kset 對應于/sys/devices目錄,device_uevent_ops后面分析。


    在裝備總線驅動模型中,我們要構造1個 device 結構對象,設置它所屬的總線(i2c_bus_type、platform_bus_type…),然后將它注冊到內核中去,其中都避免不了調用 device_register 函數。現在我們來看 device_register

    int device_register(struct device *dev)
    {
        device_initialize(dev);
         //dev.devt = MKDEV(xxx, yyyy);  // 有些時候會提供裝備的 主次裝備號
        return device_add(dev);

         ......          

    }

    void device_initialize(struct device *dev)
    {
        dev->kobj.kset = devices_kset;   // 將裝備的 kset 成員指向 devices_kset
        kobject_init(&dev->kobj, &device_ktype);   // 初始化 裝備的 kobject 成員,并設置它的 Ktype 為 device_ktype,并沒有add
        ......

    }

    int device_add(struct device *dev)
    {
         // 將 dev 的 kobject 成員鏈入 device_kset 鏈表,并在/sys/devices/目錄下創建子目錄
        error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);

         // 創建屬性 dev 文件 /sys/devices/xxx/dev ,后邊我們會知道 cat dev 會得到裝備號,供 mdev 來創建裝備節點  
          if (MAJOR(dev->devt)) {
             error = device_create_file(dev, &devt_attr); // 值得1看

             error = device_create_sys_dev_entry(dev);
         }
         // 上報1個 KOBJ_ADD 事件
         kobject_uevent(&dev->kobj, KOBJ_ADD);   
    }

    至此,我們可以發現,今后每個創建 device ,只要你調用 device_register ,device 的 Kobject 都將鏈入device_kset 鏈表,然后通過contain_of 函數,就能夠實現對 device 的訪問。也就是說 kobject 常常是嵌入在其他模塊中,通過Kobject、kset之前的關系,實現對更大的模塊的關系管理。 也就是我們說的 Kobject 作用的第3條。


現在我們來看看 kobject_uevent

    int kobject_uevent(struct kobject *kobj, enum kobject_action action)
    {
        return kobject_uevent_env(kobj, action, NULL);
    }

    int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
               char *envp_ext[])
    {

         /* 如果kobject 不屬于1個Kset,則向上查找到1個 屬于1個kset的kobject為止 */
            top_kobj = kobj;
            while (!top_kobj->kset && top_kobj->parent)
            top_kobj = top_kobj->parent;

                kset = top_kobj->kset;    // 找到 最接近的 kset,這里就是device_kset

                uevent_ops = kset->uevent_ops;    // 獲得 uevent_ops == dev_uevent

            // 如果 uevent_suppress 被設置 則屏蔽 uevent
            // 如果設置了 filter 則用 filter 過濾事件,稍后我們會看,只要設置了 bus 或 class 的device 都會通過

            // 調用name函數得到subsystem的名字;否則,subsystem的名字是kset中kobject的名字
            if (uevent_ops && uevent_ops->name)
                subsystem = uevent_ops->name(kset, kobj);
            else
                subsystem = kobject_name(&kset->kobj);

         /*  申請env內存 */
            env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);

        /* 獲得Path 也就是kobj的路徑 /sys/devices/xxx */
            devpath = kobject_get_path(kobj, GFP_KERNEL);

        /*  設置環境變量 */
            retval = add_uevent_var(env, "ACTION=%s", action_string);  // KOBJ_ADD

            retval = add_uevent_var(env, "DEVPATH=%s", devpath);  // 路徑

            retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);   // 子系統的名字

        // 如果 uevent_ops->uevent 存在則調用,明顯存在,后面分析。
               if (uevent_ops && uevent_ops->uevent) {
                   retval = uevent_ops->uevent(kset, kobj, env);
               }

        retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
               // uevent_helper[0] == /sbin/mdev     這個是通過 /etc/ini.d/rcS 指定的
               if (uevent_helper[0]) {
                    char *argv [3];

                    argv [0] = uevent_helper;
                    argv [1] = (char *)subsystem;
                    argv [2] = NULL;
                    retval = add_uevent_var(env, "HOME=/");
                    retval = add_uevent_var(env,"PATH=/sbin:/bin:/usr/sbin:/usr/bin");

                    // 調用用戶空間程序,程序名 argv[0], 并把環境變量當作參數傳遞過去

                    retval = call_usermodehelper(argv[0], argv, env->envp, UMH_WAIT_EXEC);
               }

    }

   明顯 uevent 的機制,就是設置環境變量,然后調用用戶空間程序 mdev 進行更新裝備。


上面屢次使用到了device_kset->uevent_fops,是時候來看看了(并沒有甚么卵用)

    static struct kset_uevent_ops device_uevent_ops = {
        .filter =        dev_uevent_filter, // 只要設置了bus or class 就不會過濾
        .name =      dev_uevent_name, // 返回bus or class的name
        .uevent =    dev_uevent,  // 設置主次裝備號的環境變量
    };
    // 如果設置了總線 或 類 返回1
    static int dev_uevent_filter(struct kset *kset, struct kobject *kobj)
    {
        struct kobj_type *ktype = get_ktype(kobj);

        if (ktype == &device_ktype) {
            struct device *dev = to_dev(kobj);
            if (dev->bus)
                return 1;
            if (dev->class)
                return 1;
         }
    }
    // 返回 Bus  或 類的名字
    static const char *dev_uevent_name(struct kset *kset, struct kobject *kobj)
     {
        struct device *dev = to_dev(kobj);

        if (dev->bus)
               return dev->bus->name;
        if (dev->class)
               return dev->class->name;
     }

    static int dev_uevent(struct kset *kset, struct kobject *kobj,
              struct kobj_uevent_env *env)
    {

        if (MAJOR(dev->devt)) {

            add_uevent_var(env, "MAJOR=%u", MAJOR(dev->devt));// 在環境變量中設置主次裝備號
            add_uevent_var(env, "MINOR=%u", MINOR(dev->devt)); // 但是mdev其實不是從這里讀取的
            name = device_get_devnode(dev, &mode, &tmp);
            if (name) {
                add_uevent_var(env, "DEVNAME=%s", name);
            if (mode)
                add_uevent_var(env, "DEVMODE=%#o", mode & 0777);
        }
    }

    return retval;

}


還有1個重點,kobject_init(&dev->kobj, &device_ktype)需要注意。

    static struct kobj_type device_ktype = {
        .release    = device_release,
        .sysfs_ops    = &dev_sysfs_ops, // 它有必要 看1看
    };

    static struct sysfs_ops dev_sysfs_ops = {
        . show    = dev_attr_show, // 這里僅指定了show store函數,并沒有指定 屬性文件
        .store    = dev_attr_store,
    };

    指定屬性文件 是在 error = device_create_file(dev, &devt_attr);

    int device_create_file(struct device *dev, struct device_attribute *attr)
    {
        error = sysfs_create_file(&dev->kobj, &attr->attr);

    }

    devt_attr 定義 在 static struct device_attribute devt_attr =  __ATTR(dev, S_IRUGO, show_dev, NULL);

    #define __ATTR(_name,_mode,_show,_store) { \
        .attr = {.name = __stringify(_name), .mode = _mode },    \
        .show    = _show,                    \
        .store    = _store,                    \
    }

    將宏展開:

    static struct device_attribute devt_attr = {

        .attr = {.name = __stringify(dev),.mode = S_IRUGO},
        .show    = show_dev,   // 就1行 return print_dev_t(buf, dev->devt) 返回dev的裝備號         
        .store    = NULL,

    }

    真實的屬性文件是:attr = {.name = __stringify(dev),.mode = S_IRUGO},我們在用戶空間cat dev的時候調用的是Kobject->ktye->show,也就是:

    static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
                 char *buf)
    {
        struct device_attribute *dev_attr = to_dev_attr(attr); // 轉換成上邊devt_attr 的結構類型
        struct device *dev = to_dev(kobj);

        if (dev_attr->show)    // 調用 show_dev 傳遞 主次裝備號
            ret = dev_attr->show(dev, dev_attr, buf);
        }

真是大費周章~~

可以總結1下上面的工作了

    1、每個 device 的kobkect都將被鏈入kset 鏈表

    2、每個 device 的kobkect 在/sys/device/目錄下創建子目錄

    3、每個 device 的kobkect 在/sys/device/$(name)/目錄下創建屬性文件 dev

    4、每個 device 的kobkect 的ktype 都被設置為device_ktype,通過 show 可以訪問到device的主次裝備號

    5、將 device 的 kobject 的PATH 、name、主次裝備號等等設置到環境變量nev->nevp里

    6、調用用戶空間 mdev

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    mdev 是啥,mdev 可以說是udev的精簡版,在 busybox 制作文件系統的時候被編譯進去,它主要的工作就是根據/sys 目錄的信息來幫助我們自動創建裝備節點,更詳細的概念請自行百度。向要弄清,mdev 創建裝備幾點的進程,那只能看Busybox的源碼了。。


附上1個我做實驗時打印出來的環境變量

       env[0] ACTION=add
       env[1] DEVPATH=/devices/platform/myled
       env[2] SUBSYSTEM=platform
       env[3] MAJOR=251
       env[4] MINOR=0
       env[5] DEVNAME=myled
       env[6] MODALIAS=platform:myled
       env[7] SEQNUM=642
       env[8] HOME=/
       env[9] PATH=/sbin:/bin:/usr/sbin:/usr/bin

現在我們來看看,從內核空間調用的這個用戶程序mdev

    int mdev_main(int argc, char **argv)
    {
        // mdev -s 開機掃描/sys 目錄創建裝備節點,這里不分析
        if (argc == 2 && !strcmp(argv[1],"-s")) {
            ......
        } else {
            action = getenv("ACTION");    // 取得action 就是add or remove
            env_path = getenv("DEVPATH"); // 取得DEVPATH
            if (!action || !env_path)
                bb_show_usage();

            sprintf(temp, "/sys%s", env_path);   // /sys+DEVPATH 比如/sys/devices/xxx
            if (!strcmp(action, "remove"))  // 移除 dev
                make_device(temp, 1);
            else if (!strcmp(action, "add")) { // 增加 dev
                make_device(temp, 0);
            }
        }
    }

    static void make_device(char *path, int delete)
    {
        // 獲得主次裝備號,看看是如何獲得的
        if (!delete) {
            // 在path 后邊 + “/dev”  那末path == /sys/devices/xxx/dev
            strcat(path, "/dev");
            len = open_read_close(path, temp + 1, 64); //讀dev 我們前邊說過了,這里會調用show 傳遞主次裝備號~
            *temp++ = 0;
            if (len < 1) return;
        }
   
        // 取得裝備名字,根據最后1個"/"
        device_name = bb_basename(path);
   
        // 根據 path 的第5個字符來判斷裝備類型,如果是在/sys/class 目錄的話 就是字符裝備,其他的都是塊裝備
        type = path[5]=='c' ? S_IFCHR : S_IFBLK;
   
        // 如果 /etc/mdev.conf 有這個配置文件的話,根據配置文件的規則來 創建裝備節點 并履行1些命令
        if (ENABLE_FEATURE_MDEV_CONF) {
            // 這個不如直接來看 mdev.conf 來得實在
            fd = open("/etc/mdev.conf", O_RDONLY);
            ......
        }

        if (!delete) {
            if (sscanf(temp, "%d:%d", &major, &minor) != 2) return;
            // mknod 創建裝備節點
            if (mknod(device_name, mode | type, makedev(major, minor)) && errno != EEXIST)
                bb_perror_msg_and_die("mknod %s", device_name);

        }
    }

    關于 /etc/mdev.conf 真是太有用途了 ,附上韋東山老師 uevent 的文檔,我就不賣弄了。

---------------------------------------------------------------------------------------------------------------------------------------------------------------

我接上U盤,想自動掛載,怎樣辦? mdev.conf的格式: <device regex> <uid>:<gid> <octal permissions> [<@|$|*> <command>] device regex:正則表達式,表示哪個裝備 uid: owner gid: 組ID octal permissions:以8進制表示的屬性 @:創建裝備節點以后履行命令 $:刪除裝備節點之前履行命令 *: 創建裝備節點以后 和 刪除裝備節點之前 履行命令 command:要履行的命令 <span style="white-space:pre"> // 韋東山老師寫了個驅動,有 led led1 led2 led3 這4個裝備</span> 寫mdev.conf 1. leds 0:0 777 led1 0:0 777 led2 0:0 777 led3 0:0 777 2. leds?[123]? 0:0 777 3. leds?[123]? 0:0 777 @ echo create /dev/$MDEV > /dev/console 4. leds?[123]? 0:0 777 * if [ $ACTION = "add" ]; then echo create /dev/$MDEV > /dev/console; else echo remove /dev/$MDEV > /dev/console; fi 5. leds?[123]? 0:0 777 * /bin/add_remove_led.sh 把命令寫入1個腳本: add_remove_led.sh #!/bin/sh if [ $ACTION = "add" ]; then echo create /dev/$MDEV > /dev/console; else echo remove /dev/$MDEV > /dev/console; fi 6. U盤自動加載 sda[1⑼]+ 0:0 777 * if [ $ACTION = "add" ]; then mount /dev/$MDEV /mnt; else umount /mnt; fi 7. sda[1⑼]+ 0:0 777 * /bin/add_remove_udisk.sh add_remove_udisk.sh #!/bin/sh if [ $ACTION = "add" ]; then mount /dev/$MDEV /mnt; else umount /mnt; fi



-------------------------------------------------------------------------------------------------------------------------------------------

附上1個 我做實驗的代碼,僅供參考,基于Linux2.6.32.2內核

#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/device.h> #include <linux/interrupt.h> #include <linux/sched.h> #include <linux/irq.h> #include <asm/uaccess.h> #include <linux/input.h> #include <linux/platform_device.h> // 裝備資源 static struct resource led_resource[] = { //jz2440的參數,驅動未測試 [0] = { .start = 0x56000010, .end = 0x56000010 + 8 - 1, .flags = IORESOURCE_MEM, }, [1] = { .start = 5, .end = 5, .flags = IORESOURCE_IRQ, }, }; static void led_release(struct device *dev){ } // 創建1個裝備 static struct platform_device led_dev = { .name = "myled", //裝備名字 與 驅動相匹配 .id = ⑴, .num_resources = ARRAY_SIZE(led_resource), .resource = led_resource, .dev = { .release = led_release, .devt = MKDEV(252, 1), }, }; static int led_dev_init(void){ //向bus注冊led_dev match drv鏈表進行配對 platform_device_register(&led_dev); return 0; } static void led_dev_exit(void){ platform_device_unregister(&led_dev); } module_init(led_dev_init); module_exit(led_dev_exit); MODULE_LICENSE("GPL");
打印出來的環境變量:

env[0] ACTION=add
env[1] DEVPATH=/devices/platform/myled
env[2] SUBSYSTEM=platform
env[3] MAJOR=251
env[4] MINOR=0
env[5] DEVNAME=myled
env[6] MODALIAS=platform:myled
env[7] SEQNUM=642
env[8] HOME=/
env[9] PATH=/sbin:/bin:/usr/sbin:/usr/bin



我們創建了 dev 并設置它的主次裝備號,mdev 就自動為我們創建裝備節點了~


最后,我們再來回顧1下全部流程~

kobject:

    1、每個 device 的kobkect都將被鏈入kset 鏈表

    2、每個 device 的kobkect 在/sys/device/目錄下創建子目錄

    3、每個 device 的kobkect 在/sys/device/$(name)/目錄下創建屬性文件 dev

    4、每個 device 的kobkect 的ktype 都被設置為device_ktype,通過 show 可以訪問到device的主次裝備號

uevent:

    5、將 device 的 kobject 的PATH 、name、主次裝備號等等設置到環境變量nev->nevp里

    6、調用用戶空間 mdev

mdev:

    7、讀取環境變量,創建裝備節點~


    至此,我們應當對kobject的作用有了1個全面的了解。寫的不好,還請多批評指正。

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 成 人免费视频l免费观看 | 正在播放国产露脸做91 | 高清 国产 日韩 欧美 | 成人欧美精品久久久久影院 | 成人性生活免费看 | 伊人欧美 | 欧美亚洲另类在线 | 高清日本一级特黄aa大片 | 毛片新网址 | 色综合久久综合欧美综合网 | 99久久精品国产国产毛片 | 亚洲精品乱无伦码 | 成人国产在线看不卡 | julia一区二区三区中文字幕 | 2022久久国产精品免费热麻豆 | 国产又黄又爽又色的免费 | 国产午夜精品一区二区三区 | 波多野结中文字幕在线69视频 | 国产永久在线观看 | 国产丝袜福利视频在线播放 | 亚洲国产欧美精品 | 欧美一级毛片图 | 五月婷婷免费视频 | 午夜在线a亚洲v天堂网2019 | 日韩一区二区久久久久久 | 手机看片在线精品观看 | 国产五月婷婷 | 久久午夜羞羞影院免费观看 | 亚洲 欧美 精品 | 国产成人精品视频 | 欧美啪啪一级毛片 | 国产午夜人做人免费视频中文 | 叼嘿视频在线观看免费 | 亚洲精品永久www忘忧草 | 欧美性受xxxx狂喷水 | 日本一本视频 | 国产成人看片免费视频观看 | 免费观看www视频 | 亚洲欧美综合乱码精品成人网 | 亚洲欧美四级在线播放 | 波多久久夜色精品国产 |