linux设备驱动模型
为什么需要设备驱动模型
- 早期内核(2.4之前)没有统一的设备驱动模型,但照样可以用
- 2.4~2.6期间使用devfs,挂载在/dev目录。
- 需要在内核驱动中创建设备文件(devfs_register),命名死板
- 2.6以后使用sysfs,挂载在/sys目录
- 将设备分类、分层次统一进行管理
- 配合udev/mdev守护进程动态创建设备文件,命令规则自由制定
sysfs概述
linux系统通过sysfs体现出设备驱动模型
- sysfs是一个虚拟文件系统(类似proc文件系统)
- 目录对应的inode节点会记录基本驱动对象(kobject),从而将系统中的设备组成层次结构
- 用户可以读写目录下的不同文件来配置驱动对象(kobject)的不同属性
设备驱动模型基本元素
-
kobject:sysfs中的一个目录,常用来表示基本驱动对象,不允许发送消息到用户空间
-
kset:sysfs中的一个目录,常用来管理kobject,允许发送消息到用户空间
-
kobj_type:目录下属性文件的操作接口
驱动模型一
kset可批量管理kobject
kobject无法批量管理kobject
驱动模型二
- 上层kobject节点无法遍历查找下层kobject
kobject
sysfs中每一个目录都对应一个kobject
include/linux/kobject.h
struct kobject { //用来表示该kobject的名称 const char *name; //链表节点 struct list_head entry; //该kobject的上层节点,构建kobject之间的层次关系 struct kobject *parent; //该kobject所属的kset对象,用于批量管理kobject对象 struct kset *kset; //该Kobject的sysfs文件系统相关的操作和属性 struct kobj_type *ktype; //该kobject在sysfs文件系统中对应目录项 struct kernfs_node *sd; /* sysfs directory entry */ //该kobject的引用次数 struct kref kref; #ifdef CONFIG_DEBUG_KOBJECT_RELEASE struct delayed_work release; #endif //记录内核对象的初始化状态 unsigned int state_initialized:1; //表示该kobject所代表的内核对象有没有在sysfs建立目录 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; };
kset
struct kset { //用来将起中的object对象构建成链表 struct list_head list; //自旋锁 spinlock_t list_lock; //当前kset内核对象的kobject变量 struct kobject kobj; //定义了一组函数指针,当kset中的某些kobject对象发生状态变化需要通知用户空间时,调用其中的函数来完成 const struct kset_uevent_ops *uevent_ops; }
kobj_type
struct kobj_type { //销毁kobject对象时调用 void (*release)(struct kobject *kobj); //kobject对象属性文件统一操作接口 const struct sysfs_ops *sysfs_ops; //kobject默认属性文件的名字、"文件具体操作接口" struct attribute **default_attrs; const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj); const void *(*namespace)(struct kobject *kobj); void (*get_ownership)(struct kobject *kobj, kuid_t *uid, kgid_t *gid); };
kobject:驱动的基石
- 构建一个kobject对象
- 构建一个sysfs中的目录项(kernfs_node)
- 把他们关联起来
重点
- 关注sysfs目录项与kobject对象的关联过程
- 关注kobject对象默认的属性文件操作接口
kobject_create_and_add()函数
掌握这一个函数即可
lib/kobject.c
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent) { struct kobject *kobj; int retval; /*创建并初始化一个kobject对象*/ kobj = kobject_create(); if (!kobj) return NULL; /*sysfs创建一个目录项并与kobject对象关联*/ retval = kobject_add(kobj, parent, "%s", name); if (retval) { pr_warn("%s: kobject_add error: %d ", __func__, retval); kobject_put(kobj); kobj = NULL; } return kobj; }
kobject_create()函数
lib/kobject.c
struct kobject *kobject_create(void) { struct kobject *kobj; /*动态申请内存,存放kobject对象*/ kobj = kzalloc(sizeof(*kobj), GFP_KERNEL); if (!kobj) return NULL; kobject_init(kobj, &dynamic_kobj_ktype); return kobj; }
static struct kobj_type dynamic_kobj_ktype = { .release = dynamic_kobj_release, .sysfs_ops = &kobj_sysfs_ops, };
const struct sysfs_ops kobj_sysfs_ops = { .show = kobj_attr_show, .store = kobj_attr_store, };
kobject_init()函数
lib/kobject.c
void kobject_init(struct kobject *kobj, struct kobj_type *ktype) { ... kobject_init_internal(kobj); /*设置目录属性文件的操作接口*/ kobj->ktype = ktype; return; ... }
kobject_init_internal()函数
lib/kobject.c
static void kobject_init_internal(struct kobject *kobj) { if (!kobj) return; /*将kobject的引用计数设置为1*/ kref_init(&kobj->kref); /*初始化链表节点*/ INIT_LIST_HEAD(&kobj->entry); /*该kobject对象还没和sysfs目录项关联*/ kobj->state_in_sysfs = 0; kobj->state_add_uevent_sent = 0; kobj->state_remove_uevent_sent = 0; /*kobject对象的初始化标志*/ kobj->state_initialized = 1; }
kobject_add()函数
lib/kobject.c
retval = kobject_add(kobj, parent, “%s”, name);
int kobject_add(struct kobject *kobj, struct kobject *parent,const char *fmt, ...) { va_list args; int retval; ... /*获取第一个可变参数,可变参数函数的实现与函数传参的栈结构有关*/ va_start(args, fmt); retval = kobject_add_varg(kobj, parent, fmt, args); va_end(args); ... return retval; }
kobject_add_varg()函数
lib/kobject.c
static __printf(3, 0) 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) { pr_err("kobject: can not set name properly! "); return retval; } /*第一次设置kobj的parent指针*/ kobj->parent = parent; return kobject_add_internal(kobj); }
kobject_set_name_vargs()函数
lib/kobject.c
int kobject_set_name_vargs(struct kobject *kobj, const char *fmt, va_list vargs) { const char *s; ... /*参数格式化打印到s字符串中*/ s = kvasprintf_const(GFP_KERNEL, fmt, vargs); ... /*设置kobject对象的名称*/ kobj->name = s; ... }
kobject_add_internal()函数
lib/kobject.c
static int kobject_add_internal(struct kobject *kobj) { struct kobject *parent; ... parent = kobject_get(kobj->parent); if (kobj->kset) { /*如果parent为空,parent设置为kobj->kset->kobj*/ if (!parent) parent = kobject_get(&kobj->kset->kobj); /*把该kobject加入到kset链表的末尾*/ kobj_kset_join(kobj); /*第二次设置kobj的parent指针*/ kobj->parent = parent; } ... error = create_dir(kobj); ... kobj->state_in_sysfs = 1; ... }
create_dir()函数
lib/kobject.c
static int create_dir(struct kobject *kobj) { const struct kobj_ns_type_operations *ops; int error; error = sysfs_create_dir_ns(kobj, kobject_namespace(kobj)); ... }
sysfs_create_dir_ns()函数
fs/sysfs/dir.c
int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) { struct kernfs_node *parent, *kn; kuid_t uid; kgid_t gid; BUG_ON(!kobj); if (kobj->parent) /*获取上一层节点的目录项*/ parent = kobj->parent->sd; else /*设置上一层节点的目录项为sysfs根目录*/ parent = sysfs_root_kn; if (!parent) return -ENOENT; kn = kernfs_create_dir_ns(parent, kobject_name(kobj), S_IRWXU | S_IRUGO | S_IXUGO, uid, gid, kobj, ns); ... /*kobj对象关联sysfs目录项*/ kobj->sd = kn; return 0; }
kernfs_create_dir_ns()函数
struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, const char *name, umode_t mode, kuid_t uid, kgid_t gid, void *priv, const void *ns) { struct kernfs_node *kn; int rc; /* allocate */ kn = kernfs_new_node(parent, name, mode | S_IFDIR, uid, gid, KERNFS_DIR); ... /*sysfs目录项关联kobject对象*/ kn->priv = priv; ... }
kernfs_new_node()函数
struct kernfs_node *kernfs_new_node(struct kernfs_node *parent, const char *name, umode_t mode, kuid_t uid, kgid_t gid, unsigned flags) { struct kernfs_node *kn; kn = __kernfs_new_node(kernfs_root(parent), name, mode, uid, gid, flags); if (kn) { kernfs_get(parent); kn->parent = parent; } return kn; }
kobj_type:用户空间的法宝
- 为kobject对象构建多个属性文件
- 为每个属性文件设置具体操作接口
- vfs的inode对象与sysfs的kernfs_node对象的绑定过程
重点
- 关注属性文件具体操作接口的赋值过程
- 关注open()、read()、write函数的底层机制
第一阶段:属性文件操作接口赋值
sysfs_create_group()函数
fs/sysfs/group.c
创建接口文件,并且绑定接口
int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp) { return internal_create_group(kobj, 0, grp); }
-
attribute_group结构体:
include/linux/sysfs.h
struct attribute_group { const char *name; umode_t (*is_visible)(struct kobject *, struct attribute *, int); umode_t (*is_bin_visible)(struct kobject *, struct bin_attribute *, int); struct attribute **attrs; struct bin_attribute **bin_attrs; };
-
struct attribute结构体:
include/linux/sysfs.h
struct attribute { const char *name; umode_t mode; };
- kobj_attribute结构体
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); };
internal_create_group()函数
fs/sysfs/group.c
tatic int internal_create_group(struct kobject *kobj, int update, const struct attribute_group *grp) { struct kernfs_node *kn; kuid_t uid; kgid_t gid; int error; ... if (grp->name) ... else kn = kobj->sd; ... error = create_files(kn, kobj, uid, gid, grp, update); ... }
create_files()函数
fs/sysfs/group.c
static int create_files(struct kernfs_node *parent, struct kobject *kobj, kuid_t uid, kgid_t gid, const struct attribute_group *grp, int update) { struct attribute *const *attr; struct bin_attribute *const *bin_attr; int error = 0, i; if (grp->attrs) { for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) { umode_t mode = (*attr)->mode; ... error = sysfs_add_file_mode_ns(parent, *attr, false, mode, uid, gid, NULL); ... } ... }
sysfs_add_file_mode_ns()函数
fs/sysfs/file.c
int sysfs_add_file_mode_ns(struct kernfs_node *parent, const struct attribute *attr, bool is_bin, umode_t mode, kuid_t uid, kgid_t gid, const void *ns) { struct lock_class_key *key = NULL; const struct kernfs_ops *ops; struct kernfs_node *kn; loff_t size; if (!is_bin) { struct kobject *kobj = parent->priv; /*kobj_sysfs_ops*/ const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops; ... if (sysfs_ops->show && sysfs_ops->store) { if (mode & SYSFS_PREALLOC) ops = &sysfs_prealloc_kfops_rw; else ops = &sysfs_file_kfops_rw; else if ... } ... kn = __kernfs_create_file(parent, attr->name, mode & 0777, uid, gid, size, ops, (void *)attr, ns, key); ... }
- kernfs_ops节点的操作函数
static const struct kernfs_ops sysfs_file_kfops_rw = { .seq_show = sysfs_kf_seq_show, .write = sysfs_kf_write, };
__kernfs_create_file()函数
struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, const char *name, umode_t mode, kuid_t uid, kgid_t gid, loff_t size, const struct kernfs_ops *ops, void *priv, const void *ns, struct lock_class_key *key) { struct kernfs_node *kn; unsigned flags; int rc; flags = KERNFS_FILE; kn = kernfs_new_node(parent, name, (mode & S_IALLUGO) | S_IFREG, uid, gid, flags); if (!kn) return ERR_PTR(-ENOMEM); /*操作接口赋值*/ kn->attr.ops = ops; kn->attr.size = size; kn->ns = ns; /*文件属性赋值*/ kn->priv = priv; if (ops->seq_show) kn->flags |= KERNFS_HAS_SEQ_SHOW; ... }
第二阶段:open()
ead()write()的底层机制
kernfs_init_inode()函数
static void kernfs_init_inode(struct kernfs_node *kn, struct inode *inode) { kernfs_get(kn); /*sysfs的kernels_node赋值给vfs的inode*/ inode->i_private = kn; inode->i_mapping->a_ops = &kernfs_aops; inode->i_op = &kernfs_iops; inode->i_generation = kn->id.generation; set_default_inode_attr(inode, kn->mode); kernfs_refresh_inode(kn, inode); /* 判断sysfs的kernels_node类型 */ switch (kernfs_type(kn)) { case KERNFS_DIR: inode->i_op = &kernfs_dir_iops; inode->i_fop = &kernfs_dir_fops; if (kn->flags & KERNFS_EMPTY_DIR) make_empty_dir_inode(inode); break; case KERNFS_FILE: inode->i_size = kn->attr.size; /*文件的操作接口*/ inode->i_fop = &kernfs_file_fops; break; case KERNFS_LINK: inode->i_op = &kernfs_symlink_iops; break; default: BUG(); } unlock_new_inode(inode); }
const struct file_operations kernfs_file_fops = { .read = kernfs_fop_read, .write = kernfs_fop_write, .llseek = generic_file_llseek, .mmap = kernfs_fop_mmap, .open = kernfs_fop_open, .release = kernfs_fop_release, .poll = kernfs_fop_poll, .fsync = noop_fsync, };
kernfs_fop_open()函数
static int kernfs_fop_open(struct inode *inode, struct file *file) { struct kernfs_node *kn = inode->i_private; struct kernfs_open_file *of; ... of = kzalloc(sizeof(struct kernfs_open_file), GFP_KERNEL); ... /*sysfs中文件的kernfs_node赋值给of->kn*/ of->kn = kn; /*进程的struct file赋值给of->file/ of->file = file; ... if (ops->seq_show) error = seq_open(file, &kernfs_seq_ops); ... /*struct file的私有指针赋值给of->seq_file */ of->seq_file = file->private_data; /*of赋值给of->seq_file->private*/ of->seq_file->private = of; ... }
static const struct seq_operations kernfs_seq_ops = { .start = kernfs_seq_start, .next = kernfs_seq_next, .stop = kernfs_seq_stop, .show = kernfs_seq_show, };
seq_open
int seq_open(struct file *file, const struct seq_operations *op) { struct seq_file *p; WARN_ON(file->private_data); p = kmem_cache_zalloc(seq_file_cache, GFP_KERNEL); if (!p) return -ENOMEM; file->private_data = p; ... p->op = op; ... }
kernfs_fop_read()函数
static ssize_t kernfs_fop_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct kernfs_open_file *of = kernfs_of(file); if (of->kn->flags & KERNFS_HAS_SEQ_SHOW) return seq_read(file, user_buf, count, ppos); else return kernfs_file_direct_read(of, user_buf, count, ppos); }
kernfs_of()函数
static struct kernfs_open_file *kernfs_of(struct file *file) { return ((struct seq_file *)file->private_data)->private; }
seq_read()函数
ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { struct seq_file *m = file->private_data; ... err = m->op->show(m, p); ... err = copy_to_user(buf, m->buf, n); ... }
kernfs_seq_show()函数
static int kernfs_seq_show(struct seq_file *sf, void *v) { struct kernfs_open_file *of = sf->private; ... return of->kn->attr.ops->seq_show(sf, v); }
sysfs_kf_seq_show()函数
static int sysfs_kf_seq_show(struct seq_file *sf, void *v) { struct kernfs_open_file *of = sf->private; struct kobject *kobj = of->kn->parent->priv; const struct sysfs_ops *ops = sysfs_file_ops(of->kn); ssize_t count; char *buf; count = seq_get_buf(sf, &buf); if (count < PAGE_SIZE) { seq_commit(sf, -1); return 0; } memset(buf, 0, PAGE_SIZE); /* * Invoke show(). Control may reach here via seq file lseek even * if @ops->show() isn't implemented. */ if (ops->show) { count = ops->show(kobj, of->kn->priv, buf); if (count < 0) return count; } /* * The code works fine with PAGE_SIZE return but it's likely to * indicate truncated result or overflow in normal use cases. */ if (count >= (ssize_t)PAGE_SIZE) { printk("fill_read_buffer: %pS returned bad count ", ops->show); /* Try to struggle along */ count = PAGE_SIZE - 1; } seq_commit(sf, count); return 0; }
sysfs_file_ops()函数
static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn) { struct kobject *kobj = kn->parent->priv; if (kn->flags & KERNFS_LOCKDEP) lockdep_assert_held(kn); return kobj->ktype ? kobj->ktype->sysfs_ops : NULL; }
seq_get_buf()函数
static inline size_t seq_get_buf(struct seq_file *m, char **bufp) { BUG_ON(m->count > m->size); if (m->count < m->size) *bufp = m->buf + m->count; else *bufp = NULL; return m->size - m->count; }
kobj_attr_show()函数
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; }
设备驱动模型实验1-kobject点灯
实验思路
内核模块+led驱动+kobject+kobj_attribute
内核模块
- 动态加载功能
led驱动
- 控制硬件led
kobject
- 在/sys创建目录项
kobj_attribute
- 为kobject对象的属性文件提供独有的读写接口
kset:驱动的骨架
kobject的容器,体现设备驱动的层次关系
kset_create_and_add()函数
lib/kobject.c
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); if (!kset) return NULL; error = kset_register(kset); if (error) { kfree(kset); return NULL; } return kset; }
kset_create()函数
lib/kobject.c
static struct kset *kset_create(const char *name,const struct kset_uevent_ops *uevent_ops,struct kobject *parent_kobj) { struct kset *kset; int retval; kset = kzalloc(sizeof(*kset), GFP_KERNEL); if (!kset) return NULL; retval = kobject_set_name(&kset->kobj, "%s", name); if (retval) { kfree(kset); return NULL; } /*注册消息发送接口*/ kset->uevent_ops = uevent_ops; kset->kobj.parent = parent_kobj; kset->kobj.ktype = &kset_ktype; kset->kobj.kset = NULL; return kset; }
kset_init()函数
lib/kobject.c
void kset_init(struct kset *k) { kobject_init_internal(&k->kobj); INIT_LIST_HEAD(&k->list); spin_lock_init(&k->list_lock); }
kset_register()函数
lib/kobject.c
int kset_register(struct kset *k) { int err; if (!k) return -EINVAL; kset_init(k); err = kobject_add_internal(&k->kobj); if (err) return err; /*发送驱动模型消息到应用层*/ kobject_uevent(&k->kobj, KOBJ_ADD); return 0; }