本檔案的函數清單:
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檔案之後:
很幸運的,在網上有找到這一個圖
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIml2Zu00R6NTNyQzN1YTNxMTMfBzLcBTMvwVOwETMwIzLcRnbl1GajFGd0F2LcRXZu5ibkN3YukGavw1LcpDc0RHaiojIsJye.gif)
這一個圖的了解,也貫穿着整份代碼的分析。
首先要說明的是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;
}