解决动态添加SurfaceView,屏幕会闪烁一下的问题
1.把实例化SurfaceView并添加到相关的view 与 更新SurfaceView内容分开。
1)SurfaceView sfv1 = new SurfaceView(this);
RelativeLayout test = findViewById(R.id.test);
test.addView(sfv1);
2)获取内容后,更新sfv1内容,sfv1.draw();这里的draw()方法是响应的用于更新图像/图形的方法。
SourceInsight作为一款代码阅读利器而被很多IT公司接受使用,因为它确实很方便。有这样一种场景:我在修改当前代码文件时,可能需要和其他文件进行比较,这样我就需要打开这个文件的目录,选中文件和进行比较。如果你的目录层次比较浅的话,那还好应付。但万一你的目录层次是N层的话,那就会让人感到厌恶。
这里笔者有一个权宜之计,打开SourceInsight的Option->Custom Commands,添加一条命令,且叫做Explore Current File,在Run选项里面填写explorer %d,再点击Keys为其分配一个快捷键。如此下来就可以实现在SourceInsight里面阅读代码时,按快捷键就可以直接打开当前文件所在的目录。
还有一个笔者认为更好的实现方式:修改Command为“explorer /select,%f”。这条命令的用途是打开这个文件所在的目录并选中该文件,好处就是如果这个目录下有数十个甚至更多文件时,我们可以快速定位文件,不至于选错文件。是不是比上一条更适合编程人员呢?
本文件的函数列表:
char *kobject_get_path(struct kobject *kobj, gfp_t gfp_mask)
获取指定kobject的完整路径名
void kobject_init(struct kobject * kobj)
初始化kobj(引用为1,链表为空,设置kset宿主)
int kobject_add(struct kobject * kobj)
添加一个kobj
int kobject_register(struct kobject * kobj)
kobject注册函数
int kobject_set_name(struct kobject * kobj, const char * fmt, ...)
按格式化设置kobj->k_name(obj的名字)
int kobject_rename(struct kobject * kobj, const char *new_name)
kobject重命名
int kobject_move(struct kobject *kobj, struct kobject *new_parent)
重新给kobj指定父kobj
void kobject_del(struct kobject * kobj)
从kset->list中删除kobj
void kobject_unregister(struct kobject * kobj)
卸载kobj,主要调用了kobject_del
void kobject_cleanup(struct kobject * kobj)
static void kobject_release(struct kref *kref)
释放kobj,核心操作是调用了kobj关联的ktype结构体中的release函数,然后将kset和父kobj引用记数减1
当kobject_put调用将指定的kobj的kref计数减到0的时候,就会自动调用kobject_release函数了。
static void dir_release(struct kobject *kobj)
默认的关联在ktype中的release函数,作用是释放了kobj占用的内存
struct kobject *kobject_kset_add_dir(struct kset *kset, struct kobject *parent, const char *name)
struct kobject *kobject_add_dir(struct kobject *parent, const char *name)
申请一个obj对象,关联上kset,parent等参数,然后调用kobject_register函数将刚申请的kobj注册,最后返回这个刚申请的kobj。这里通过对register函数的实际调用,更进一步的分析了register函数实现的过程细节
而kobject_addr_dir函数则是直接将参数透传给kobject_kset_add_dir,只是kset设置为NULL。
int kset_register(struct kset * k)
注册一个kset,实际上,这个函数的实现和kobject_register很相似,所以最后的区别,可能就需要在kobject_uevent中找了
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
写在在分析完kobject.c文件之后:
很幸运的,在网上有找到这一个图
这一个图的理解,也贯穿着整份代码的分析。
首先要说明的是obj的层次,kset是kobj的集合,或者说宿主。其实在linux很多代码都是这样的分层结构,只是名字叫得不同而已,比如IIC的适配器就是client的宿主,或者说集合。然后kset之下的每一个kobj都挂载在kset->list链表之上,这又和I2C驱动中,每一个client都挂载在adp->clients链表之下如出一辙。所以kobj->kset理所当然的就指向了kset。
1、而一个不同之处在于,kset本身也包含了一个kobj,这个obj是kset->list下所有kobj的父设备。
2、而kobj的release操作则是放在另外一个结构体ktype中,在获取这个结构体时,是优先返回kset指向的ktype,如果kset没有指定ktype,才会返回kobj自己指的ktype。
3、kobj很大的作用是用name来创建文件夹,然后根据kobj->ktype->attr的属性来创建文件。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
先看一下kobject.h的头文件中的定义:
一个枚举结构,表示动作
enum kobject_action {
KOBJ_ADD,
KOBJ_REMOVE,
KOBJ_CHANGE,
KOBJ_MOVE,
KOBJ_ONLINE,
KOBJ_OFFLINE,
KOBJ_MAX
};
//这个结构体很多资料都有说明,这里先直接COPY别人的说明
struct kobject {
const char * k_name; //指向设备名称的指针
struct kref kref; //对象引用计数,只有一个成员refcount
struct list_head entry; //挂接到kset->list中的单元(下图蓝线)
struct kobject * parent; //指向父对象的指针
struct kset * kset; //所属kset的指针
struct kobj_type * ktype; //指向其对象类型描述符的指针
struct sysfs_dirent * sd; //文件路径
};
Struct kobj_type {
void (*release)(struct kobject *); //释放kobject占用的资源
struct sysfs_ops * sysfs_ops; //指向sysfs操作表
struct attribute ** default_attrs; // sysfs文件系统缺省属性列表
};
attribute, 属性。它以文件的形式输出到sysfs的目录当中。在kobject对应的目录下面。文件
名就是name。文件读写的方法对应于kobj_type中的sysfs_ops。
kset最重要的是建立上层(sub-system)和下层的(kobject)的关联性,换句话说,kset是kobject的集合。
struct kset {
struct kobj_type *ktype; //指向该kset对象类型描述符的指针
struct list_head list; //连接该kset中所有kobject的链表头(蓝箭头)
spinlock_t list_lock; //list上的锁
struct kobject kobj; //嵌入的kobject
struct kset_uevent_ops *uevent_ops; //指向热插拔操作表的指针
};
struct kset_uevent_ops {
int (*filter)(struct kset *kset, struct kobject *kobj);
const char *(*name)(struct kset *kset, struct kobject *kobj);
int (*uevent)(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env);
};
struct kobj_uevent_env {
char *envp[UEVENT_NUM_ENVP];
int envp_idx;
char buf[UEVENT_BUFFER_SIZE];
int buflen;
};
//子系统属性,用在最后一个函数中
struct subsys_attribute {
struct attribute attr; //属性
ssize_t (*show)(struct kset *, char *); //显示函数
ssize_t (*store)(struct kset *, const char *, size_t); //存储函数
};
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
这一组两个函数是创建kobj的文件夹以及属性文件
先看头文件里的一个函数,因为下面要用到
这个函数的作用是返回kobj_type指针,优先返回kobject->kset->ktype的,如果他没有就返回kobject->ktype的。之前有说,kset是kobject的集合,所以这个返回就意味着集合属性优先。
static inline struct kobj_type * get_ktype(struct kobject * k)
{
if (k->kset && k->kset->ktype)
return k->kset->ktype;
else
return k->ktype;
}
//根据kobj的ktype->attr属性,在kobj文件夹下创建每一个文件
static int populate_dir(struct kobject *kobj)
{
struct kobj_type *t = get_ktype(kobj); //ktype(kobject释放函数,ops,属性attr)
struct attribute * attr;
int error = 0;
int i;
//ktype存在 且 默认属性存在
if (t && t->default_attrs) {
//提取每一个属性创建一个文件,放在kobj文件夹下
for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {
if ((error = sysfs_create_file(kobj, attr)))
break;
}
}
return error;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//根据kobj->name和kobj->sd创建一个文件夹,然后调用populate_dir在该文件夹下创建具体的文件
static int create_dir(struct kobject *kobj)
{
int error = 0;
if (kobject_name(kobj)) { //获取kobj-> k_name
error = sysfs_create_dir(kobj); //创建一个目录
if (!error) {
if ((error = populate_dir(kobj))) //根据kobj的属性创建文件
sysfs_remove_dir(kobj); //如果失败则删除目录
}
}
return error;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//返回该链表头所在的kobject结构体
static inline struct kobject *to_kobj(struct list_head *entry)
{
return container_of(entry, struct kobject, entry);
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//获取kobject文件名的整体长度(从根kobject开始累加)
static int get_kobj_path_length(struct kobject *kobj)
{
int length = 1;
struct kobject *parent = kobj;
do {
if (kobject_name(parent) == NULL) //本kobject没有名字,出错返回0
return 0;
length += strlen(kobject_name(parent)) + 1; //计算kobject名字的长度
parent = parent->parent; //获取父kobject
} while (parent);
return length;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//整理path为 “/xxx/xxxx…”结构
//由于kobj是只能从最底层向上找,所以这个函数的路径复制动作是从尾向前复制:
//strncpy (path + length, kobject_name(parent), cur);
//他的这个特性很容易联想到,调用这个函数之前肯定需要先调用上面的get_kobj_path_length函数先计算好路径长度。
static void fill_kobj_path(
struct kobject *kobj, //最底层的kobject
char *path, //路径字符串
int length) //路径长度
{
struct kobject *parent;
--length;
//逆向循环至根节点
for (parent = kobj; parent; parent = parent->parent) {
int cur = strlen(kobject_name(parent)); //获取name的长度
length -= cur;
strncpy (path + length, kobject_name(parent), cur);
*(path + --length) = '/';
}
pr_debug("%s: path = '%s'\n",__FUNCTION__,path);
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//获取指定kobject的路径名(实际就是kobj->name连在一起)
char *kobject_get_path(
struct kobject *kobj,
gfp_t gfp_mask)
{
char *path;
int len;
len = get_kobj_path_length(kobj); //获取路径长度
if (len == 0)
return NULL; //为0则出错
path = kzalloc(len, gfp_mask); //申请一块内存来保存路径
if (!path)
return NULL;
fill_kobj_path(kobj, path, len); //获取完整的路径
return path; //返回路径
}
EXPORT_SYMBOL_GPL(kobject_get_path);
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//初始化kobject对象(引用计数为1+1,entry链表为空,设置kobj的kset宿主)
void kobject_init(struct kobject * kobj)
{
if (!kobj)
return;
kref_init(&kobj->kref); //kref->refcount=1(只有一个成员引用kobj)
INIT_LIST_HEAD(&kobj->entry); //kset链表为空
kobj->kset = kset_get(kobj->kset); //kobj->kset指向kobj的宿主kset结构体,同时kobj->kref->refcount的引用计数加1(因为被宿主引用了),kset_get的具体实现方式见下面三个函数
}
//如果指针为空,则返回NULL,否则执行一个操作
static inline struct kset *kset_get(struct kset * k)
{
return k ? to_kset(kobject_get(&k->kobj)) : NULL;
}
//如果指针不为空,则调用kref_get使kobj的引用计数加1,然后把kobj原样返回
struct kobject *kobject_get(struct kobject * kobj)
{
if (kobj)
kref_get(&kobj->kref);
return kobj;
}
也就是说,to_kset(kobject_get(&k->kobj))等效于to_kset(&k->obj);
//返回kobj的宿主kset结构体
static inline struct kset * to_kset(struct kobject * kobj)
{
return kobj ? container_of(kobj, struct kset, kobj) : NULL;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//断开kobj->entry的链接
static void unlink(struct kobject * kobj)
{
//如果设置了宿主kset,则执行
if (kobj->kset) {
spin_lock(&kobj->kset->list_lock);
list_del_init(&kobj->entry); //断开链表的连接
spin_unlock(&kobj->kset->list_lock);
}
kobject_put(kobj);
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//添加一个kobject进他的宿主kset链表,主要操作有:
1、如果kobj->name没有指定,则给一个默认的NAME
2、设置kobj的父节点
3、如果kobj->kset有指定,则将kobj->entry加如到kset->list中
4、为kobj创建文件夹
int kobject_add(struct kobject *kobj)
{
int error = 0;
struct kobject *parent;
//引用加1,如果返回错误则说明kobj本身就为NULL
if (!(kobj = kobject_get(kobj)))
return -ENOENT;
//如果kobj->k_name指向地址为空,则设置name为“NO_NAME”
if (!kobj->k_name)
kobject_set_name(kobj, "NO_NAME");
//name为空,则错误
if (!*kobj->k_name) {
pr_debug("kobject attempted to be registered with no name!\n");
WARN_ON(1);
kobject_put(kobj); //释放引用计数
return -EINVAL;
}
parent = kobject_get(kobj->parent); //获取kobj的父节点
//如果kobj->kset不为空(宿主有指定)
//如果kobj->kset没有指定,那么这个kobject就成了野obj了
if (kobj->kset) {
spin_lock(&kobj->kset->list_lock);
//如果父节点为空,则父节点设置为宿主kset中的kobj
//如果这个地方迷糊了,那么就返回最开始的那个图看一眼
if (!parent)
parent = kobject_get(&kobj->kset->kobj);
//将entry挂载到kset->list,同样的,如果迷糊了,就看最开始的图的蓝箭头
list_add_tail(&kobj->entry,&kobj->kset->list);
spin_unlock(&kobj->kset->list_lock);
kobj->parent = parent; //设置kobj的父节点
}
//这个函数在一开始就有分析:创建文件夹,并根据kobj的属性创建文件
error = create_dir(kobj);
if (error) { //创建失败
unlink(kobj); //断开kobj->entry的链接
kobject_put(parent); //父节点的应用计数减1
dump_stack(); //记录下函数的调用过程,便于以后查错用
}
return error;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//kobject的注册
//从这个函数可以看出,实际上kobject_add实际也是初始化设置的一个步骤
int kobject_register(struct kobject *kobj)
{
int error = -EINVAL;
if (kobj) { //如果kobj指针不为空
kobject_init(kobj); //初始化一个kobj
error = kobject_add(kobj); //将kobj添加进kobj->set中
//如果没有出错,就调用uevent函数,这个函数在另外一个文件中,很大,先放放
if (!error)
kobject_uevent(kobj, KOBJ_ADD);
}
return error;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//给指定的kobj设置name,用了格式化设置参数
int kobject_set_name(
struct kobject * kobj, //指定kobj
const char *fmt, //格式化字符串
...)
{
int error = 0;
int limit;
int need;
va_list args;
char *name;
//申请1K内存保存文件名,第一次申请内存是申请了一个最大长度来获取格式化字符传的长度
name = kmalloc(1024, GFP_KERNEL);
if (!name) { //申请失败
error = -ENOMEM;
goto done;
}
//从格式化字符串中取出格式化的数据
//vsnprintf函数的四个参数分别为:首地址,长度,带格式化的字符串,格式说明
//从这里可以看出,fmt本身就是函数传递进来的参数,说明了格式化的字符串。然后va_start的作用是将fmt的格式化说明部分关联到args中,从print函数的格式,我们可以推算出args的格式为“a,b,c…”等,所以va_arg函数就是按指定格式提取出”,”分隔出的变量
va_start(args, fmt);
need = vsnprintf(name, 1024, fmt, args); //获取字符串长度
va_end(args);
kfree(name); //释放内存
//根据长度重新申请内存
limit = need + 1;
name = kmalloc(limit, GFP_KERNEL);
if (!name) {
error = -ENOMEM;
goto done;
}
va_start(args, fmt);
need = vsnprintf(name, limit, fmt, args);
va_end(args);
//长度比指定的长度长,出错。实际上这种情况不太可能发生。
if (need >= limit) {
kfree(name);
error = -EFAULT;
goto done;
}
//释放kobj原来的name内存,并关联上新的内存
kfree(kobj->k_name);
kobj->k_name = name;
done:
return error;
}
EXPORT_SYMBOL(kobject_set_name);
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//kobject重命名
int kobject_rename(
struct kobject *kobj,
const char *new_name)
{
int error = 0;
const char *devpath = NULL;
char *devpath_string = NULL;
char *envp[2];
//kobj的引用计数加1
kobj = kobject_get(kobj);
if (!kobj)
return -EINVAL;
if (!kobj->parent)
return -EINVAL;
//宿主kset已经指定
if (kobj->kset) {
struct kobject *temp_kobj;
//虽然这个函数我还没有分析,不过从函数名和参数中,我们不难猜测,这个函数是根本kobj的名字,在kset链表中找寻是否有同名的kobject
//一个疑问,为什么在给kobject命名的时候没有做这个检测呢?
temp_kobj = kset_find_obj(kobj->kset, new_name);
if (temp_kobj) { //找到同名的kobject,减少引用计数,出错返回
printk(KERN_WARNING "kobject '%s' cannot be renamed "
"to '%s' as '%s' is already in existence.\n",
kobject_name(kobj), new_name, new_name);
kobject_put(temp_kobj);
return -EINVAL;
}
}
//获取kobj的路径
devpath = kobject_get_path(kobj, GFP_KERNEL);
if (!devpath) {
error = -ENOMEM;
goto out;
}
//申请一块内存来保存旧的路径(目前还是kobj的当前路径)
devpath_string = kmalloc(strlen(devpath) + 15, GFP_KERNEL);
if (!devpath_string) {
error = -ENOMEM;
goto out;
}
//保存旧的路径
sprintf(devpath_string, "DEVPATH_OLD=%s", devpath);
envp[0] = devpath_string;
envp[1] = NULL;
//执行文件夹的重命名函数
error = sysfs_rename_dir(kobj, new_name);
if (!error) //成功,重新uevent
kobject_uevent_env(kobj, KOBJ_MOVE, envp);
out:
kfree(devpath_string); //释放内存
kfree(devpath);
kobject_put(kobj); //释放应用计数
return error;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
int kobject_move(
struct kobject *kobj,
struct kobject *new_parent)
{
int error;
struct kobject *old_parent;
const char *devpath = NULL;
char *devpath_string = NULL;
char *envp[2];
kobj = kobject_get(kobj); //obj引用
if (!kobj)
return -EINVAL;
new_parent = kobject_get(new_parent); //引用父obj
if (!new_parent) { //没有父obj
if (kobj->kset) //有属于的kset,则父obj为kset结构体中的kobj
new_parent = kobject_get(&kobj->kset->kobj);
}
//获取旧的路径,这部分与rename的处理相同
devpath = kobject_get_path(kobj, GFP_KERNEL);
if (!devpath) {
error = -ENOMEM;
goto out;
}
devpath_string = kmalloc(strlen(devpath) + 15, GFP_KERNEL);
if (!devpath_string) {
error = -ENOMEM;
goto out;
}
sprintf(devpath_string, "DEVPATH_OLD=%s", devpath);
envp[0] = devpath_string;
envp[1] = NULL;
//移动dir到新的obj目录中
error = sysfs_move_dir(kobj, new_parent);
if (error)
goto out;
//重新设置kobj->parent父obj
old_parent = kobj->parent;
kobj->parent = new_parent;
new_parent = NULL;
//释放引用
kobject_put(old_parent);
kobject_uevent_env(kobj, KOBJ_MOVE, envp);
out:
kobject_put(new_parent);
kobject_put(kobj);
kfree(devpath_string);
kfree(devpath);
return error;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//删除指定的kobj,核心操作就两步:
1、 删除目录
2、断开kobj的entry在kset->list中的链接
void kobject_del(struct kobject * kobj)
{
if (!kobj)
return;
sysfs_remove_dir(kobj);
unlink(kobj);
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
void kobject_unregister(struct kobject * kobj)
{
if (!kobj) //空指针
return;
pr_debug("kobject %s: unregistering\n",kobject_name(kobj));
kobject_uevent(kobj, KOBJ_REMOVE);
kobject_del(kobj); //删除kobj
kobject_put(kobj); //减小本kobj的引用计数
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//这个函数主要是给后面的release用的
//这个函数的工作主要如下:
1、 调用obj关联的ktype中的release函数,并释放name占用的内存
2、 指定kobj的宿主kset和父kobj的引用计数减1
void kobject_cleanup(struct kobject *kobj)
{
struct kobj_type *t = get_ktype(kobj); //获取obj中的ktype成员
struct kset *s = kobj->kset; //获取obj的宿主kset
struct kobject *parent = kobj->parent; //获取obj的父kobj
const char *name = kobj->k_name; //获取obj的name
//type成员存在,且release指定,则调用release函数
//要注意一点的是,get_ktype中,如果kobj所属的kset->ktype存在的话,是返回kset->ktype的
if (t && t->release) {
t->release(kobj);
kfree(name); //释放name所占用的内存
}
if (s)
kset_put(s); //宿主的引用记数减1
kobject_put(parent); //父obj的引用记数减1
}
//这里传递进来的参数是引用记数,函数先是container获取该引用记数所在的kobj,然后调用上面的cleanup函数
static void kobject_release(struct kref *kref)
{
kobject_cleanup(container_of(kref, struct kobject, kref));
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//减少obj的引用计数,前面用到很多,只是一直没注意当kref减到0的时候,是会自动调用kobject_release函数的
void kobject_put(struct kobject * kobj)
{
if (kobj)
kref_put(&kobj->kref, kobject_release);
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
static void dir_release(struct kobject *kobj)
{
kfree(kobj); //释放obj结构体占用的内存
}
static struct kobj_type dir_ktype = {
.release = dir_release, //关联了一个release函数
.sysfs_ops = NULL,
.default_attrs = NULL,
};
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//新建一个kobj对象,设置好kset,parent,name参数,挂上默认的ktype,然后将其注册进kset
struct kobject *kobject_kset_add_dir(
struct kset *kset,
struct kobject *parent, //不可为NULL
const char *name)
{
struct kobject *k;
int ret;
if (!parent)
return NULL;
//申请一块obj内存
k = kzalloc(sizeof(*k), GFP_KERNEL);
if (!k)
return NULL;
//指定obj的宿主,父obj,ktype,名字
k->kset = kset;
k->parent = parent;
k->ktype = &dir_ktype;
kobject_set_name(k, name);
//将obj注册
ret = kobject_register(k);
if (ret < 0) { //注册失败则删除本obj
printk(KERN_WARNING "%s: kobject_register error: %d\n",
__func__, ret);
kobject_del(k);
return NULL;
}
return k;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//添加一个obj目录,实际就是调用了上面的kobject_kset_add_dir函数
struct kobject *kobject_add_dir(
struct kobject *parent,
const char *name)
{
return kobject_kset_add_dir(NULL, parent, name);
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
在kobject_kset_add_dir中有一个对kobject_register函数调用的实例,那么我们就根据这个实际的应用在分析一次obj的注册函数:
调用之前的设置如下:(为了和后面的参数对应上,所以这里把k改成了kobj)
kobj->kset = kset;
kobj->parent = parent;
kobj->ktype = &dir_ktype;
kobject_set_name(kobj, name);
然后注册:
kobject_init(kobj);
kobject_add(kobj);
kobject_uevent(kobj, KOBJ_ADD);
最后一个函数因为我们还没有分析env,所以暂时忽略。
继续看进去——init:
kobj->kset = kset_get(kobj->kset);
à to_kset(kobject_get(&kobj->kset ->kobj)) //这里将kset中的obj引用了一次
à to_kset(&kobj->kset ->kobj)
à container_of(container_of(&kobj->kset ->kobj),struct kset,kobj)
//这回看得清楚一点了,kobj->kset->kobj,也就是kobj宿主kset中包含的那个kobj(迷糊了就回到最开始的那个图看看,kset包含的那个kobj),然后通过container也就获取到了kset指针,所以这个地方绕了一个大圈,实际的作用就是(kobject_get(&kobj->kset ->kobj)这里,kset的kobj引用加1。而to_kset实际没起什么作用,说白了就是1=1。
kobject_init(kobj)函数的功能如下:
kref_init(&kobj->kref); //初始引用计数为1
INIT_LIST_HEAD(&kobj->entry); //obj没有挂在kset->list上
kobj->kset = kset_get(kobj->kset); //kobj->kset->kobj的引用加1
然后就是add(以下代码去掉了很多错误检测的代码):
kobject_get(kobj); //kobj的引用加1
if (!kobj->k_name) kobject_set_name(kobj, "NO_NAME"); //给一个默认的名字
parent = kobject_get(kobj->parent);
if (kobj->kset) { //如果宿主存在
if (!parent) parent = kobject_get(&kobj->kset->kobj);
list_add_tail(&kobj->entry,&kobj->kset->list); //将kobj添加进kset->list中
kobj->parent = parent; //设置父obj
}
create_dir(kobj); //建立目录
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//初始化kset
void kset_init(struct kset * k)
{
kobject_init(&k->kobj); //初始化kset中的kobj
INIT_LIST_HEAD(&k->list); //将kset的list清空
spin_lock_init(&k->list_lock);
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//kset的add,实际也就是将kset包含的那个kobj添加进去
int kset_add(struct kset * k)
{
return kobject_add(&k->kobj);
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//注册一个kset
int kset_register(struct kset * k)
{
int err;
if (!k) //空指针,出错
return -EINVAL;
kset_init(k); //先将kset初始化(函数中也初始化了kset->kobj)
err = kset_add(k); //添加kset->obj
if (err)
return err;
kobject_uevent(&k->kobj, KOBJ_ADD);
return 0;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//卸载函数
void kset_unregister(struct kset * k)
{
if (!k)
return;
kobject_unregister(&k->kobj);
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//在kset链表中找寻名字为name的obj
struct kobject *kset_find_obj(
struct kset * kset,
const char * name)
{
//这个函数的实现很简单,从kset->list链表中取出每个kobj,然后strcmp比较是否和指定的name相等,相等就返回该obj
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//subsystem子系统注册,实际就是直接调用kset_reg
int subsystem_register(struct kset *s)
{
return kset_register(s);
}
void subsystem_unregister(struct kset *s)
{
kset_unregister(s);
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
int subsys_create_file(
struct kset *s,
struct subsys_attribute *a)
{
int error = 0;
if (!s || !a) //有空指针,错误
return -EINVAL;
if (kset_get(s)) { //kset有包含的obj
error = sysfs_create_file(&s->kobj, &a->attr); //根据属性创建文件
kset_put(s); //释放引用
}
return error;
}