安卓移动网站开发详解百度推广新手入门
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_RELEASEstruct 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\n", __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!\n");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)...elsekn = 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;elseops = &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()\read()\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);elsereturn 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\n",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;
}