Linux设备驱动——驱动模型之基本结构
目录
一、概述
二、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设备驱动——驱动模型之基本结构相关推荐
- Linux设备与驱动学习之----什么是设备
[ 声明:版权所有,欢迎转载,转载请注明出处,请勿用于商业用途] [ 声明:本文属于作者个人理解,如有错误,欢迎大家指正] 在学习Linux设备驱动的过程中我们用到也是看到最多的就是设备和驱动了,接下 ...
- Linux 设备总线驱动模型(转载)
尽管LDD3中说对多数程序员掌握设备驱动模型不是必要的,但对于嵌入式Linux的底层程序员而言,对设备驱动模型的学习非常重要. Linux设备模型的目的:为内核建立一个统一的设备模型,从而又一 ...
- linux设备和驱动注册,Linux驱动第五篇-----驱动注册和生成设备节点
加载驱动的指令是:insmod xx.ko 查看驱动的指令是: lsmod 卸载驱动的指令是:rmmod xx 在include/linux/platform_device.h这个文件中定义了平台驱动 ...
- linux设备和驱动加载的先后顺序
点击打开链接 Linux驱动先注册总线,总线上可以先挂device,也可以先挂driver,那么究竟怎么控制先后的顺序呢. Linux系统使用两种方式去加载系统中的模块:动态和静态. 静态加载:将所有 ...
- 宋宝华:Linux设备与驱动的手动解绑与手动绑定
众所周知,Linux靠设备与驱动之间的match,来完成设备与驱动的bind,从而触发驱动的probe()成员函数被执行.每个bus都有相应的match方法,完成match的总的入口函数是: stat ...
- linux设备和驱动匹配的方法,Linux使用设备树的i2c驱动与设备匹配方式
Linux使用设备树的i2c驱动与设备匹配有3种方式: of_driver_match_device acpi_driver_match_device i2c_match_id 源码: static ...
- Linux设备与驱动学习之----什么是驱动
[ 声明:版权所有,欢迎转载,转载请注明出处,请勿用于商业用途] [ 声明:本文属于作者个人理解,如有错误,欢迎大家指正] 在上一篇博文中我们介绍了 Linux 中的device 概念,这篇博文中我们 ...
- linux 设备节点 驱动,【Linux驱动】自动创建设备节点
开始学习驱动的时候,是将驱动程序编译成模块然后用mknod命令手动建立设备节点以提供给应用程序调用.这对于刚开始调试驱动程序的时候常用的一种方法.但是,当有种需要必须在系统启动的时候就将驱动程序就绪, ...
- linux设备树 驱动,(9条消息)zynq linux驱动之使用设备树开发
PC:Windows 10 虚拟机:ubuntu 16.04 vivado:2017.04 PetaLinux:2017.04 开发板:黑金AX7010 根文件系统:debian8 --------- ...
- 一张图说明linux 设备 节点 驱动 主设备号 和次设备号之间的关系
Linux各种设备都以文件的形式存放在/dev目录下,称为设备文件. 应用程序可以打开.关闭和读写这些设备文件,完成对设备的操作,就像操作普通的数据文件一样.为了管理这些设备,系统为设备编了号,每个设 ...
最新文章
- 北京大学计算机女博士经常看什么资料?
- 在三层交换机上配置DHCP
- 架构师成长系列 | 从 2019 到 2020,Apache Dubbo 年度回顾与总结
- 使用HttpHandler解析并展示PDF文档内容
- Luogu2295 MICE
- 如何把自己写的python程序给别人用
- 类库从自带的配置文件中获取信息(DLL文件 获取 DLL文件自带的配置信息) z...
- Java基础 - 易错知识点整理(待更新)
- 满满干货!邮储银行java面试
- 微信emoji表情包存入数据库报错处理
- 梨花风起正清明,清明习俗知多少?
- 小白学java-JVM知识点总结
- 这几个图片格式转换工具可以收藏
- 宁波实训day1: java web开发常用工具安装
- 亚控科技的KingSCADA多个漏洞
- 利用PE安装ISO镜像(以及精简版镜像)/安装忍术渗透系统
- 用python打开\显示\保存图像
- 在苹果Mac电脑中如何将键盘当作鼠标使用?
- 英文个人简历中英文词汇对照大全
- JS 删除Object中属性
热门文章
- Excel数据导入到oracle
- 魔兽世界服务器Trinitycore分析二:auth server的main函数
- 攻防世界misc新手_攻防世界密码学解密
- nacos默认用户名密码_Docker下,两分钟极速体验Nacos配置中心
- Spring Security基本原理
- 【AlphaGo之后会是什么】一文读懂人工智能打德扑
- 数据库性能指标 2005-04-06 19:36:14(转载)
- 索引、视图和同义词、序列
- 使用perforce+git处理连线离线工作的pipeline
- Android学习---解决Android Graphical Layout 界面效果不显示