linux设备模型_____相关函数

在linux设备模型学习过程中,会出现很多一个个不大不小的函数,它们都为实现linux设备模型发挥着各自的功能,理解和掌握这些函数还是比较有用的,否则积少成多容易导致对linux设备模型理解的偏差,下面开始具体描述这些一个个的相关函数。本文不对每个相关的文件的所有函数一个个描述,而是结合一个相对顶层函数的函数体内部执行情况来描述,这样和环境结合的更有利于理解。(内核版本:linux 2.6.32)
1、 关于linux设备模型的sysfs内部实现的基本原理:
首先,关于linux设备模型的sysfs,按照总线、设备、驱动、类区分目录结构,有一个总的关于总线的目录叫bus,bus下可以有很多子目录,每个目录都是一个个的虚拟总线,如i2c、platform等等,每个虚拟总线目录下都会有devices、drivers两个子目录,这两个子目录里的内容都是挂在这个总线下的设备和驱动,比如:/sys/bus/i2c/devices/i2c-0/,就是挂在虚拟总线i2c下的一个设备,叫i2c-0;再如:/sys/bus/platform/drivers/mv64xxx_i2c/,就是挂在虚拟总线platform下的一个驱动,叫mv64xxx_i2c;
注意,挂在同一总线下的设备和驱动,不一定肯定有对应关系,比如我的这个板子的情况,先举个有对应关系的例子:
/ # ls sys/bus/platform/devices/mv64xxx_i2c.0/driver/ -l
total 0
-rwxrwxrwx    1 root     root          4096 Jan  1 14:24 bind
lrwxrwxrwx    1 root     root             0 Jan  1 14:24 mv64xxx_i2c.0 -> ../../../../devices/platform/mv64xxx_i2c.0
-rwxrwxrwx    1 root     root          4096 Jan  1 14:24 uevent
-rwxrwxrwx    1 root     root          4096 Jan  1 14:24 unbind
/ # ls sys/bus/platform/drivers/mv64xxx_i2c/
bind            mv64xxx_i2c.0/  uevent          unbind
--------------------------------------------------------------------------------------------------------------
/ # ls sys/bus/platform/drivers/mv64xxx_i2c/ -l
total 0
-rwxrwxrwx    1 root     root          4096 Jan  1 14:24 bind
lrwxrwxrwx    1 root     root             0 Jan  1 14:24 mv64xxx_i2c.0 -> ../../../../devices/platform/mv64xxx_i2c.0
-rwxrwxrwx    1 root     root          4096 Jan  1 14:24 uevent
-rwxrwxrwx    1 root     root          4096 Jan  1 14:24 unbind
上面是设备mv64xxx_i2c.0的驱动,下面是驱动mv64xxx_i2c的设备,黑体的部分说明了它们是对应的,具体为什么能对应后面会细致描述

再举个没联系的例子:
/ # ls sys/bus/platform/devices/gpon/ -l
total 0
lrwxrwxrwx    1 root     root             0 Jan  1 13:37 bus -> ../../../bus/platform
drwxr-xr-x    2 root     root             0 Jan  1 13:37 data
drwxr-xr-x    2 root     root             0 Jan  1 13:37 info
drwxr-xr-x    2 root     root             0 Jan  1 13:37 introp
drwxr-xr-x    2 root     root             0 Jan  1 13:37 misc
-r--r--r--    1 root     root          4096 Jan  1 13:37 modalias
drwxr-xr-x    2 root     root             0 Jan  1 13:37 pm
drwxr-xr-x    2 root     root             0 Jan  1 13:37 protocol
lrwxrwxrwx    1 root     root             0 Jan  1 13:37 subsystem -> ../../../bus/platform
-rw-r--r--    1 root     root          4096 Jan  1 13:37 uevent
-------------------------------------------------------------------------------------------------------
/ # ls sys/bus/platform/drivers/
ehci_marvell/  mv64xxx_i2c/   mvsdio/        soc-audio/
kw_cpuidle/    mv88fx_neta/   serial8250/
/ # ls sys/bus/platform/drivers/
上面是挂在platform总线下的设备gpon,可以看出,gpon目录下没有driver目录,并且platform总线下的drivers目录下也没有什么和gpon相关的目录;这样的情况往往是,设备gpon的驱动部分没有加入驱动模型或者其真实的驱动/设备部分在sysfs的其他地方(后者可能性更大),而这里的gpon设备仅仅是为了用户态查看/修改实际设备的一些参数的接口(类似proc的功能),事实也正如此,gpon目录下的诸如data、info、pm、protocol、misc、introp目录都是加入到sysfs的设备属性,便于用户通过cat、echo等命令可以快速的查看、修改相关内容;
/sys/bus/目录下可以有很多虚拟总线,每个总线下都有devices和drivers目录,各自都可以挂多个设备或驱动;linux内核本身已在内核启动过程中建立了不少bus,是构造linux驱动模型的基础,一般情况下不需要我们再建立bus;


刚才是bus,现在再说说设备,/sys/devices/目录,和/sys/bus/一样,devices也是一个顶层目录(顶层目录可以理解为实际就是kset,后面会描述),所有注册在内核了的设备,都可以在这个目录及其子目录里找到;

至于驱动,它不是顶层目录,它是隶属于某个总线的,如ls sys/bus/platform/drivers/ -l,显示了挂在platform总线下的所有驱动;

最后是类(class),它也是顶层目录,/sys/class/目录下是一个个的类,每个类下是一个个的设备;目前感觉它的一个比较大的作用是,首先建立一个设备(字符/块),然后建立一个类,最后利用该设备的主次设备号在这个类下建立一个设备,那么在/dev/目录下和/sys/dev/char/(或是/sys/dev/block/)目录下都会有该设备的主次设备号文件,尤其是对于/dev目录下能创建设备文件,无需手动mknod创建设备文件(暂还不知道如何达到这点),这应该是一个挺大的便利,也看到不少设备驱动这样做;

上面是宏观上的概念的东西,bus、class、device、driver,从微观实现角度,这些东西都是/sys/下的一个个的有紧密关联的目录:
这里先说一下kobject这个东西,我们知道,不论总线、设备、驱动、类,它们自己本身也好,还是它们底下的子女也好,都是一个个的“设备”,从sysfs看,它们是一个个的目录,从具体实现看,它们都是要占用内存的,好比说每个人都得有个房子(kobject);而kobject就是描述房子的结构体,并且这些房子都是有关联的,比如有的房子是另一个房子的parent,每个房子都需要有parent,有的房子还可能属于某个class,并且每个房子都得有个大的归属(kset),以及有个大类型(ktype);这些parent、class、kset、ktype都是比较重要的房子(kobject)属性,当然还有其他一些属性;正是这些属性及其关联性,具体实现了linux设备模型;
基本可以这样理解,每个kobject对应sysfs下一个目录,从sysfs所挂的/sys/开始,总线目录bus及其下边的各个总线目录、class目录及其下边的各个类目录、devices目录及其下边的各个设备目录,都是对应一个kobject;本文重点是设备驱动及相关的sysfs,所以只考虑bus、class、devices、driver的内容,至于sysfs下的诸如module、fs、kernel、power、block、firmware等等先不管(其实道理差不多);
这么多的kobject是有很多复杂的关联性的,如上面所说,每个kobject结构体都得有parent、kset、ktype等一系列的属性,正是这些属性实现了linux的设备模型,这些属性都是有相关的逻辑,比较重要的比如说,上层调用类似如“platform_device_register”的函数要在platform总线下创建一个设备,因为是设备,所以它的kset是设备归属集(设备归属集这个词是我自己瞎编的),设备归属集应该是全局变量devices_kset(它是在内核早期初始化时赋值,后面描述),它的ktype就是全局变量device_ktype,又因为它挂在platform总线下,所以它的parent在这里就会是platform总线的devices目录所在的kobject;parent这个东西很重要,它直接决定这个device或driver挂在哪个地方,换句话说就是它应该挂在哪些个目录。
2、 启动过程中sysfs相关的内容:
上面讲的恐怕很不清楚,我现在的理解也有限,像很多文章的贴代码和画一堆图的方式也未必理解的透,下面结合启动过程来看:
Linux内核在启动过程的早期会调用一个函数叫driver_init(/driver/base/init.c文件中),这里边有sysfs相关的一些内容,下面开始分析:
 devtmpfs_init();
这是注册dev文件系统并将其挂到/dev目录,不关心;
 devices_init();
重要,在sysfs下创建devices目录、dev目录、dev/char目录、dev/block目录:
一、devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
通过函数kset_create_and_add给kset型全局变量devices_kset赋值:进入函数kset_create_and_add:
(1)、kset = kset_create(name, uevent_ops, parent_kobj);
1)、kset = kzalloc(sizeof(*kset), GFP_KERNEL);
动态分配一个kset结构体空间;
2)、retval = kobject_set_name(&kset->kobj, name);
Kset虽然是代表顶层目录的大的归属集(很多文章叫容器),但它自身也同样是个目录,所以一样需要kobject的实体,事实上kset结构体中就包含kobject结构体,函数kobject_set_name是专门用于设置kobject对象的名字的;这里它的名字设置为传入的参数“devices”,也就是这个目录的名字叫devices;
3)、kset->kobj.parent = parent_kobj;
初始化这个kset的kobject实体的父亲为传入的参数为NULL,即还没有确定父亲;
4)、kset->kobj.ktype = &kset_ktype;
初始化这个kset的kobject实体的类型为kset类型;这说明这个kobject实体“不一般”,是一个kset身份的kobject实体;
5)、kset->kobj.kset = NULL;
初始化这个kset的kobject实体的kset为NULL,作为顶层目录这种“概念性”的东西,如/sys/bus、/sys/devices这些,它们本身是kset,它们自己的kset就是NULL了;
(2)、error = kset_register(kset);
这是实际赋值kset结构体的过程,进入函数kset_register:
1)、kset_init(k);
函数kset_init是对kset结构中的kobject进行一些简单初始化,此外初始化了kset结构体的链表指针和自旋锁;
2)、err = kobject_add_internal(&k->kobj);
这一步和kset作为顶层目录的含义无关,完全是为了它的kobject,函数kobject_add_internal是专门对每个kobject结构体判断其应在sysfs哪个目录下(实际就是判断该kobject的parent成员)并且挂接在sysfs的函数;
2.1)、判断父亲
if (kobj->kset) {
if (!parent)    
parent = kobject_get(&kobj->kset->kobj);  
kobj_kset_join(kobj);         
kobj->parent = parent;       
 }
上面的代码片段是针对还没有parent的kobject的,对于顶层目录,它的kset为NULL(可见上面的(1).5)),所以不会继续执行里面的程序;
2.2)、error = create_dir(kobj);
这个就是根据kobject的结果在sysfs下创建目录了;
2.3)、kobj->state_in_sysfs = 1;
创建目录成功后,给该kobject的state_in_sysfs成员赋值为1,说明该kobject已经在sysfs创建目录了;
(3)、ices_init()的devices_kset创建总结:
至此,全局变量devices_kset初始化完毕,分配了自己的kset结构空间,并对结构体中成员赋值,其中最重要的是kobject实体成员,作为顶层目录它的kobject的类型为kset_ktype,没有parent,也没有kset,最终注册进sysfs并创建目录/sys/devices;
二、dev_kobj = kobject_create_and_add("dev", NULL);
在/sys/目录下创建目录dev,但注意,它只是kobject不是kset;
(1)、kobj = kobject_create();
首先动态分配一个kobject实体,然后调用kobject_init对其进行初始化,
kobject_init(kobj, &dynamic_kobj_ktype);
该函数最主要的工作是:设置kobject成员state_initialized为1,并且设置该kobject的类型为dynamic_kobj_ktype;
(2)、retval = kobject_add(kobj, parent, "%s", name);
这一步是具体向sysfs增加该kobject,kobject_add函数主要是调用函数kobject_add_varg,进入该函数;
retval = kobject_add_varg(kobj, parent, fmt, args);
2.1)、retval = kobject_set_name_vargs(kobj, fmt, vargs);
设置该kobject的名字,由参数可知是“dev”;
2.2)、kobj->parent = parent;
设置该kobject的父亲,由参数可知是NULL;
2.3)、return kobject_add_internal(kobj);
这个kobject也没有属于的kset,所以它的parent就是NULL,直接调用create_dir在sysfs下创建目录;
3)、devices_init()的dev_kobj创建总结:
至此,dev_kobj也创建完毕,它只是个kobject,但也在顶层目录,没有parent也没有kset,最终注册进sysfs并创建目录/sys/dev;
三、sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
在/sys/dev/目录下创建目录block,重点看它的第二参数是上一步建立好的dev_kobj,即这个kobject的父亲是dev_kobj,也就是说它是/sys/dev/下的子目录即/sys/dev/block;过程略;
四、sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
道理完全同上,即创建目录/sys/dev/block;过程略;
 buses_init();
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
和上一步的建立devices的道理完全一样,在顶层建立目录/sys/bus;过程略;
 classes_init();
class_kset = kset_create_and_add("class", NULL, NULL);;
和上一步的建立devices的道理完全一样,在顶层建立目录/sys/class;过程略;
 platform_bus_init();
重要,将建立总线设备platform_bus,在sysfs下注册/sys/bus/platform目录、/sys/bus/platform/devices目录、/sys/bus/platform/drivers目录,之后用的platform总线地方非常多;
(1)、error = device_register(&platform_bus);
这也应该是sysfs中注册的第一个设备。进入函数device_register;
1)、device_initialize(dev);
该函数主要功能就是初始化设备的kset和ktype,分别为devices_kset和device_ktype;
2)、return device_add(dev);
这是具体将设备加入sysfs。
if (dev->init_name) {
  dev_set_name(dev, "%s", dev->init_name);
  dev->init_name = NULL;
}
上面是设置设备的kobject的名字;这里就是platform;
parent = get_device(dev->parent);
setup_parent(dev, parent);
然后是确定该设备的kobject的parent,从这两个函数可以归纳一个简单的方法来判断父亲:
如果该设备不属于哪个class,并且目前没有父亲,那么现在它只能没有父亲;
如果属于某个class,那它的父亲很可能要在这个class里面出现(要么是class的kobject,要么是class下子目录的kobject)
总之,对于parent的理解方法,把握住parent是确定该kobject最终安放在sysfs的哪个目录这个事情,就比较容易把握了,这里对于platform一个总线设备,它目前的parent还是NULL;
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
这一步是真正的加入sysfs,这里第二参数还是NULL,该函数将继续调用函数kobject_add_varg;
retval = kobject_add_varg(kobj, parent, fmt, args);
首先是设置kobject的名字,已设置过;
然后把父亲参数parent(值为NULL)赋给kobject的parent,再调用函数kobject_add_internal;
return kobject_add_internal(kobj);
首先就是判断父亲,
if (kobj->kset) {
if (!parent)    
parent = kobject_get(&kobj->kset->kobj);  
kobj_kset_join(kobj);         
kobj->parent = parent;       
}
已知platform总线设备的kset为devices_kset,然后parent为NULL,继续执行parent = kobject_get(&kobj->kset->kobj);
这里设置了kobject的父亲是它所属kset的kobject实体,即devices_kset的kobject实体;
然后就是调用create_dir创建目录
至此,创建目录/sys/devices/platform目录;
(2)、error =  bus_register(&platform_bus_type);
这一步是注册一个总线叫platform,总线的数据结构为bus_type,它最重要的成员除了名字name就是私有成员p,进入函数bus_register;
1)、priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
    priv->bus = bus;
bus->p = priv;
首先动态分配私有成员空间,然后把它的私有成员回指指针指向全局变量platform_bus_type;
   2)、retval = kobject_set_name(&priv->

linux设备模型_____相关函数
在linux设备模型学习过程中,会出现很多一个个不大不小的函数,它们都为实现linux设备模型发挥着各自的功能,理解和掌握这些函数还是比较有用的,否则积少成多容易导致对linux设备模型理解的偏差,下面开始具体描述这些一个个的相关函数。本文不对每个相关的文件的所有函数一个个描述,而是结合一个相对顶层函数的函数体内部执行情况来描述,这样和环境结合的更有利于理解。(内核版本:linux 2.6.32)
1、 关于linux设备模型的sysfs内部实现的基本原理:
首先,关于linux设备模型的sysfs,按照总线、设备、驱动、类区分目录结构,有一个总的关于总线的目录叫bus,bus下可以有很多子目录,每个目录都是一个个的虚拟总线,如i2c、platform等等,每个虚拟总线目录下都会有devices、drivers两个子目录,这两个子目录里的内容都是挂在这个总线下的设备和驱动,比如:/sys/bus/i2c/devices/i2c-0/,就是挂在虚拟总线i2c下的一个设备,叫i2c-0;再如:/sys/bus/platform/drivers/mv64xxx_i2c/,就是挂在虚拟总线platform下的一个驱动,叫mv64xxx_i2c;
注意,挂在同一总线下的设备和驱动,不一定肯定有对应关系,比如我的这个板子的情况,先举个有对应关系的例子:
/ # ls sys/bus/platform/devices/mv64xxx_i2c.0/driver/ -l
total 0
-rwxrwxrwx    1 root     root          4096 Jan  1 14:24 bind
lrwxrwxrwx    1 root     root             0 Jan  1 14:24 mv64xxx_i2c.0 -> ../../../../devices/platform/mv64xxx_i2c.0
-rwxrwxrwx    1 root     root          4096 Jan  1 14:24 uevent
-rwxrwxrwx    1 root     root          4096 Jan  1 14:24 unbind
/ # ls sys/bus/platform/drivers/mv64xxx_i2c/
bind            mv64xxx_i2c.0/  uevent          unbind
--------------------------------------------------------------------------------------------------------------
/ # ls sys/bus/platform/drivers/mv64xxx_i2c/ -l
total 0
-rwxrwxrwx    1 root     root          4096 Jan  1 14:24 bind
lrwxrwxrwx    1 root     root             0 Jan  1 14:24 mv64xxx_i2c.0 -> ../../../../devices/platform/mv64xxx_i2c.0
-rwxrwxrwx    1 root     root          4096 Jan  1 14:24 uevent
-rwxrwxrwx    1 root     root          4096 Jan  1 14:24 unbind
上面是设备mv64xxx_i2c.0的驱动,下面是驱动mv64xxx_i2c的设备,黑体的部分说明了它们是对应的,具体为什么能对应后面会细致描述

再举个没联系的例子:
/ # ls sys/bus/platform/devices/gpon/ -l
total 0
lrwxrwxrwx    1 root     root             0 Jan  1 13:37 bus -> ../../../bus/platform
drwxr-xr-x    2 root     root             0 Jan  1 13:37 data
drwxr-xr-x    2 root     root             0 Jan  1 13:37 info
drwxr-xr-x    2 root     root             0 Jan  1 13:37 introp
drwxr-xr-x    2 root     root             0 Jan  1 13:37 misc
-r--r--r--    1 root     root          4096 Jan  1 13:37 modalias
drwxr-xr-x    2 root     root             0 Jan  1 13:37 pm
drwxr-xr-x    2 root     root             0 Jan  1 13:37 protocol
lrwxrwxrwx    1 root     root             0 Jan  1 13:37 subsystem -> ../../../bus/platform
-rw-r--r--    1 root     root          4096 Jan  1 13:37 uevent
-------------------------------------------------------------------------------------------------------
/ # ls sys/bus/platform/drivers/
ehci_marvell/  mv64xxx_i2c/   mvsdio/        soc-audio/
kw_cpuidle/    mv88fx_neta/   serial8250/
/ # ls sys/bus/platform/drivers/
上面是挂在platform总线下的设备gpon,可以看出,gpon目录下没有driver目录,并且platform总线下的drivers目录下也没有什么和gpon相关的目录;这样的情况往往是,设备gpon的驱动部分没有加入驱动模型或者其真实的驱动/设备部分在sysfs的其他地方(后者可能性更大),而这里的gpon设备仅仅是为了用户态查看/修改实际设备的一些参数的接口(类似proc的功能),事实也正如此,gpon目录下的诸如data、info、pm、protocol、misc、introp目录都是加入到sysfs的设备属性,便于用户通过cat、echo等命令可以快速的查看、修改相关内容;
/sys/bus/目录下可以有很多虚拟总线,每个总线下都有devices和drivers目录,各自都可以挂多个设备或驱动;linux内核本身已在内核启动过程中建立了不少bus,是构造linux驱动模型的基础,一般情况下不需要我们再建立bus;


刚才是bus,现在再说说设备,/sys/devices/目录,和/sys/bus/一样,devices也是一个顶层目录(顶层目录可以理解为实际就是kset,后面会描述),所有注册在内核了的设备,都可以在这个目录及其子目录里找到;

至于驱动,它不是顶层目录,它是隶属于某个总线的,如ls sys/bus/platform/drivers/ -l,显示了挂在platform总线下的所有驱动;

最后是类(class),它也是顶层目录,/sys/class/目录下是一个个的类,每个类下是一个个的设备;目前感觉它的一个比较大的作用是,首先建立一个设备(字符/块),然后建立一个类,最后利用该设备的主次设备号在这个类下建立一个设备,那么在/dev/目录下和/sys/dev/char/(或是/sys/dev/block/)目录下都会有该设备的主次设备号文件,尤其是对于/dev目录下能创建设备文件,无需手动mknod创建设备文件(暂还不知道如何达到这点),这应该是一个挺大的便利,也看到不少设备驱动这样做;

上面是宏观上的概念的东西,bus、class、device、driver,从微观实现角度,这些东西都是/sys/下的一个个的有紧密关联的目录:
这里先说一下kobject这个东西,我们知道,不论总线、设备、驱动、类,它们自己本身也好,还是它们底下的子女也好,都是一个个的“设备”,从sysfs看,它们是一个个的目录,从具体实现看,它们都是要占用内存的,好比说每个人都得有个房子(kobject);而kobject就是描述房子的结构体,并且这些房子都是有关联的,比如有的房子是另一个房子的parent,每个房子都需要有parent,有的房子还可能属于某个class,并且每个房子都得有个大的归属(kset),以及有个大类型(ktype);这些parent、class、kset、ktype都是比较重要的房子(kobject)属性,当然还有其他一些属性;正是这些属性及其关联性,具体实现了linux设备模型;
基本可以这样理解,每个kobject对应sysfs下一个目录,从sysfs所挂的/sys/开始,总线目录bus及其下边的各个总线目录、class目录及其下边的各个类目录、devices目录及其下边的各个设备目录,都是对应一个kobject;本文重点是设备驱动及相关的sysfs,所以只考虑bus、class、devices、driver的内容,至于sysfs下的诸如module、fs、kernel、power、block、firmware等等先不管(其实道理差不多);
这么多的kobject是有很多复杂的关联性的,如上面所说,每个kobject结构体都得有parent、kset、ktype等一系列的属性,正是这些属性实现了linux的设备模型,这些属性都是有相关的逻辑,比较重要的比如说,上层调用类似如“platform_device_register”的函数要在platform总线下创建一个设备,因为是设备,所以它的kset是设备归属集(设备归属集这个词是我自己瞎编的),设备归属集应该是全局变量devices_kset(它是在内核早期初始化时赋值,后面描述),它的ktype就是全局变量device_ktype,又因为它挂在platform总线下,所以它的parent在这里就会是platform总线的devices目录所在的kobject;parent这个东西很重要,它直接决定这个device或driver挂在哪个地方,换句话说就是它应该挂在哪些个目录。
2、 启动过程中sysfs相关的内容:
上面讲的恐怕很不清楚,我现在的理解也有限,像很多文章的贴代码和画一堆图的方式也未必理解的透,下面结合启动过程来看:
Linux内核在启动过程的早期会调用一个函数叫driver_init(/driver/base/init.c文件中),这里边有sysfs相关的一些内容,下面开始分析:
 devtmpfs_init();
这是注册dev文件系统并将其挂到/dev目录,不关心;
 devices_init();
重要,在sysfs下创建devices目录、dev目录、dev/char目录、dev/block目录:
一、devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
通过函数kset_create_and_add给kset型全局变量devices_kset赋值:进入函数kset_create_and_add:
(1)、kset = kset_create(name, uevent_ops, parent_kobj);
1)、kset = kzalloc(sizeof(*kset), GFP_KERNEL);
动态分配一个kset结构体空间;
2)、retval = kobject_set_name(&kset->kobj, name);
Kset虽然是代表顶层目录的大的归属集(很多文章叫容器),但它自身也同样是个目录,所以一样需要kobject的实体,事实上kset结构体中就包含kobject结构体,函数kobject_set_name是专门用于设置kobject对象的名字的;这里它的名字设置为传入的参数“devices”,也就是这个目录的名字叫devices;
3)、kset->kobj.parent = parent_kobj;
初始化这个kset的kobject实体的父亲为传入的参数为NULL,即还没有确定父亲;
4)、kset->kobj.ktype = &kset_ktype;
初始化这个kset的kobject实体的类型为kset类型;这说明这个kobject实体“不一般”,是一个kset身份的kobject实体;
5)、kset->kobj.kset = NULL;
初始化这个kset的kobject实体的kset为NULL,作为顶层目录这种“概念性”的东西,如/sys/bus、/sys/devices这些,它们本身是kset,它们自己的kset就是NULL了;
(2)、error = kset_register(kset);
这是实际赋值kset结构体的过程,进入函数kset_register:
1)、kset_init(k);
函数kset_init是对kset结构中的kobject进行一些简单初始化,此外初始化了kset结构体的链表指针和自旋锁;
2)、err = kobject_add_internal(&k->kobj);
这一步和kset作为顶层目录的含义无关,完全是为了它的kobject,函数kobject_add_internal是专门对每个kobject结构体判断其应在sysfs哪个目录下(实际就是判断该kobject的parent成员)并且挂接在sysfs的函数;
2.1)、判断父亲
if (kobj->kset) {
if (!parent)    
parent = kobject_get(&kobj->kset->kobj);  
kobj_kset_join(kobj);         
kobj->parent = parent;       
 }
上面的代码片段是针对还没有parent的kobject的,对于顶层目录,它的kset为NULL(可见上面的(1).5)),所以不会继续执行里面的程序;
2.2)、error = create_dir(kobj);
这个就是根据kobject的结果在sysfs下创建目录了;
2.3)、kobj->state_in_sysfs = 1;
创建目录成功后,给该kobject的state_in_sysfs成员赋值为1,说明该kobject已经在sysfs创建目录了;
(3)、ices_init()的devices_kset创建总结:
至此,全局变量devices_kset初始化完毕,分配了自己的kset结构空间,并对结构体中成员赋值,其中最重要的是kobject实体成员,作为顶层目录它的kobject的类型为kset_ktype,没有parent,也没有kset,最终注册进sysfs并创建目录/sys/devices;
二、dev_kobj = kobject_create_and_add("dev", NULL);
在/sys/目录下创建目录dev,但注意,它只是kobject不是kset;
(1)、kobj = kobject_create();
首先动态分配一个kobject实体,然后调用kobject_init对其进行初始化,
kobject_init(kobj, &dynamic_kobj_ktype);
该函数最主要的工作是:设置kobject成员state_initialized为1,并且设置该kobject的类型为dynamic_kobj_ktype;
(2)、retval = kobject_add(kobj, parent, "%s", name);
这一步是具体向sysfs增加该kobject,kobject_add函数主要是调用函数kobject_add_varg,进入该函数;
retval = kobject_add_varg(kobj, parent, fmt, args);
2.1)、retval = kobject_set_name_vargs(kobj, fmt, vargs);
设置该kobject的名字,由参数可知是“dev”;
2.2)、kobj->parent = parent;
设置该kobject的父亲,由参数可知是NULL;
2.3)、return kobject_add_internal(kobj);
这个kobject也没有属于的kset,所以它的parent就是NULL,直接调用create_dir在sysfs下创建目录;
3)、devices_init()的dev_kobj创建总结:
至此,dev_kobj也创建完毕,它只是个kobject,但也在顶层目录,没有parent也没有kset,最终注册进sysfs并创建目录/sys/dev;
三、sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
在/sys/dev/目录下创建目录block,重点看它的第二参数是上一步建立好的dev_kobj,即这个kobject的父亲是dev_kobj,也就是说它是/sys/dev/下的子目录即/sys/dev/block;过程略;
四、sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
道理完全同上,即创建目录/sys/dev/block;过程略;
 buses_init();
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
和上一步的建立devices的道理完全一样,在顶层建立目录/sys/bus;过程略;
 classes_init();
class_kset = kset_create_and_add("class", NULL, NULL);;
和上一步的建立devices的道理完全一样,在顶层建立目录/sys/class;过程略;
 platform_bus_init();
重要,将建立总线设备platform_bus,在sysfs下注册/sys/bus/platform目录、/sys/bus/platform/devices目录、/sys/bus/platform/drivers目录,之后用的platform总线地方非常多;
(1)、error = device_register(&platform_bus);
这也应该是sysfs中注册的第一个设备。进入函数device_register;
1)、device_initialize(dev);
该函数主要功能就是初始化设备的kset和ktype,分别为devices_kset和device_ktype;
2)、return device_add(dev);
这是具体将设备加入sysfs。
if (dev->init_name) {
  dev_set_name(dev, "%s", dev->init_name);
  dev->init_name = NULL;
}
上面是设置设备的kobject的名字;这里就是platform;
parent = get_device(dev->parent);
setup_parent(dev, parent);
然后是确定该设备的kobject的parent,从这两个函数可以归纳一个简单的方法来判断父亲:
如果该设备不属于哪个class,并且目前没有父亲,那么现在它只能没有父亲;
如果属于某个class,那它的父亲很可能要在这个class里面出现(要么是class的kobject,要么是class下子目录的kobject)
总之,对于parent的理解方法,把握住parent是确定该kobject最终安放在sysfs的哪个目录这个事情,就比较容易把握了,这里对于platform一个总线设备,它目前的parent还是NULL;
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
这一步是真正的加入sysfs,这里第二参数还是NULL,该函数将继续调用函数kobject_add_varg;
retval = kobject_add_varg(kobj, parent, fmt, args);
首先是设置kobject的名字,已设置过;
然后把父亲参数parent(值为NULL)赋给kobject的parent,再调用函数kobject_add_internal;
return kobject_add_internal(kobj);
首先就是判断父亲,
if (kobj->kset) {
if (!parent)    
parent = kobject_get(&kobj->kset->kobj);  
kobj_kset_join(kobj);         
kobj->parent = parent;       
}
已知platform总线设备的kset为devices_kset,然后parent为NULL,继续执行parent = kobject_get(&kobj->kset->kobj);
这里设置了kobject的父亲是它所属kset的kobject实体,即devices_kset的kobject实体;
然后就是调用create_dir创建目录
至此,创建目录/sys/devices/platform目录;
(2)、error =  bus_register(&platform_bus_type);
这一步是注册一个总线叫platform,总线的数据结构为bus_type,它最重要的成员除了名字name就是私有成员p,进入函数bus_register;
1)、priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
    priv->bus = bus;
bus->p = priv;
首先动态分配私有成员空间,然后把它的私有成员回指指针指向全局变量platform_bus_type;
   2)、retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
这是为了后面建立priv->subsys.kobj的kset做准备,设置该kset的kobject实体的名字,名字就是该bus的名字即platform;
3)、设置总线的kset和ktype,分别为bus_kset和bus_ktype(bus_kset在buses_init中已初始化),这是因为注册的是总线;
4)、priv->drivers_autoprobe = 1;
设置该标志,当有driver注册时,会自动匹配devices 上的设备并用probe初始化,当有device注册时也同样找到driver并会初始化;
5)、retval = kset_register(&priv->subsys);
kset_register这个函数刚才分析过,所以这里是再次创建一个kset;会创建目录/sys/bus/platform;
6)、retval = bus_create_file(bus, &bus_attr_uevent);
这是在当前bus目录下生成bus_attr_uevent属性文件,关于uevent,本文暂不关注;
7)、priv->devices_kset = kset_create_and_add("devices", NULL,
       &priv->subsys.kobj);
priv->drivers_kset = kset_create_and_add("drivers", NULL,
       &priv->subsys.kobj);
这是再次建立两个kset,分别叫devices和drivers,注意,这两个kset的kobject实体的parent都是刚才建立的kset(priv->subsys.kobj),所以最终创建的目录devices和drivers都是刚才的/sys/bus/platform目录的子目录;
8)、retval = add_probe_files(bus);
这是增加bus_attr_drivers_probe 和bus_attr_drivers_autoprobe文件,即/sys/bus/platform/ drivers_autoprobe文件和/sys/bus/platform/ drivers_probe文件;
9)、retval = bus_add_attrs(bus);
这是增加bus的属性文件,一般bus不添加属性;
至此,在/sys/bus/目录下,增加了一个总线叫做platform,它还有两个子目录,一个是devices,一个是drivers,注意,它们都是kset类型;

上面这些,就是内核初始化早期创建的sysfs模型,创建了/sys/bus、/sys/bus/platform、/sys/bus/platform/devices、/sys/bus/platform/drivers、/sys/devices、/sys/devices/platform、/sys/class以及一系列的相关联的数据结构;看到这里,如果认真分析了kset、kobject等相关的函数和数据结构,应该对这些函数和数据结构有一定的理解了,后面的具体的设备模型的操作,诸如I2C等外设,还将在linux设备模型上建立很多内容,但道理都是一样的。后面将结合一些实际的设备驱动进一步加深对linux设备模型的理解。

subsys.kobj, "%s", bus->name);
这是为了后面建立priv->subsys.kobj的kset做准备,设置该kset的kobject实体的名字,名字就是该bus的名字即platform;
3)、设置总线的kset和ktype,分别为bus_kset和bus_ktype(bus_kset在buses_init中已初始化),这是因为注册的是总线;
4)、priv->drivers_autoprobe = 1;
设置该标志,当有driver注册时,会自动匹配devices 上的设备并用probe初始化,当有device注册时也同样找到driver并会初始化;
5)、retval = kset_register(&priv->subsys);
kset_register这个函数刚才分析过,所以这里是再次创建一个kset;会创建目录/sys/bus/platform;
6)、retval = bus_create_file(bus, &bus_attr_uevent);
这是在当前bus目录下生成bus_attr_uevent属性文件,关于uevent,本文暂不关注;
7)、priv->devices_kset = kset_create_and_add("devices", NULL,
       &priv->subsys.kobj);
priv->drivers_kset = kset_create_and_add("drivers", NULL,
       &priv->subsys.kobj);
这是再次建立两个kset,分别叫devices和drivers,注意,这两个kset的kobject实体的parent都是刚才建立的kset(priv->subsys.kobj),所以最终创建的目录devices和drivers都是刚才的/sys/bus/platform目录的子目录;
8)、retval = add_probe_files(bus);
这是增加bus_attr_drivers_probe 和bus_attr_drivers_autoprobe文件,即/sys/bus/platform/ drivers_autoprobe文件和/sys/bus/platform/ drivers_probe文件;
9)、retval = bus_add_attrs(bus);
这是增加bus的属性文件,一般bus不添加属性;
至此,在/sys/bus/目录下,增加了一个总线叫做platform,它还有两个子目录,一个是devices,一个是drivers,注意,它们都是kset类型;

上面这些,就是内核初始化早期创建的sysfs模型,创建了/sys/bus、/sys/bus/platform、/sys/bus/platform/devices、/sys/bus/platform/drivers、/sys/devices、/sys/devices/platform、/sys/class以及一系列的相关联的数据结构;看到这里,如果认真分析了kset、kobject等相关的函数和数据结构,应该对这些函数和数据结构有一定的理解了,后面的具体的设备模型的操作,诸如I2C等外设,还将在linux设备模型上建立很多内容,但道理都是一样的。后面将结合一些实际的设备驱动进一步加深对linux设备模型的理解。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值