[转]浅析/sys/class目录的创建流程
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[转]浅析/sys/class目录的创建流程相关的知识,希望对你有一定的参考价值。
浅析/sys/class目录的创建流程
/sys/class目录创建成功之后,其他的driver就可以调用
struct class *class_create(struct module *owner, const char *name);
int class_register(struct class *cls);
成功向class添加自己的目录和attr文件了.
因为sysfs是一个内存文件系统,所以文件的物理存储关系就需要使用sd来维护,因此sysfs_dirent即sd就类似于硬盘中的磁道.
sysfs文件系统是一个排它式的文件系统,
不论被mount多少次都只产生一个sb超级块,
如果尝试再次mount,即尝试再次调用sysfs_get_sb获取另一个sb超级块,那么将执行atomic_inc(old->s_active);增加
已被mount的引用计数,然后如果s已经执行了alloc_super,那么调用destroy_super将其销毁,然后返回这个已被mount了的
super_block超级块old,这样就实现了sysfs文件系统
不论被mount多少次都只产生一个sb超级块的效果
,所以取名为get_sb_single[luther.gliethttp]
int __init classes_init(void)
{
class_kset = kset_create_and_add("class", NULL, NULL);//parent_kobj为NULL,"class"目录将创建到sysfs_root-sysfs的根‘/‘下面.
if (!class_kset)
return -ENOMEM;
//到这里/sys/class目录就已经创建成功了,并且当sysfs被mount到/sys目录之后,/sys/class就可以通过ls可见了[luther.gliethttp]
/* ick, this is ugly, the things we go through to keep from showing up
* in sysfs... */
kset_init(&class_obj_subsys);
kobject_set_name(&class_obj_subsys.kobj, "class_obj");
if (!class_obj_subsys.kobj.parent)
class_obj_subsys.kobj.parent = &class_obj_subsys.kobj;
return 0;
}
struct kset *kset_create_and_add(const char *name,
struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
{
struct kset *kset;
int error;
kset = kset_create(name, uevent_ops, parent_kobj);//创建kset
if (!kset)
return NULL;
error = kset_register(kset);
if (error) {
kfree(kset);
return NULL;
}
return kset;
}
static struct kset *kset_create(const char *name,
struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
{
struct kset *kset;
kset = kzalloc(sizeof(*kset), GFP_KERNEL);//申请内存空间
if (!kset)
return NULL;
kobject_set_name(&kset->kobj, name);//kmalloc内存,然后拷贝name,最后kobj->name = name;
kset->uevent_ops = uevent_ops;//uevent处理函数
kset->kobj.parent = parent_kobj;//
/*
* 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管理的所有属性文件的read和write通用实现函数,show和store
kset->kobj.kset = NULL;//该kset就是父,不再有其他kset来管理本kset
return kset;
}
========================================
int kobject_set_name(struct kobject *kobj, const char *fmt, ...)
{
va_list args;
int retval;
va_start(args, fmt);
retval = kobject_set_name_vargs(kobj, fmt, args);
va_end(args);
return retval;
}
static int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
va_list vargs)
{
va_list aq;
char *name;
va_copy(aq, vargs);
name = kvasprintf(GFP_KERNEL, fmt, vargs);//kmalloc之后赋值,返回到name指针
va_end(aq);
if (!name)
return -ENOMEM;
/* Free the old name, if necessary. */
kfree(kobj->name);//释放原有的name,如果有
/* Now, set the new name */
kobj->name = name;//设置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;//返回拷贝完数据之后的内存空间
}
========================================
//定义属性文件的read和write实现函数,show和store
static struct kobj_type kset_ktype = {
.sysfs_ops = &kobj_sysfs_ops,
.release = kset_release,
};
struct sysfs_ops kobj_sysfs_ops = {
.show = kobj_attr_show,
.store = kobj_attr_store,
};
========================================
int kset_register(struct kset *k)//注册本kset到其父目录下
{
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;
}
void kset_init(struct kset *k)
{
kobject_init_internal(&k->kobj);
INIT_LIST_HEAD(&k->list);
spin_lock_init(&k->list_lock);
}
static int kobject_add_internal(struct kobject *kobj)
{
int error = 0;
struct kobject *parent;
if (!kobj)//必须存在
return -ENOENT;
if (!kobj->name || !kobj->name[0]) {//kobj必须有名字,因为他要作为目录或者文件显示在sysfs中
pr_debug("kobject: (%p): attempted to be registered with empty "
"name!\n", kobj);
WARN_ON(1);
return -EINVAL;
}
parent = kobject_get(kobj->parent);//获取其父kobj结构体,我们的class_kset它的parent为NULL
/* join kset if set, use it as parent if we do not already have one */
if (kobj->kset) {//我们的class_kset它的kset为NULL
if (!parent)
parent = kobject_get(&kobj->kset->kobj);//如果没有为该kobj指定parent,那么kobj所在的kset将作为
//本kobj的父目录
kobj_kset_join(kobj);//将kobj链接到管理kobj的kset上去
kobj->parent = parent;
}
pr_debug("kobject: ‘%s‘ (%p): %s: parent: ‘%s‘, set: ‘%s‘\n",
kobject_name(kobj), kobj, __FUNCTION__,
parent ? kobject_name(parent) : "",
kobj->kset ? kobject_name(&kobj->kset->kobj) : "");
error = create_dir(kobj);//在sysfs中为该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",
__FUNCTION__, kobject_name(kobj));
else
printk(KERN_ERR "%s failed for %s (%d)\n",
__FUNCTION__, kobject_name(kobj), error);
dump_stack();
} else
kobj->state_in_sysfs = 1;
return error;
}
static void kobj_kset_join(struct kobject *kobj)
{
if (!kobj->kset)
return;
kset_get(kobj->kset);
spin_lock(&kobj->kset->list_lock);
list_add_tail(&kobj->entry, &kobj->kset->list);//将该kobj添加到所属kset的链表上
spin_unlock(&kobj->kset->list_lock);
}
static int create_dir(struct kobject *kobj)
{
int error = 0;
if (kobject_name(kobj)) {//返回kobj的name指针
error = sysfs_create_dir(kobj);//在sysfs文件系统上创建目录
if (!error) {
error = populate_dir(kobj);//向创建的目录添加该kobj需要创建的文件
if (error)
sysfs_remove_dir(kobj);
}
}
return error;
}
static inline const char *kobject_name(const struct kobject *kobj)
{
return kobj->name;
}
int sysfs_create_dir(struct kobject * kobj)
{
struct sysfs_dirent *parent_sd, *sd;
int error = 0;
BUG_ON(!kobj);
if (kobj->parent)//如果该kobj有parent,那么在parent下面创建目录
parent_sd = kobj->parent->sd;
else
parent_sd = &sysfs_root;//那么从sysfs的根‘/‘下面创建该kobj对应的目录[luther.gliethttp]
error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd);//ok,&sd现在包含了kobj在sysfs文件系统中的文件描述信息
if (!error)
kobj->sd = sd;//ok,现在该kobj已经在sysfs中,在parent目录下给自己建立了一个可见的目录,sd包含描述该目录的inode信息,因为sysfs是一个内存文件系统,所以文件的物理存储关系就需要使用sd来维护,因此sysfs_dirent即sd就类似于硬盘中的磁道.
return error;
}
static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,
const char *name, struct sysfs_dirent **p_sd)
{
umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;//目录属性标识
struct sysfs_addrm_cxt acxt;
struct sysfs_dirent *sd;
int rc;
/* allocate */
sd = sysfs_new_dirent(name, mode, SYSFS_DIR);//申请sysfs_dirent结构体内存
if (!sd)
return -ENOMEM;
sd->s_dir.kobj = kobj;
/* link in */
sysfs_addrm_start(&acxt, parent_sd);//准备acxt结构体中需要使用到的信息对象
rc = sysfs_add_one(&acxt, sd);
sysfs_addrm_finish(&acxt);//收尾工作,刷新parent目录时间
if (rc == 0)
*p_sd = sd;
else
sysfs_put(sd);
return rc;
}
struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type)
{
char *dup_name = NULL;
struct sysfs_dirent *sd;
if (type & SYSFS_COPY_NAME) {
name = dup_name = kstrdup(name, GFP_KERNEL);
if (!name)
return NULL;
}
sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL);//从sysfs_dir_cachep申请一个slab对象内存空间
if (!sd)
goto err_out1;
if (sysfs_alloc_ino(&sd->s_ino))//从radix树上,顺序申请下一个空闲的唯一整数,作为sysfs文件系统上文件的inode唯一节点索引号[luther.gliethttp]
goto err_out2;
atomic_set(&sd->s_count, 1);
atomic_set(&sd->s_active, 0);
sd->s_name = name;
sd->s_mode = mode;
sd->s_flags = type;
return sd;
err_out2:
kmem_cache_free(sysfs_dir_cachep, sd);
err_out1:
kfree(dup_name);
return NULL;
}
static int sysfs_alloc_ino(ino_t *pino)
{
int ino, rc;
retry:
spin_lock(&sysfs_ino_lock);
rc = ida_get_new_above(&sysfs_ino_ida, 2, &ino);//从radix树上,顺序申请下一个空闲的唯一整数
spin_unlock(&sysfs_ino_lock);
if (rc == -EAGAIN) {
if (ida_pre_get(&sysfs_ino_ida, GFP_KERNEL))
goto retry;
rc = -ENOMEM;
}
*pino = ino;
return rc;
}
//准备acxt结构体中需要使用到的信息对象
void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
struct sysfs_dirent *parent_sd)
{
struct inode *inode;
memset(acxt, 0, sizeof(*acxt));//先清0所有数据
acxt->parent_sd = parent_sd;//填入parent_sd
/* Lookup parent inode. inode initialization and I_NEW
* clearing are protected by sysfs_mutex. By grabbing it and
* looking up with _nowait variant, inode state can be
* determined reliably.
*/
mutex_lock(&sysfs_mutex);
inode = ilookup5_nowait(sysfs_sb, parent_sd->s_ino, sysfs_ilookup_test,
parent_sd);
//从存储着所有文件系统yaffs2,ext3,fat和sysfs等文件系统中被读入内存之后建立的内存inode节点映像的
//hash表inode_hashtable中查找parent_sd->s_ino节点号对应的inode节点指针[luther.gliethttp].
if (inode && !(inode->i_state & I_NEW)) {
//ok,该inode确实已经存在,并且不是正在创建中.
/* parent inode available */
acxt->parent_inode = inode;//填入该parent的inode映像指针.
/* sysfs_mutex is below i_mutex in lock hierarchy.
* First, trylock i_mutex. If fails, unlock
* sysfs_mutex and lock them in order.
*/
if (!mutex_trylock(&inode->i_mutex)) {
mutex_unlock(&sysfs_mutex);
mutex_lock(&inode->i_mutex);
mutex_lock(&sysfs_mutex);
}
} else
iput(inode);//释放NEW的inode索引
}
//从存储着所有文件系统yaffs2,ext3,fat和sysfs等文件系统中被读入内存之后建立的内存inode节点映像的
//hash表inode_hashtable中查找parent_sd->s_ino节点号对应的inode节点指针[luther.gliethttp].
struct inode *ilookup5_nowait(struct super_block *sb, unsigned long hashval,
int (*test)(struct inode *, void *), void *data)
{
struct hlist_head *head = inode_hashtable + hash(sb, hashval);
return ifind(sb, head, test, data, 0);
}
ifind
=>find_inode
=>if (inode->i_sb != sb) continue;//比较该inode是否属于该sb超级块,所以这样就可以区分不同超级块管理的相同节点号[luther.gliethttp]
=>if (!test(inode, data)) continue;//如果在hash短链上的inode属于指定的sb超级块,那么调用test函数进一步定位inode的正确性,对于sysfs文件系统,使用了sysfs_ilookup_test来进一步判断
static int sysfs_ilookup_test(struct inode *inode, void *arg)
{
struct sysfs_dirent *sd = arg;
return inode->i_ino == sd->s_ino;//只需要保证该sd的s_ino和inode->i_ino相同即说明s_ino号对应的节点已经在sysfs文件系统创建了[luther.gliethttp]
}
int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
{
if (sysfs_find_dirent(acxt->parent_sd, sd->s_name)) {//检查欲填入的sd是否已经在parent目录下存在了
printk(KERN_WARNING "sysfs: duplicate filename ‘%s‘ "
"can not be created\n", sd->s_name);
WARN_ON(1);
return -EEXIST;
}
sd->s_parent = sysfs_get(acxt->parent_sd);//为新建的sd指定有效的parent目录结构体
if (sysfs_type(sd) == SYSFS_DIR && acxt->parent_inode)
inc_nlink(acxt->parent_inode);
acxt->cnt++;
sysfs_link_sibling(sd);//sibling们,按inode节点号,从小到大顺序链接在一起
return 0;
}
//因为sysfs是一种内存文件系统,所以所有文件数据信息都在内存中,文件的级联关系是使用单向链表来完成的,
//所以遍历parent目录结构体挂载的所有文件孩子和目录孩子的目录结构逐一比较name即可[luther.gliethttp].
struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
const unsigned char *name)
{
struct sysfs_dirent *sd;
for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling)
if (!strcmp(sd->s_name, name))
return sd;
return NULL;
}
//sibling们,按inode节点号,从小到大顺序链接在一起[luther.gliethttp]
static void sysfs_link_sibling(struct sysfs_dirent *sd)
{
struct sysfs_dirent *parent_sd = sd->s_parent;
struct sysfs_dirent **pos;
BUG_ON(sd->s_sibling);
/* Store directory entries in order by ino. This allows
* readdir to properly restart without having to add a
* cursor into the s_dir.children list.
*/
for (pos = &parent_sd->s_dir.children; *pos; pos = &(*pos)->s_sibling) {
if (sd->s_ino < (*pos)->s_ino)//如果新加入的sd的节点号小于该pos节点号,那么break.
break;
}
sd->s_sibling = *pos;//将自己插入到pos前面
*pos = sd;
}
//在classes_init
//=>kset_create_and_add
//=>kset_create
//=>kset->kobj.ktype = &kset_ktype;
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) {//在classes_init中对应的kset_ktype没有属性文件要创建,所以该执行将直接返回
for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {
error = sysfs_create_file(kobj, attr);
if (error)
break;
}
}
return error;
}
//向sysfs文件系统kobj所在目录添加文件
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
{
BUG_ON(!kobj || !kobj->sd || !attr);
return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR);
}
int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,
int type)
{
umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG;//普通文件属性标识
struct sysfs_addrm_cxt acxt;
struct sysfs_dirent *sd;
int rc;
sd = sysfs_new_dirent(attr->name, mode, type);//同create_dir
if (!sd)
return -ENOMEM;
sd->s_attr.attr = (void *)attr;
sysfs_addrm_start(&acxt, dir_sd);//同create_dir
rc = sysfs_add_one(&acxt, sd);//同create_dir
sysfs_addrm_finish(&acxt);
if (rc)
sysfs_put(sd);
return rc;
}
以上是关于[转]浅析/sys/class目录的创建流程的主要内容,如果未能解决你的问题,请参考以下文章