分享好友

×
取消 复制
返回小栈
个人对kobject的一点研究
飘絮絮絮丶2020-06-03 18:01:13

在LINUX中最让人不解的大概就是/sys下面的内容了

下面首先让我们来创建一个简单的platform设备,并从这个设备的视角进行深入,在此篇文章的深入过程中,我们只看kobeject的模型
我所使用的内核版本号为2.6.26,操作系统的内核版本号为2.6.27-7,暂未发现2.6.27-7与2.6.26的重大不同

首先写一个简单的模块

#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/module.h>

static int __init test_probe(struct platform_device *pdev)
{
int err = 0;
return err;
}

static int test_remove(struct platform_device *pdev)
{
return 0;
}
static struct platform_device test_device = {
.name = "test_ts",
.id = -1,
};

static struct platform_driver test_driver = {
.probe = test_probe,
.remove = test_remove,
.driver = {
.name = "test_ts",
.owner = THIS_MODULE,
},
};

static int __devinit test_init(void)
{
platform_device_register(&test_device);
return platform_driver_register(&test_driver);
}

static void __exit test_exit(void)
{
platform_device_unregister(&test_device);
platform_driver_unregister(&test_driver);
}

module_init(test_init);
module_exit(test_exit);

MODULE_AUTHOR("zwolf");
MODULE_DESCRIPTION("Module test");
MODULE_LICENSE("GPL");
MODULE_ALIAS("test");


接下来是makefile

#Makefile

obj-m:=test.o
KDIR:=/lib/modules/2.6.27-7-generic/build
PWD:=$(shell pwd)

default:
$(MAKE) -C $(KDIR) M=$(PWD) modules


KDIR中的目录请改为各位实际运行中的内核目录

make之后进行模块的加载 sudo insmod ./test.ko

现在到sys目录中查看我们的设备是否已经加载上了

首先是/sys/bus/platform/devices/

在devices下,每一个连接文件都代表了一个设备

ls可看见test_ts,进入test_ts,ls可发现driver这个链接文件,ls-l查看,发现这个文件是连到/sys/bus/platform/drivers/test_ts的

这里需要说明的是连接的含义,并不是driver驱动存在于test_ts这个设备中,而是test_ts使用的驱动为/sys/bus/platform/drivers/test_ts

现在换到/sys/bus/platform/drivers这个目录下

ls查看会发现这里的文件都为目录,而非连接文件,说明这是驱动真正放置的位置

现在进入test_ts目录,然后ls,发现有一个test_ts的连接文件,ls –l查看可发现该文件连接到/sys/devices/platform/test_ts下

回到/sys/bus/platform/devices/下ls –l也会发现test_ts连接到/sys/devices/platform/test_ts

为什么test_ts这个设备放置于/sys/devices/platform下,而不是/sys/bus/platform/devices下呢

我认为和直观性有关,在sys下有这么几个目录block bus class dev devices firmware kernel module fs power
devices很直观的说明了设备在这个目录下,便于大家查找
而/sys/bus/platform/devices下的连接是为了分类查找

画了张目录图,如下,绿色框的为连接文件,绿色线条为连接的对象

题外话:我自身对于这样的分类不是很喜欢,臃肿 重复 而且信息也不好规划,希望在以后的版本能对sys进行大的改造

现在来看另两个图,也就是构成sys的核心kobject,首先第一个是我去掉了连接部分的内容 也就是绿色线条的目录图


第二个是组成这个目录图的核心,kobject图,我也叫他层次图

不看大号绿色箭头右边的内容的话是不是发现两个架构相同?
对的,kobject的层次决定了目录的结构
kobeject图很大,但也不要担心,里面的内容其实不多,基础框架涉及3个主要结构kset kobject和ktype
在说明test_ts的注册之前,先让我们看一下sys下的两个基础目录bus,devices

首先是bus
bus的注册在/drivers/base/bus.c里
int __init buses_init(void)
{
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
if (!bus_kset)
return -ENOMEM;
return 0;
}

先看bus_uevent_ops,这是一个uevent的操作集(我也还没清楚uevent的用途,所以uevent的内容先放着)

然后到kset_create_and_add

struct kset *kset_create_and_add(const char *name,
struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
//传递进来的参数为("bus", &bus_uevent_ops, NULL)
{
struct kset *kset;
int error;

//创建一个kset容器
kset = kset_create(name, uevent_ops, parent_kobj);
if (!kset)
return NULL;

//注册创建的kset容器
error = kset_register(kset);
if (error) {
kfree(kset);
return NULL;
}
return kset;
}

首先需要创建一个kset容器
static struct kset *kset_create(const char *name,
struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
//传递进来的参数为("bus", &bus_uevent_ops, NULL)
{
struct kset *kset;

//为kset分配内存
kset = kzalloc(sizeof(*kset), GFP_KERNEL);
if (!kset)
return NULL;

//设置kset中kobject的名字,这里为bus
kobject_set_name(&kset->kobj, name);

//设置uevent操作集,这里为bus_uevent_ops
kset->uevent_ops = uevent_ops;

//设置父对象,这里为NULL
kset->kobj.parent = parent_kobj;

//设置容器操作集
kset->kobj.ktype = &kset_ktype;

//设置父容器
kset->kobj.kset = NULL;

return kset;
}

这里的ktype,也就是kset_ktype是一个操作集,用于为sys下文件的实时反馈做服务,例如我们cat name的时候就要通过ktype提供的show函数,具体什么怎么运用,将在后面讲解

现在回到kset_create_and_add中的kset_register,将建立好的kset添加进sys里

int kset_register(struct kset *k)
{
int err;

if (!k)
return -EINVAL;

//初始化
kset_init(k);

//添加该容器
err = kobject_add_internal(&k->kobj);
if (err)
return err;
kobject_uevent(&k->kobj, KOBJ_ADD);
return 0;
}

kset_init进行一些固定的初始化操作,里面没有我们需要关心的内容
kobject_add_internal为重要的一个函数,他对kset里kobj的从属关系进行解析,搭建正确的架构

static int kobject_add_internal(struct kobject *kobj)
{
int error = 0;
struct kobject *parent;

//检测kobj是否为空
if (!kobj)
return -ENOENT;

//检测kobj名字是否为空
if (!kobj->name || !kobj->name[0]) {
pr_debug("kobject: (%p): attempted to be registered with empty "
"name!\n", kobj);
WARN_ON(1);
return -EINVAL;
}

//提取父对象
parent = kobject_get(kobj->parent);

/* join kset if set, use it as parent if we do not already have one */
//父容器存在则设置父对象
if (kobj->kset) {//在bus的kset中为空,所以不会进入到下面的代码

//检测是否已经设置父对象
if (!parent)
//无则使用父容器为父对象
parent = kobject_get(&kobj->kset->kobj);

//添加该kobj到父容器的链表中
kobj_kset_join(kobj);

//设置父对象
kobj->parent = parent;
}

pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
kobject_name(kobj), kobj, __func__,
parent ? kobject_name(parent) : "<NULL>",
kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");

//建立相应的目录
error = create_dir(kobj);

if (error) {
kobj_kset_leave(kobj);
kobject_put(parent);
kobj->parent = NULL;

if (error == -EEXIST)
printk(KERN_ERR "%s failed for %s with "
"-EEXIST, don't try to register things with "
"the same name in the same directory.\n",
__func__, kobject_name(kobj));
else
printk(KERN_ERR "%s failed for %s (%d)\n",
__func__, kobject_name(kobj), error);
dump_stack();
} else
kobj->state_in_sysfs = 1;

return error;
}

至此bus的目录就建立起来了

模型如下

 

接下来是devices,在/drivers/base/core.c里

int __init devices_init(void)
{
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
if (!devices_kset)
return -ENOMEM;
return 0;
}
过程和bus的注册一致,我就不复述了~
模型如下

 

然后是platform的注册
在platform的注册中,分为两个部分,一部分是注册到devices中,另一部分是注册到bus中,代码在/drivers/base/platform.c中
int __init platform_bus_init(void)
{
int error;

//注册到devices目录中
error = device_register(&platform_bus);
if (error)
return error;

//注册到bus目录中
error = bus_register(&platform_bus_type);

if (error)
device_unregister(&platform_bus);
return error;
}

首先是device_register,注册的参数为platform_bus,如下所示
struct device platform_bus = {
.bus_id = "platform",
};
很简单,只有一个参数,表明了目录名

int device_register(struct device *dev)
{
//初始化dev结构
device_initialize(dev);
//添加dev至目录
return device_add(dev);
}

void device_initialize(struct device *dev)
{
//重要的一步,指明了父容器为devices_kset,而devices_kset的注册在前面已经介绍过了
dev->kobj.kset = devices_kset;
//初始化kobj的ktype为device_ktype
kobject_init(&dev->kobj, &device_ktype);
klist_init(&dev->klist_children, klist_children_get,
klist_children_put);
INIT_LIST_HEAD(&dev->dma_pools);
INIT_LIST_HEAD(&dev->node);
init_MUTEX(&dev->sem);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_init_wakeup(dev, 0);
set_dev_node(dev, -1);
}

int device_add(struct device *dev)
{
struct device *parent = NULL;
struct class_interface *class_intf;
int error;

dev = get_device(dev);
if (!dev || !strlen(dev->bus_id)) {
error = -EINVAL;
goto Done;
}

pr_debug("device: '%s': %s\n", dev->bus_id, __func__);

parent = get_device(dev->parent);
setup_parent(dev, parent);

if (parent)
set_dev_node(dev, dev_to_node(parent));

//设置dev->kobj的名字和父对象,并建立相应的目录
error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id);
if (error)
goto Error;

if (platform_notify)
platform_notify(dev);

if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);

//建立uevent文件
error = device_create_file(dev, &uevent_attr);
if (error)
goto attrError;

if (MAJOR(dev->devt)) {
error = device_create_file(dev, &devt_attr);
if (error)
goto ueventattrError;
}
//建立subsystem连接文件连接到所属class,这里没有设置class对象所以不会建立
error = device_add_class_symlinks(dev);
if (error)
goto SymlinkError;
//建立dev的描述文件,这里没有设置描述文件所以不会建立
error = device_add_attrs(dev);
if (error)
goto AttrsError;
//建立链接文件至所属bus,这里没有设置所属bus所以不会建立
error = bus_add_device(dev);
if (error)
goto BusError;
//添加power文件,因为platform不属于设备,所以不会建立power文件
error = device_pm_add(dev);
if (error)
goto PMError;
kobject_uevent(&dev->kobj, KOBJ_ADD);

//检测驱动中有无适合的设备进行匹配,但没有设置bus,所以不会进行匹配
bus_attach_device(dev);
if (parent)
klist_add_tail(&dev->knode_parent, &parent->klist_children);

if (dev->class) {
down(&dev->class->sem);
list_add_tail(&dev->node, &dev->class->devices);

list_for_each_entry(class_intf, &dev->class->interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
up(&dev->class->sem);
}
Done:
put_device(dev);
return error;
PMError:
bus_remove_device(dev);
BusError:
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_DEL_DEVICE, dev);
device_remove_attrs(dev);
AttrsError:
device_remove_class_symlinks(dev);
SymlinkError:
if (MAJOR(dev->devt))
device_remove_file(dev, &devt_attr);
ueventattrError:
device_remove_file(dev, &uevent_attr);
attrError:
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
kobject_del(&dev->kobj);
Error:
cleanup_device_parent(dev);
if (parent)
put_device(parent);
goto Done;
}



在kobject_add-> kobject_add_varg-> kobject_add_internal中

//提取父对象,因为没有设置,所以为空
parent = kobject_get(kobj->parent);

//父容器存在则设置父对象,在前面的dev->kobj.kset = devices_kset中设为了devices_kset
if (kobj->kset) {
//检测是否已经设置父对象
if (!parent)
//无则使用父容器为父对象
parent = kobject_get(&kobj->kset->kobj);
//添加该kobj到父容器的链表中
kobj_kset_join(kobj);

//设置父对象
kobj->parent = parent;
}

现在devices下的platform目录建立好了,模型如下,其中红线描绘了目录关系

  


文章来源CU社区:个人对kobject的一点研究

0
0
戳我,来吐槽~