Linux驱动的kobj/kset/bus/dev/drv/class

sysfs文件系统是基于RAM实现的文件系统,以下操作便是基于sysfs文件系统。

一、kobject和kset和kobject_uevent()

1)kobject

kobject表示一个内核对象,在使用时通常是嵌在某一内核对象的数据结构中,比如字符设备cdev定义的内核对象也嵌入了。

struct kobject {
    /*用来表示内核对象的名称,如果该内核对象加入系统,那么它的name会出现在sysfs文件系统中,
    表现形式是一个新的目录名字*/
    const char        *name;
    /*将一系列的内核对象构成链表*/
    struct list_head    entry;
    /*指向该内核对象的上层节点,通过该成员构建内核对象的层次化关系*/
    struct kobject        *parent;
    /*当前内核对象所属的kset对象指针,kset可以容纳一系列同类型的kobject对象*/
    struct kset        *kset;
    /*定义了该内核对象的一组sysfs文件系统相关的操作函数和属性。同时,内核通过ktype成员将
    kobject对象的sysfs文件操作(show和store)与其属性文件关联起来。*/
    const struct kobj_type    *ktype;
    /*用来表示该内核对象在sysfs文件系统中对应的目录项的实例*/
    struct kernfs_node    *sd;
    /*内核通过该成员追踪内核对象的生命周期*/
    struct kref        kref;
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
    struct delayed_work    release;
#endif
    /*表示该kobject代表的内核对象的初始化状态,1表示对象已被初始化,0表示未被初始化*/
    unsigned int state_initialized:1;
    /*表示该kobject代表的内核对象有没有在sysfs中建立一个入口点*/
    unsigned int state_in_sysfs:1;
    unsigned int state_add_uevent_sent:1;
    unsigned int state_remove_uevent_sent:1;
    /*如果kobect隶属于某一个kset,那么它的状态变化可能导致其所在的kset对象向用户空间发送
    event消息。该成员便用来表示当该kobject状态发生变化时,是否让其所在的kset向用户空间
    发送event消息,值1表示不让kset发送。*/
    unsigned int uevent_suppress:1;
};

2)kset

kset可以认为是一组kobject组合。kset本身也是一个内核对象,所以需要内嵌一个kobect对象。

struct kset_uevent_ops {
    int (* const filter)(struct kobject *kobj);
    const char *(* const name)(struct kobject *kobj);
    int (* const uevent)(struct kobject *kobj, struct kobj_uevent_env *env);
};

struct kset {
    /*用来将其中的kobect对象构建成链表*/
    struct list_head list;
    /*对kset上的list链表进行访问操作时用来作为互斥保护*/
    spinlock_t list_lock;
    /*代表当前kset内核对象的kobject变量*/
    struct kobject kobj;
    /*当kset中的某些kobject对象发送状态变化需要通知用户空间时,可以调用其中的函数来完成*/
    const struct kset_uevent_ops *uevent_ops;
} __randomize_layout;

3)kobject_uevent()

Linux设备模型中一个非常重要的功能便是对设备热插拔的支持,能够在设备动态加入或者移除系统时能检查到,然后通知用户去加载或者卸载该设备所对应的驱动。具体到底层的实现细节上,热插拔在内核中通过kobject_uevent()函数来实现,它通过发送一个uevent消息和调用call_usermodehelper来与用户空间进程沟通;它是udev等工具赖以工作的基石,如果系统中有守护进程udevd,那么它应一直监听kobject_uevent通过netlink广播出去的uevet数据包。

/*这列枚举值定义了kset对象的一些状态变化*/
enum kobject_action {
    KOBJ_ADD,  //表明向系统添加一个kset
    KOBJ_REMOVE,
    KOBJ_CHANGE,
    KOBJ_MOVE,
    KOBJ_ONLINE,
    KOBJ_OFFLINE,
    KOBJ_BIND,
    KOBJ_UNBIND,
};
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
{
    return kobject_uevent_env(kobj, action, NULL);
}

比如,可以通过kobject_uevent将内核对象kobject的状态变化通知用户程序:

static ssize_t mytest_store(struct kobject *kobj,
        struct kobj_attribute *attr, const char *buf, size_t count)
{
    ...
    flag = buf[0] - `0`;

    switch (flag) {
    case 0:
        kobject_uevent(kobj, KOBJ_ADD);
        break;
    case 1:
        kobject_uevent(kobj, KOBJ_REMOVE);
        break;
    default:
       break;
    }

    return count;
}

static struct kobj_attribute mytest_attr =
        __ATTR(active, 0644, mytest_show, mytest_store);

二、bus_type/device/device_driver/class

1)内核启动过程中的一些初始化

static int bus_uevent_filter(struct kobject *kobj)
{
    const struct kobj_type *ktype = get_ktype(kobj);

    if (ktype == &bus_ktype)
        /*如果要求发送uevent消息的kobject对象类型是bus_type,那么返回1,意味着uevent消息
        将会发送到用户空间。所以这里的bus_uevent_ops使得bus_kset只用来发送bus类型的内核对象
        产生的uevent消息。*/
        return 1;
    return 0;
}

static const struct kset_uevent_ops bus_uevent_ops = {
    .filter = bus_uevent_filter,
};

int __init buses_init(void)
{
    /*
    1)创建一个名为bus的kset,并将其加入到sysfs文件系统树中(/sys/bus),这里需要注意bus_uevent_ops定义了
    当"bus"这个kset中有状态变化时,用来通知用户空间uevent消息的操作集。由于当某个kset中有
    状态变化时,如果需要向用户空间发送event消息,将由该kset的顶层kset来指向,因为bus_kset
    是系统中所有bus subsystem最顶层的kset,所以bus中的uevent最终会汇集到这里的bus_uevent_ops
    中。这个操作函数集中只定义了一个filter操作,意味着当“bus”kset中发送状态变化时,会通过
    bus_uevent_ops中的filter函数先行处理,以决定是否通知用户空间。
    2)这里创建的"bus总线将是系统中所有后续注册总线的祖先。"
    */
    bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
    if (!bus_kset)
        return -ENOMEM;

    system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);
    if (!system_kset)
        return -ENOMEM;

    return 0;
}

int __init devices_init(void)
{
    /*系统中的每个设备都是一个struct device对象,为容纳所有这些设备定义了一个devices_kset*/
    devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
    ......
    /*内核将系统中的设备分为block和char二类,每类对应一个内核对象,分别为sysfs_dev_block_kobj
    和sysfs_dev_char_kobj*/
    dev_kobj = kobject_create_and_add("dev", NULL);
    ......
    sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
    ......
    sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
    ......
}

int __init classes_init(void)
{
    class_kset = kset_create_and_add("class", NULL, NULL);
    if (!class_kset)
        return -ENOMEM;
    return 0;
}

void __init driver_init(void)
{
    ......
    devices_init();
    buses_init();
    classes_init();
    ......
}

2)从总线的角度看,总线设备驱动三者的关系

如下所示,其中struct kset sybsys标识了系统中当前总线对象与bus_kset间的隶属关系,而struct kset *drivers_kset和struct kset *devices_kset则是在向系统注册当前新总线时动态生成的容纳该总线上所有驱动和设备的kset,与此对应,二个klist成员则以链表的形式将该总线上的所有驱动与设备链接到一起

Linux驱动的kobj/kset/bus/dev/drv/class_第1张图片

struct subsys_private {
    /*用来表示bus所在的子系统,在内核中所有通过bus_register注册进系统的bus所在的kset都将
    指向bus_kset,换句话说bus_kset是系统中所有bus内核对象的容器,而新注册的bus本身也是一个
    kset型对象*/
    struct kset subsys
    /*表示该bus上所有设备的一个集合*/
    struct kset *devices_kset;
    struct list_head interfaces;
    struct mutex mutex;
    /*表示该bus上所有驱动的一个集合*/
    struct kset *drivers_kset;
    /*表示该bus上所有设备的链表*/
    struct klist klist_devices;
    /*表示该bus上所有驱动的链表*/
    struct klist klist_drivers;
    struct blocking_notifier_head bus_notifier;
    /*用来表示向系统中某一总线注册某一设备或驱动的时候,是否进行设备与驱动的绑定操作*/
    unsigned int drivers_autoprobe:1;
    /*指向与struct bus_type_private对象相关联的bus*/
    struct bus_type *bus;
    struct kset glue_dirs;
    struct class *class;
};

3)class

相对于设备device,class是一种更高层次的抽象,用户对设备进行功能上的划分。

Linux驱动的kobj/kset/bus/dev/drv/class_第2张图片

你可能感兴趣的:(Linux,Kernel,linux)