LINUX设备驱动之设备模型一kobject

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

转载请注明出处:http://ericfang.cublog.cn/

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

LINUX设备驱动驱动程序模型的核心数据结构是kobject,kobject数据结构在/linux/kobject.h中定义:

struct kobject {

const char             *name;

struct list_head       entry;

struct kobject         *parent;

struct kset             *kset;

struct kobj_type     *ktype;

struct sysfs_dirent  *sd;

struct kref             kref;

unsigned int state_initialized:1;

unsigned int state_in_sysfs:1;

unsigned int state_add_uevent_sent:1;

unsigned int state_remove_uevent_sent:1;

unsigned int uevent_suppress:1;

};

每个kobject都有它的父节点parent、kset、kobj_type指针,这三者是驱动模型的基本结构,kset是kobject的集合,在/linux/kobject.h中定义:

struct kset {

struct list_head list;

spinlock_t list_lock;

struct kobject kobj;

struct kset_uevent_ops *uevent_ops;

};

可以看到每个kset内嵌了一个kobject(kobj字段),用来表示其自身节点,其list字段指向了所包含的kobject的链表头。我们在后面的分析中将看到kobject如果没有指定父节点,parent将指向其kset内嵌的kobject。

每个kobject都有它的kobj_type字段指针,用来表示kobject在文件系统中的操作方法,kobj_type结构也在/linux/kobject.h中定义:

struct kobj_type {

void (*release)(struct kobject *kobj);

struct sysfs_ops *sysfs_ops;

struct attribute ** default_attrs;

};

release方法是在kobject释放是调用,sysfs_ops指向kobject对应的文件操作,default_attrskobject的默认属性,sysfs_ops的将使用default_attrs属性(在后面的分析中我们将会看到)。

从上面的分析我们可以想象到kobject、kset、kobj_type的层次结构:

我们可以把一个kobject添加到文件系统中去(实际上是添加到其父节点所代表的kset中去),内核提供kobject_create_and_add()接口函数:

struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)

{

struct kobject *kobj;

int retval;

kobj = kobject_create();

if (!kobj)

return NULL;

retval = kobject_add(kobj, parent, "%s", name);

if (retval) {

printk(KERN_WARNING "%s: kobject_add error: %d/n",

__func__, retval);

kobject_put(kobj);

kobj = NULL;

}

return kobj;

}

kobject _create()为要创建的kobject分配内存空间并对其初始化。

struct kobject *kobject_create(void)

{

struct kobject *kobj;

kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);

if (!kobj)

return NULL;

kobject_init(kobj, &dynamic_kobj_ktype);

return kobj;

}

kobject_init()对kobject基本字段进行初始化,用输入参数设置kobj_type属性。

这里粘出代码以供参考:

void kobject_init(struct kobject *kobj, struct kobj_type *ktype)

{

char *err_str;

if (!kobj) {

err_str = "invalid kobject pointer!";

goto error;

}

if (!ktype) {

err_str = "must have a ktype to be initialized properly!/n";

goto error;

}

if (kobj->state_initialized) {

/* do not error out as sometimes we can recover */

printk(KERN_ERR "kobject (%p): tried to init an initialized "

"object, something is seriously wrong./n", kobj);

dump_stack();

}

kobject_init_internal(kobj);

kobj->ktype = ktype;

return;

error:

printk(KERN_ERR "kobject (%p): %s/n", kobj, err_str);

dump_stack();

}

static void kobject_init_internal(struct kobject *kobj)

{

if (!kobj)

return;

kref_init(&kobj->kref);

INIT_LIST_HEAD(&kobj->entry);

kobj->state_in_sysfs = 0;

kobj->state_add_uevent_sent = 0;

kobj->state_remove_uevent_sent = 0;

kobj->state_initialized = 1;

}

接着看kobject_add()函数:

int kobject_add(struct kobject *kobj, struct kobject *parent,

const char *fmt, ...)

{

va_list args;

int retval;

if (!kobj)

return -EINVAL;

if (!kobj->state_initialized) {

printk(KERN_ERR "kobject '%s' (%p): tried to add an "

"uninitialized object, something is seriously wrong./n",

kobject_name(kobj), kobj);

dump_stack();

return -EINVAL;

}

va_start(args, fmt);

retval = kobject_add_varg(kobj, parent, fmt, args);

va_end(args);

return retval;

}

在上面的初始化中已把位变量设位1

va_start(args, fmt)和va_end(args)使用可变参数(可见参数用法不在这里分析),在kobject_add_varg中将把fmt指向的内容赋给kobject的name字段。下面我们详细看看kobject_add_varg函数:

static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,

const char *fmt, va_list vargs)

{

int retval;

retval = kobject_set_name_vargs(kobj, fmt, vargs);

if (retval) {

printk(KERN_ERR "kobject: can not set name properly!/n");

return retval;

}

kobj->parent = parent;

return kobject_add_internal(kobj);

}

kobject_set_name_vargs(kobj, fmt, vargs),如果kobj的name字段指向的内容为空,则为分配一个内存空间并用fmt指向的内容初始化,把地址赋给kobj的name字段。

int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,

va_list vargs)

{

const char *old_name = kobj->name;

char *s;

if (kobj->name && !fmt)

return 0;

kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);

if (!kobj->name)

return -ENOMEM;

/* ewww... some of these buggers have '/' in the name ... */

while ((s = strchr(kobj->name, '/')))

s[0] = '!';

kfree(old_name);

return 0;

}

char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap)

{

unsigned int len;

char *p;

va_list aq;

va_copy(aq, ap);

len = vsnprintf(NULL, 0, fmt, aq);

va_end(aq);

p = kmalloc(len+1, gfp);

if (!p)

return NULL;

vsnprintf(p, len+1, fmt, ap);

return p;

}

继续kobject_add_varg()返回kobject_add_internal(kobj),就是在这个函数理为kobj创建文件系统结构:

static int kobject_add_internal(struct kobject *kobj)

{

int error = 0;

struct kobject *parent;

if (!kobj)

return -ENOENT;

if (!kobj->name || !kobj->name[0]) {

WARN(1, "kobject: (%p): attempted to be registered with empty "

"name!/n", kobj);

return -EINVAL;

}

检查kobj和它的name字段,不存在则返回错误信息。

parent = kobject_get(kobj->parent);

获得其父节点,并增加父节点的计数器,kobject结构中的kref字段用于容器的计数,kobject_get和kobject_put分别增加和减少计数器,如果计数器为0,则释放该kobject,kobject_get返回该kobject。

/* join kset if set, use it as parent if we do not already have one */

if (kobj->kset) {

if (!parent)

parent = kobject_get(&kobj->kset->kobj);

kobj_kset_join(kobj);

kobj->parent = parent;

}

在这里我们可以看到,如果调用kobject_create_and_add()时参数parent设为NULL,则会去检查kobj的kset是否存在,如果存在就会把kset所嵌套的kobj作为其父节点,并把kobj添加到kset中去。

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>");

打印一些调试信息,接着为kobj创建目录:

error = create_dir(kobj);

if (error) {

kobj_kset_leave(kobj);

kobject_put(parent);

kobj->parent = NULL;

/* be noisy on error issues */

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;

}

如果创建不成功,则回滚上面的操作,成功的话则设置kobj的state_in_sysfs标志。

在看看create_dir()函数中具体创建了那些内容:

static int create_dir(struct kobject *kobj)

{

int error = 0;

if (kobject_name(kobj)) {

error = sysfs_create_dir(kobj);

if (!error) {

error = populate_dir(kobj);

if (error)

sysfs_remove_dir(kobj);

}

}

return error;

}

sysfs_create_dir()先为kobj创建了一个目录文件

int sysfs_create_dir(struct kobject * kobj)

{

struct sysfs_dirent *parent_sd, *sd;

int error = 0;

BUG_ON(!kobj);

if (kobj->parent)

parent_sd = kobj->parent->sd;

else

parent_sd = &sysfs_root;

error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd);

if (!error)

kobj->sd = sd;

return error;

}

如果kobj->parent为NULL,就把&sysfs_root作为父节点sd,即在/sys下面创建结点。

然后调用populate_dir:

static int populate_dir(struct kobject *kobj)

{

struct kobj_type *t = get_ktype(kobj);

struct attribute *attr;

int error = 0;

int i;

if (t && t->default_attrs) {

for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {

error = sysfs_create_file(kobj, attr);

if (error)

break;

}

}

return error;

}

得到kobj的kobj_type,历遍kobj_type的default_attrs并创建属性文件,文件的操作会回溯到sysfs_ops的show和store会调用封装了attribute的kobj_attribute结构的store和show方法(在后面的代码中将会分析)。

由于上面kobject_init(kobj, &dynamic_kobj_ktype)用默认dynamic_kobj_ktype作为kobj_type参数,而dynamic_kobj_ktype的default_attrs为NULL,所以这里没有创建属性文件。

至此,我们已经知道了kobject_create_and_add()函数创建kobject,挂到父kobject,并设置其kobj_type,在文件系统中为其创建目录和属性文件等。

另外,如果我们已静态定义了要创建的kobject,则可以调用kobject_init_and_add()来注册kobject,其函数如下:

int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,

struct kobject *parent, const char *fmt, ...)

{

va_list args;

int retval;

kobject_init(kobj, ktype);

va_start(args, fmt);

retval = kobject_add_varg(kobj, parent, fmt, args);

va_end(args);

return retval;

}

通过上面的分析我们很轻松就能理解这个函数。

内核提供注销kobject的函数是kobject_del()

void kobject_del(struct kobject *kobj)

{

if (!kobj)

return;

sysfs_remove_dir(kobj);

kobj->state_in_sysfs = 0;

kobj_kset_leave(kobj);

kobject_put(kobj->parent);

kobj->parent = NULL;

}

删除kobj目录及其目录下的属性文件,清kobj的state_in_sysfs标志,把kobj从kset中删除,减少kobj->parent的计数并设其指针为空。

LINUX设备驱动之设备模型一kobject相关推荐

  1. LINUX设备驱动之设备模型一--kobject

    http://blog.csdn.net/yangzhu1982/article/details/6186016 Linux设备驱动之设备模型一kobject Eric Fang  2010-01-1 ...

  2. linux设备驱动--字符设备模型

    linux设备驱动--字符设备模型 最近正在学习设备驱动开发,因此打算写一个系列博客,即是对自己学习的一个总结,也是对自己的一个督促,有不对,不足,需要改正的地方还望大家指出,而且希望结识志同道合的朋 ...

  3. 字符设备驱动、平台设备驱动、设备驱动模型、sysfs的比较和关联

    参考原文:https://www.kancloud.cn/yueqian_scut/emlinux/106829 对原文笔误地方做了修改.重新排版 目录 字符设备驱动.平台设备驱动.设备驱动模型.sy ...

  4. Linux设备驱动和设备匹配过程

    Linux设备驱动和设备匹配过程 1. 设备驱动匹配简述 2. 重点结构体介绍 2.1 `struct device` 2.2 `struct platform_device` 2.3 `struct ...

  5. 【驱动】linux设备驱动·字符设备驱动开发

    Preface 前面对linux设备驱动的相应知识点进行了总结,现在进入实践阶段! <linux设备驱动入门篇>:http://infohacker.blog.51cto.com/6751 ...

  6. Linux设备模型、平台设备驱动、设备树(device tree)、GPIO子系统以及pinctrl子系统介绍

    文章目录 一.Linux设备模型介绍 (1)设备驱动模型总体介绍 (2)设备驱动模型文件表现 (3)设备驱动模型工作原理 [1]总线 [2]设备 [3]驱动 [4]注册流程 二.平台设备驱动介绍 (1 ...

  7. Linux设备驱动--块设备(二)之相关结构体

    上回最后面介绍了相关数据结构,下面再详细介绍 块设备对象结构 block_device 内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区.如果该结构代表一个分区,则其成 ...

  8. bio linux 创建_Linux设备驱动--块设备之概念和框架以及相关结构体

    基本概念 块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性读到 ...

  9. Linux设备驱动--块设备(三)之程序设计(转)

    http://blog.csdn.net/jianchi88/article/details/7212701 块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数 ...

最新文章

  1. 神经网络完成芯片设计仅需几小时
  2. Day20 Ajax
  3. 简单阐述hdfs的体系结构_HDFS主要特性和体系结构
  4. ElementUI数据联动计算并实现数据校验
  5. 再次搬家到自建博客http://www.wikieee.com/blog/leiad/homepage
  6. C 语言和C++语言的 struct对比
  7. JBoss Forge NetBeans集成–入门
  8. android 年月日时分秒控件,Android 年月日时分秒时间选择器
  9. 实现键盘输入信息,按enter键调用摄像头自动拍照
  10. OpenCV学习——摄像头实时进行人脸识别
  11. flexray VPX控制板设计方案详细
  12. 【背包问题】基于粒子群算法求解0-1背包问题matlab代码
  13. excel两个表格数据对比_常简单又实用的Excel数据对比技巧
  14. c语言头文件写法,C语言头文件正确写法
  15. 【深度学习】写诗机器人tensorflow实现
  16. 献给正在纠结的朋友——转产品还是转测试
  17. 隔壁老王|亲述,我的运维心路历程
  18. IOS开发之——音乐播放器-概述(01)
  19. oracle dba 培训教程 第15章 管理角色
  20. 【数据库】国土空间规划数据库(全域)_模板空库

热门文章

  1. 第一章 数据资产的定义
  2. Win7笔记本如何调整屏幕亮度
  3. Redirect组件的使用
  4. ffmpeg音频滤镜
  5. 《Fast Online Object Tracking and Segmentation: A Unifying Approach》
  6. cmake错误:Could not find a configuration file for package “Eigen3“
  7. 为什么要用PolyFill(JS中的修补匠)
  8. logstash集成elasticsearch报错: Attempted to resurrect connection to dead ES instance
  9. POJ 2263 Heavy Cargo
  10. zblogphp登录和注册_基于PHP的登录和注册的功能的实现