http://blog.csdn.net/yangzhu1982/article/details/6186016

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

Eric Fang  2010-01-11

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

本站分析linux内核源码,版本号为2.6.32.3

转载请注明出处: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

    LINUX设备驱动之设备模型一kobject -------------------------------------------------------------- 转载请注明出处:http:/ ...

  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. python详细下载安装教程-Pycharm及python安装详细教程
  2. IAR编译的工程无法正常仿真的问题
  3. 分享两个JavaScript打乱数组顺序实现随机排序洗牌的方法(应用于音乐视频的随机播放等)
  4. android权限 启动失败,Android 6.0打开失败:EACCES(权限被拒绝)
  5. 专访Vue作者尤雨溪:Vue CLI 3.0重构的原因
  6. win10打印机终结点映射器_用了就回不去?微软官方免费“外挂”,让win10好用到飞起...
  7. 如何利用PHP会话显示出当前在线的用户
  8. 惊艳的HTML5粒子动画特效
  9. Android 系统(231)--OTA对要发布的编译版本进行签名
  10. Spring : Spring Aop之ProxyFactory
  11. HDU3549+Ford-Fulkerson
  12. mysql4语法_4 MySQL 语法技巧
  13. 深山红叶PE工具箱嫦娥一号纪念版
  14. 2023长安大学物流管理专硕考研成功经验分享
  15. java软件工程师基本技能_Java软件工程师主要有什么技能
  16. 微信小程序 getPhoneNumber获取用户手机号
  17. VR和AR将如何发展下去?哪个更有前景?
  18. Elasticsearch:运用 Go 语言实现 Elasticsearch 搜索
  19. vivado 2019.2下载地址分享,网盘分享
  20. 想从动物科学转专业去计算机,动物科学转专业或者加第二专业动物医学,望老师同学们提些建议。...

热门文章

  1. 每日一皮:这年头没点绝活连洗车都不行...
  2. suse 安装mysql5.6_SuSE11安装MySQL5.6.40:RPM安装方式
  3. linux系统各文件夹的作用,linux系统文件夹的作用 good
  4. PCL点云数据 滤波降噪
  5. python DBSCAN聚类例子
  6. leetcode精选
  7. Pytorch 计算参数量与计算量Flops
  8. pytorch cpu占用较高
  9. std thread
  10. src is not broadcastable to dst, but they have the same number of elements