linux设备模型_____相关函数

2024-05-09 22:38

本文主要是介绍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设备模型的理解。

这篇关于linux设备模型_____相关函数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/974703

相关文章

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

sqlite3 相关知识

WAL 模式 VS 回滚模式 特性WAL 模式回滚模式(Rollback Journal)定义使用写前日志来记录变更。使用回滚日志来记录事务的所有修改。特点更高的并发性和性能;支持多读者和单写者。支持安全的事务回滚,但并发性较低。性能写入性能更好,尤其是读多写少的场景。写操作会造成较大的性能开销,尤其是在事务开始时。写入流程数据首先写入 WAL 文件,然后才从 WAL 刷新到主数据库。数据在开始

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

Retrieval-based-Voice-Conversion-WebUI模型构建指南

一、模型介绍 Retrieval-based-Voice-Conversion-WebUI(简称 RVC)模型是一个基于 VITS(Variational Inference with adversarial learning for end-to-end Text-to-Speech)的简单易用的语音转换框架。 具有以下特点 简单易用:RVC 模型通过简单易用的网页界面,使得用户无需深入了

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

引言 随着时间的发展,大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用,随着人们期望的不断增加,目标也发生了巨大的变化。在短短的几个月的时间里,人们对大模型的认识已经从对其zero-shot能力感到惊讶,转变为考虑改进模型质量、提高模型可用性。 「大语言模型(LLMs)其实就是利用高容量的模型架构(例如Transformer)对海量的、多种多样的数据分布进行建模得到,它包含了大量的先验