目录

一、概述

二、kobject

2.1 说明

2.2 数据抽象

2.3 接口

2.4 实现

三 、kset

3.1 说明

3.2 数据抽象

3.3 接口

3.4 实现

四、uevent

4.1 说明

4.2 数据抽象

4.3 接口

4.4 实现

四、总结


一、概述

将以前的笔记整理一下,便于查阅。

二、kobject

2.1 说明

kobject(kernel object)实际上是顶层抽象基类,它的主要功能有:

  • 引用计数(reference count),当其为0时释放
  • 建立层次结构(hierarchy):通过parent指针,指向上层kobject
  • 利用建立的结构,配合sysfs,将kobject信息导出到用户空间

2.2 数据抽象

struct kobject {const char       *name;          /* 外部数据:属性 */struct list_head   entry;      /* 内部数据:层次结构 */struct kobject       *parent;    /* 外部数据:层次结构 */struct kset      *kset;          /* 外部数据:层次结构 */struct kobj_type *ktype;     /* 外部数据:方法 */struct kernfs_node *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;
};
  • 属性 name
  • 结构 entry, parent, kset,sd,kref
  • 方法/接口 ktype

看下ktype:

struct kobj_type {void (*release)(struct kobject *kobj);const struct sysfs_ops *sysfs_ops;struct attribute **default_attrs;const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);const void *(*namespace)(struct kobject *kobj);
};
  • release和kobject释放有关系,由于object层层被继承,父类不可能知道子类资源的释放方式,当子类的实例销毁时,注册自己release就好。
  • sysfs_ops和default_attrs和sysfs文件系统有关,用于显示kobj相关的信息,实现时逐级调用上层的show/store实现。

2.3 接口

  • void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
  • int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...)
  • int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...)
  • struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)

对一个外部动态分配kobject来说,使用kobject_init_and_add接口,初始化时需要指定自己的name,ktype,和parent。

如果使用kobject_create_and_add,由内部分配kobject结构,且其ktype由内部指定,对应:

static struct kobj_type dynamic_kobj_ktype = {.release  = dynamic_kobj_release,.sysfs_ops  = &kobj_sysfs_ops,
};

注意看,当ktype由此接口内部指定时,说明调用者没有什么额外的资源,直接使用内部的release函数就好了:

static void dynamic_kobj_release(struct kobject *kobj)
{pr_debug("kobject: (%p): %s\n", kobj, __func__);kfree(kobj);
}

而sysfs_ops也是集中在kobj层面,对于sysfs文件的内容,Linux也抽象了一个称为struct attribute的基类,不同层级只要继承该结构,并提供真正的操作函数即可,如kobject默认的:

struct kobj_attribute {struct attribute attr;ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,char *buf);ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t count);
};

实现上:

static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
{struct kobj_attribute *kattr;ssize_t ret = -EIO;kattr = container_of(attr, struct kobj_attribute, attr);if (kattr->show)ret = kattr->show(kobj, kattr, buf);return ret;
}

2.4 实现

kref 用来实现引用计数

sd用来和sysfs交互,这里不具体展开,认为每个sysfs的节点与一个kobject相关连就好了,这样再通过parent就建立起一个关于kobject的树形结构

kobject_init就是完成动态的初始化——指定方法和内部基本结构

kobject_add就是将kobj加入到sysfs,设置name,这里需要注意:

  • 如果parent为空,就设置为kobject所属的kset对应的kobject,这里面的含义是,kset是特殊kobject,它是kobject的容器,表示一类有共同特性的kobject;如果没有对应的kset与之关联,就置空,这意味着kobject的节点在sysfs根目录下。
 parent = kobject_get(kobj->parent);/* 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_init_and_add完成上述两个函数的功能。

三 、kset

3.1 说明

kset本身继承了kobject,是一个特殊的kobj,主要功能是
1. 提供kobj的集合
2. 提供对容器内所有成员进行管理的机制:uevnet

3.2 数据抽象

struct kset {struct list_head list;spinlock_t list_lock;struct kobject kobj;const struct kset_uevent_ops *uevent_ops;
};
  • 继承 kobj
  • 方法 uevent_ops
  • 结构 list,list_lock

kset要管理位于容器内的kobj,使用list

3.3 接口

如果使用kset_create_and_add,kset由函数内部分配,此时ktype也由内部指定,其他的name,parent, uvent_ops由调用者指定。

  • void kset_init(struct kset *k)
  • int kset_register(struct kset *k)
  • struct kset *kset_create_and_add(const char *name, const struct kset_uevent_ops *uevent_ops,  struct kobject *parent_kobj)
static void kset_release(struct kobject *kobj)
{struct kset *kset = container_of(kobj, struct kset, kobj);pr_debug("kobject: '%s' (%p): %s\n",kobject_name(kobj), kobj, __func__);kfree(kset);
}static struct kobj_type kset_ktype = {.sysfs_ops  = &kobj_sysfs_ops,.release = kset_release,
};

如果kset嵌入了其他结构而随其一同动态分配,就要使用kset_register接口了

3.4 实现

void kset_init(struct kset *k)
{kobject_init_internal(&k->kobj);INIT_LIST_HEAD(&k->list);spin_lock_init(&k->list_lock);
}

这里kset_init似乎实现的不那么考究,在我看来,既然kset继承于kobject,在初始化时就应该使用kobject提供的外部接口,但是一些object所谓的外部变量如name,ktype该如何传递呢?kset在提供接口时使用了kobject中的internal接口——即不会涉及到外部变量的传递

int kset_register(struct kset *k)
{int err;kset_init(k);err = kobject_add_internal(&k->kobj);kobject_uevent(&k->kobj, KOBJ_ADD);return 0;
}

可以看到kset_register在实现上和kset_init思路一样。在使用kset_register接口时,要自行初始化父类(对kset来说,name,ktype,parent)和自身uevent_ops

  • uevent_ops
  • name, parent(kset), ktype

kset_create_and_add提供了动态分配的API版本,和kobject类似,该接口没有更上层的kset了:

 /** The kobject of this kset will have a type of kset_ktype and belong to* no kset itself.  That way we can properly free it when it is* finished being used.*/kset->kobj.ktype = &kset_ktype;kset->kobj.kset = NULL;
struct kset *kset_create_and_add(const char *name,const struct kset_uevent_ops *uevent_ops,struct kobject *parent_kobj)
{struct kset *kset;int error;kset = kset_create(name, uevent_ops, parent_kobj);error = kset_register(kset);return kset;
}

四、uevent

4.1 说明

前面说到kobject提供将内核信息导出到用户空间的功能,这是通过uvent机制实现的

4.2 数据抽象

enum kobject_action {KOBJ_ADD,KOBJ_REMOVE,KOBJ_CHANGE,KOBJ_MOVE,KOBJ_ONLINE,KOBJ_OFFLINE,KOBJ_MAX
};

4.3 接口

将指定的kobject指定kobject_actio导出到用户态:

  • int kobject_uevent(struct kobject *kobj, enum kobject_action action)
  • int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, char *envp_ext[])

4.4 实现

int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,char *envp_ext[])
{/* search the kset we belong to */top_kobj = kobj;while (!top_kobj->kset && top_kobj->parent)top_kobj = top_kobj->parent;if (!top_kobj->kset) {pr_debug("kobject: '%s' (%p): %s: attempted to send uevent ""without kset!\n", kobject_name(kobj), kobj,__func__);return -EINVAL;}kset = top_kobj->kset;uevent_ops = kset->uevent_ops;if (kobj->uevent_suppress) {pr_debug("kobject: '%s' (%p): %s: uevent_suppress ""caused the event to drop!\n",kobject_name(kobj), kobj, __func__);return 0;}/* skip the event, if the filter returns zero. */if (uevent_ops && uevent_ops->filter)if (!uevent_ops->filter(kset, kobj)) {pr_debug("kobject: '%s' (%p): %s: filter function ""caused the event to drop!\n",kobject_name(kobj), kobj, __func__);return 0;}
}
  • 必须找到kobject对应的kset
  • 检查一些条件,kobj->uevent_suppress设置则丢弃
  • uevent_ops->filter,使用kset管理kobject,如果容器不允许其通过,则丢弃
 /* environment buffer */env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);if (!env)return -ENOMEM;/* complete object path */devpath = kobject_get_path(kobj, GFP_KERNEL);if (!devpath) {retval = -ENOENT;goto exit;}/* default keys */retval = add_uevent_var(env, "ACTION=%s", action_string);if (retval)goto exit;retval = add_uevent_var(env, "DEVPATH=%s", devpath);if (retval)goto exit;retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);if (retval)goto exit;/* keys passed in from the caller */if (envp_ext) {for (i = 0; envp_ext[i]; i++) {retval = add_uevent_var(env, "%s", envp_ext[i]);if (retval)goto exit;}}/* let the kset specific function add its stuff */if (uevent_ops && uevent_ops->uevent) {retval = uevent_ops->uevent(kset, kobj, env);if (retval) {pr_debug("kobject: '%s' (%p): %s: uevent() returned ""%d\n", kobject_name(kobj), kobj,__func__, retval);goto exit;}}
  • 如果可以发送,则要将发送的消息整合起来,通过kobj_uevent_env{}
  • 将action_string,devpath,subsystem,额外信息envp_ext,uevent_ops->uevent,uevent_seqnum都加入消息中。

分析一下构造消息的过程:

struct kobj_uevent_env {char *argv[3];char *envp[UEVENT_NUM_ENVP];int envp_idx;char buf[UEVENT_BUFFER_SIZE];int buflen;
};

将上述消息放到buf中,每单一的消息由envp指向,消息的数量envp_idx,这个功能由add_uevent_var实现,很简单,就不再分析了。

 /* send netlink message */list_for_each_entry(ue_sk, &uevent_sock_list, list) {struct sock *uevent_sock = ue_sk->sk;struct sk_buff *skb;size_t len;if (!netlink_has_listeners(uevent_sock, 1))continue;/* allocate message with the maximum possible size */len = strlen(action_string) + strlen(devpath) + 2;skb = alloc_skb(len + env->buflen, GFP_KERNEL);if (skb) {char *scratch;/* add header */scratch = skb_put(skb, len);sprintf(scratch, "%s@%s", action_string, devpath);/* copy keys to our continuous event payload buffer */for (i = 0; i < env->envp_idx; i++) {len = strlen(env->envp[i]) + 1;scratch = skb_put(skb, len);strcpy(scratch, env->envp[i]);}NETLINK_CB(skb).dst_group = 1;retval = netlink_broadcast_filtered(uevent_sock, skb,0, 1, GFP_KERNEL,kobj_bcast_filter,kobj);/* ENOBUFS should be handled in userspace */if (retval == -ENOBUFS || retval == -ESRCH)retval = 0;} elseretval = -ENOMEM;}
  • 通过netlink构造一个skb将整合过的消息发出去。

五、总结

kobject和kset的关系结构上可以用下图表示:

蓝色箭头构建的是sysfs,黑色箭头方便kset管理容器内的kobject,红色箭头便于kobject找到所属kset

Linux设备驱动——驱动模型之基本结构相关推荐

  1. Linux设备与驱动学习之----什么是设备

    [ 声明:版权所有,欢迎转载,转载请注明出处,请勿用于商业用途] [ 声明:本文属于作者个人理解,如有错误,欢迎大家指正] 在学习Linux设备驱动的过程中我们用到也是看到最多的就是设备和驱动了,接下 ...

  2. Linux 设备总线驱动模型(转载)

    尽管LDD3中说对多数程序员掌握设备驱动模型不是必要的,但对于嵌入式Linux的底层程序员而言,对设备驱动模型的学习非常重要.     Linux设备模型的目的:为内核建立一个统一的设备模型,从而又一 ...

  3. linux设备和驱动注册,Linux驱动第五篇-----驱动注册和生成设备节点

    加载驱动的指令是:insmod xx.ko 查看驱动的指令是: lsmod 卸载驱动的指令是:rmmod xx 在include/linux/platform_device.h这个文件中定义了平台驱动 ...

  4. linux设备和驱动加载的先后顺序

    点击打开链接 Linux驱动先注册总线,总线上可以先挂device,也可以先挂driver,那么究竟怎么控制先后的顺序呢. Linux系统使用两种方式去加载系统中的模块:动态和静态. 静态加载:将所有 ...

  5. 宋宝华:Linux设备与驱动的手动解绑与手动绑定

    众所周知,Linux靠设备与驱动之间的match,来完成设备与驱动的bind,从而触发驱动的probe()成员函数被执行.每个bus都有相应的match方法,完成match的总的入口函数是: stat ...

  6. linux设备和驱动匹配的方法,Linux使用设备树的i2c驱动与设备匹配方式

    Linux使用设备树的i2c驱动与设备匹配有3种方式: of_driver_match_device acpi_driver_match_device i2c_match_id 源码: static ...

  7. Linux设备与驱动学习之----什么是驱动

    [ 声明:版权所有,欢迎转载,转载请注明出处,请勿用于商业用途] [ 声明:本文属于作者个人理解,如有错误,欢迎大家指正] 在上一篇博文中我们介绍了 Linux 中的device 概念,这篇博文中我们 ...

  8. linux 设备节点 驱动,【Linux驱动】自动创建设备节点

    开始学习驱动的时候,是将驱动程序编译成模块然后用mknod命令手动建立设备节点以提供给应用程序调用.这对于刚开始调试驱动程序的时候常用的一种方法.但是,当有种需要必须在系统启动的时候就将驱动程序就绪, ...

  9. linux设备树 驱动,(9条消息)zynq linux驱动之使用设备树开发

    PC:Windows 10 虚拟机:ubuntu 16.04 vivado:2017.04 PetaLinux:2017.04 开发板:黑金AX7010 根文件系统:debian8 --------- ...

  10. 一张图说明linux 设备 节点 驱动 主设备号 和次设备号之间的关系

    Linux各种设备都以文件的形式存放在/dev目录下,称为设备文件. 应用程序可以打开.关闭和读写这些设备文件,完成对设备的操作,就像操作普通的数据文件一样.为了管理这些设备,系统为设备编了号,每个设 ...

最新文章

  1. 北京大学计算机女博士经常看什么资料?
  2. 在三层交换机上配置DHCP
  3. 架构师成长系列 | 从 2019 到 2020,Apache Dubbo 年度回顾与总结
  4. 使用HttpHandler解析并展示PDF文档内容
  5. Luogu2295 MICE
  6. 如何把自己写的python程序给别人用
  7. 类库从自带的配置文件中获取信息(DLL文件 获取 DLL文件自带的配置信息) z...
  8. Java基础 - 易错知识点整理(待更新)
  9. 满满干货!邮储银行java面试
  10. 微信emoji表情包存入数据库报错处理
  11. 梨花风起正清明,清明习俗知多少?
  12. 小白学java-JVM知识点总结
  13. 这几个图片格式转换工具可以收藏
  14. 宁波实训day1: java web开发常用工具安装
  15. 亚控科技的KingSCADA多个漏洞
  16. 利用PE安装ISO镜像(以及精简版镜像)/安装忍术渗透系统
  17. 用python打开\显示\保存图像
  18. 在苹果Mac电脑中如何将键盘当作鼠标使用?
  19. 英文个人简历中英文词汇对照大全
  20. JS 删除Object中属性

热门文章

  1. Excel数据导入到oracle
  2. 魔兽世界服务器Trinitycore分析二:auth server的main函数
  3. 攻防世界misc新手_攻防世界密码学解密
  4. nacos默认用户名密码_Docker下,两分钟极速体验Nacos配置中心
  5. Spring Security基本原理
  6. 【AlphaGo之后会是什么】一文读懂人工智能打德扑
  7. 数据库性能指标 2005-04-06 19:36:14(转载)
  8. 索引、视图和同义词、序列
  9. 使用perforce+git处理连线离线工作的pipeline
  10. Android学习---解决Android Graphical Layout 界面效果不显示