Linux設備管理(一)_kobject_kset_kobj_type

来源:http://www.cnblogs.com/xiaojiang1025/archive/2016/12/18/6193959.html
-Advertisement-
Play Games

Linux內核大量使用面向對象的設計思想,通過追蹤源碼,我們甚至可以使用面向對象語言常用的UML類圖來分析Linux設備管理的"類"之間的關係。這裡以4.8.5內核為例從kobject,kset,kobj_type的分析入手,進而一探內核對於設備的管理方式 container_of巨集 這個巨集幾乎是l ...


Linux內核大量使用面向對象的設計思想,通過追蹤源碼,我們甚至可以使用面向對象語言常用的UML類圖來分析Linux設備管理的"類"之間的關係。這裡以4.8.5內核為例從kobject,kset,kobj_type的分析入手,進而一探內核對於設備的管理方式

container_of巨集

這個巨集幾乎是linux數據結構的基礎,Linux中的鏈表與傳統的鏈表不同,其鏈表的節點本身並不包含任何數據,任何想要插入到鏈表的數據只需要包含一個事先寫好的節點

//include/linux/types.h
184 struct list_head {                                                         
185     struct list_head *next, *prev;
186 };

但是,使用這種通用的鏈表的第一個問題就是如何根據一個list_head成員來找到相應的數據,Linux社區的大神們早就找到了相應的方法,就是利用下麵這個container_of巨集,只需要輸入成員指針ptr包含該成員的結構體類型type,以及該成員在結構體中名字name就可以返回包含ptr的type類型的結構首地址,這個巨集充分利用了C語言直接操作記憶體的特性。需要註意的是,如果單純為了得到地址只需要ptr-&((type* 0)->member),內核的寫法其實還利用了編譯器的類型檢查機製做了一份校驗工作,即如果傳入的ptr類型和type->member的類型不匹配,會報錯,

//include/linux/kernel.h

14 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 

830 #define container_of(ptr, type, member) ({          \                      
831     const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
832     (type *)( (char *)__mptr - offsetof(type,member) );})

kobject結構

Linux內核中有大量的驅動,而這些驅動往往具有類似的結構,根據面向對象的思想,我們就可以將這些共同的部分提取為父類,這個父類就是kobject,也就是驅動編程中使用的.ko文件的由來,下麵這張圖是我根據內核源碼的kobject繪製的簡單的UML圖,從中可以看出,kobject包含了大量的設備必須的信息,而三大類設備驅動都需要包含這個kobject結構,也就是"繼承"自kobject。一個kobject對象就對應sys目錄中的一個設備。
內核源碼中的kobject結構定義如下

 //include/linux/kobject.h
 63 struct kobject {  
 64     const char          *name;
 65     struct list_head    entry;
 66     struct kobject      *parent;
 67     struct kset         *kset;
 68     struct kobj_type    *ktype;
 69     struct kernfs_node  *sd; /* sysfs directory entry */
 70     struct kref         kref;
 71 #ifdef CONFIG_DEBUG_KOBJECT_RELEASE
 72     struct delayed_work release;
 73 #endif
 74     unsigned int state_initialized:1;
 75     unsigned int state_in_sysfs:1;
 76     unsigned int state_add_uevent_sent:1;
 77     unsigned int state_remove_uevent_sent:1;
 78     unsigned int uevent_suppress:1;
 79 };

這個結構中,name(64)表示kobject對象的名字,對應sysfs下的一個目錄。entry(65)是kobject中插入的head_list結構,parent(66)是指向當前kobject父對象的指針,體現在sys結構中就是包含當前kobject對象的目錄對象,kset(67)表示當前kobject對象所屬的集合,ktype(68)表示當前kobject的類型。sd(69)用於表示VFS文件系統的目錄項,是設備與文件之間的橋梁。kref(70)是對kobject的引用計數,當引用計數為0是,就回調之前註冊的release方法釋放該對象。state_initialized:1(74)初始化標誌位,在對象初始化時被置位,表示對象是否已經被初始化。state_in_sysfs:1(75)表示kobject對象在sysfs中的狀態,在對應目錄中被創建則置1,否則為0。state_add_uevent_sent:1(76)是添加設備的uevent事件是否發送標誌,添加設備時會向用戶空間發送uevent事件,請求新增設備。state_remove_uevent_sent:1(76)是刪除設備的uevent事件是否發送標誌,刪除設備時會向用戶空間發送uevent事件,請求卸載設備

kobject操作

4.8.5的內核在lib/koject.c等源碼中定義了一系列對kobject操作的函數,這裡只列出最簡單的幾個

初始化kobject

 187 static void kobject_init_internal(struct kobject *kobj)
 188 {       
 189         if (!kobj)
 190                 return;
 191         kref_init(&kobj->kref);
 192         INIT_LIST_HEAD(&kobj->entry);
 193         kobj->state_in_sysfs = 0;
 194         kobj->state_add_uevent_sent = 0; 
 195         kobj->state_remove_uevent_sent = 0;
 196         kobj->state_initialized = 1;
 197 }  
 
 325 void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
 326 {
 327         char *err_str;
             ...
 344         kobject_init_internal(kobj);                           
 345         kobj->ktype = ktype;
 346         return;
             ...
 351 }

註冊kobject

//添加kobject到內核
 200 static int kobject_add_internal(struct kobject *kobj)
 201 {
 202         int error = 0;
 203         struct kobject *parent;
             ...
 214         parent = kobject_get(kobj->parent);
 215 
 216         /* join kset if set, use it as parent if we do not already have one */
 217         if (kobj->kset) {
 218                 if (!parent)
 219                         parent = kobject_get(&kobj->kset->kobj);
 220                 kobj_kset_join(kobj);
 221                 kobj->parent = parent;
 222         }
 223 
 224         pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
 225                  kobject_name(kobj), kobj, __func__,
 226                  parent ? kobject_name(parent) : "<NULL>",
 227                  kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
 228 
 229         error = create_dir(kobj);
             ...
 246         kobj->state_in_sysfs = 1;
 247 
 248         return error;
 249 }

 354 static __printf(3, 0) int kobject_add_varg(struct kobject *kobj,  
 355                                            struct kobject *parent,
 356                                            const char *fmt, va_list vargs)
 357 {
 358         int retval;
             ...
 365         kobj->parent = parent;
 366         return kobject_add_internal(kobj);
 367 }


 394 int kobject_add(struct kobject *kobj, struct kobject *parent,
 395                 const char *fmt, ...)
 396 {
 397         va_list args;
 398         int retval;
             ...
 410         va_start(args, fmt);
 411         retval = kobject_add_varg(kobj, parent, fmt, args);
 412         va_end(args);
 413 
 414         return retval;
 415 }

初始化並註冊kobject

 429 int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,    
 430                          struct kobject *parent, const char *fmt, ...)
 431 {                            
 432         va_list args;        
 433         int retval;
 434                         
 435         kobject_init(kobj, ktype);
 436                              
 437         va_start(args, fmt);
 438         retval = kobject_add_varg(kobj, parent, fmt, args);
 439         va_end(args);
 440         
 441         return retval;
 442 }

註銷kobject

 569 void kobject_del(struct kobject *kobj)
 570 {                        
 571         struct kernfs_node *sd;
 572         
 573         if (!kobj)
 574                 return; 
 575         
 576         sd = kobj->sd;       
 577         sysfs_remove_dir(kobj);
 578         sysfs_put(sd);
 579 
 580         kobj->state_in_sysfs = 0;
 581         kobj_kset_leave(kobj);
 582         kobject_put(kobj->parent);
 583         kobj->parent = NULL;
 584 }

計數減一

//將kobject對象的引用計數加1,同時返回該對象指針。
//include/linux/kref.h
 40 static inline void kref_get(struct kref *kref) 
 41 {
 42         /* If refcount was 0 before incrementing then we have a race
 43          * condition when this kref is freeing by some other thread right now.
 44          * In this case one should use kref_get_unless_zero()
 45          */                     
 46         WARN_ON_ONCE(atomic_inc_return(&kref->refcount) < 2);
 47 }
 
//lib/kobject.c
 591 struct kobject *kobject_get(struct kobject *kobj)     
 592 {
 593         if (kobj) {
             ...
 598                 kref_get(&kobj->kref);
 599         }
 600         return kobj;
 601 }
//將kobject對象的引用計數加1,如果減為零就釋放
//include/linux/kref.h
 67 static inline int kref_sub(struct kref *kref, unsigned int count, 
 68              void (*release)(struct kref *kref))
 69 {
 70         WARN_ON(release == NULL);
 71 
 72         if (atomic_sub_and_test((int) count, &kref->refcount)) {
 73                 release(kref);
 74                 return 1;
 75         }
 76         return 0;
 77 }

 96 static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref))    
 97 {
 98         return kref_sub(kref, 1, release);
 99 }

//lib/kobject.c
 684 void kobject_put(struct kobject *kobj) 
 685 {
 686         if (kobj) {
             ...
 691                 kref_put(&kobj->kref, kobject_release);
 692         }
 693 }

kset結構

kset表示一組kobject的集合,這些kobject可以是不同或相同的類型(ktype)。sysfs中的設備組織結構很大程度上都是根據kset進行組織的,比如在平臺設備模型中,當我們註冊一個設備或驅動到平臺匯流排,其實是將對應的kobject掛接到platform匯流排的kset上,每種匯流排都是維護兩條鏈表(兩個kset),一條用於鏈接掛接在上面的驅動(驅動kset),一條用於鏈接掛接在上面的設備(設備kset)。

//include/linux/kobject.h
168 struct kset {
169     struct list_head list;
170     spinlock_t list_lock;
171     struct kobject kobj;
172     const struct kset_uevent_ops*uevent_ops;  
173 };  

list_head(169)還是那個用來掛在鏈表上的結構。kobj(171)是歸屬於該kset的所有的kobject的共有parent,這個parent就是體現內核設備組織結構的關鍵

kobj_type結構

//include/linux/kobject.h
116 struct kobj_type {
117     void (*release)(struct kobject *kobj);
118     const struct sysfs_ops *sysfs_ops;
119     struct attribute **default_attrs; 
120     const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
121     const void *(*namespace)(struct kobject *kobj);
122 };
//include/linux/sysfs.h
 29 struct attribute {
 30     const char      *name;
 31     umode_t         mode;
 32 #ifdef CONFIG_DEBUG_LOCK_ALLOC                                     
 33     bool            ignore_lockdep:1;                              
 34     struct lock_class_key   *key;                                  
 35     struct lock_class_key   skey;                                  
 36 #endif
 37 };
 
209 struct sysfs_ops {                                                         
210     ssize_t (*show)(struct kobject *, struct attribute *, char *);
211     ssize_t (*store)(struct kobject *, struct attribute *, const char *, si
212 };

這個結構主要是表徵kobject的類型,其中,release(117)是一個釋放kobject對象的介面,有點像面向對象中的析構。sysfs_ops(118)是操作kobject的方法集。由此可見,對同一類型的kobject操作會回調同一個kobj_type的方法

//include/linux/kobject.h
197 static inline struct kobj_type *get_ktype(struct kobject *kobj)            
198 {
199     return kobj->ktype;
200 }

從這個函數中可以看出,4.8.5提取kobject的ktype的時候直接提取kobject的,我還測試過3.14版本的,也是這種寫法,不過網上還有下麵的這種get_ktype的實現,還沒找到具體是哪個版本,顯然,這個版本中kset中的ktype這個類型優先於 kobject 自身中的 ktype 。因此在典型的應用中, 在 struct kobject 中的 ktype 成員被設為 NULL, 而 kset 中的ktype是實際被使用的。

static inline struct kobj_type * get_ktype(struct kobject * k)
        {
            if (k->kset && k->kset->ktype)
                return k->kset->ktype;
            else 
                return k->ktype;
        }

結構框圖

kobject,kset是Linux設備管理中的基本結構體,但在實際操作中我們幾乎不會實際操作這些結構,因為他們本身並不具有針對某一個具體設備或驅動的信息,在Linux內核中,這兩個結構都是被包含具體的設備結構中,比如cdev,gendisk等,從面向對象的角度考慮,就是每一類設備都可以看作這兩個結構的子類。
通過上面的分析,我們可以看出這三者之間的關係,並畫出下麵的結構框圖,sysfs中的上目錄結構就是根據kset之間的數據組織方式進行呈現的。





您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • $slice 如果希望數組的最大長度是固定的,那麼可以將 $slice 和 $push 組合在一起使用,就可以保證數組不會超出設定好的最大長度。$slice 的值必須是負整數。 假設$slice的值為10,如果$push 後的數組的元素個數小於10,那麼所有元素都會保留。反之,只有最後那10個元素會 ...
  • 本文是在Cat Qi的原貼的基礎之上,經本人逐題分別在MySql資料庫中實現的筆記,持續更新... 參考原貼:http://www.cnblogs.com/qixuejia/p/3637735.html 01 表結構 Student(Sno,Sname,Sage,Ssex) 學生表 Course(C ...
  • 本文是介紹MySQL資料庫InnoDB存儲引擎重做日誌漫游 00 – Undo LogUndo Log 是為了實現事務的原子性,在MySQL資料庫InnoDB存儲引擎中,還用Undo Log來實現多版本併發控制(簡稱:MVCC)。 - 事務的原子性(Atomicity) 事務中的所有操作,要麼全部完 ...
  • 1.環境準備 手動添加資料庫依賴: 在package.json的dependencies中新增, “mysql” : “latest”, 使用命令安裝mysql並添加依賴: 2.官方例子: 運行node ...
  • 一:在Dos里切換盤符 a:在電腦左下角右擊顯示圖片;(我用的是win10系統,其他系統類似) b:點擊運行,輸入cmd; c:點擊確定: d:輸入盤符:(如f:) 或F: 只寫字母,不寫分號是不行的!如下圖: cd f:(F:)是不行的如下圖: ...
  • 我在 "Linux字元設備驅動框架" 一文中已經簡單的介紹了字元設備驅動的基本的編程框架,這裡我們來探討一下Linux內核(以4.8.5內核為例)是怎麼管理字元設備的,即當我們獲得了設備號,分配了 cdev 結構,註冊了驅動的操作方法集,最後進行 cdev_add() 的時候,究竟是將哪些內容告訴了 ...
  • snull是《Linux Device Drivers》中的一個網路驅動的例子。這裡引用這個例子學習Linux網路驅動。 因為snull的源碼,網上已經更新到適合最新內核,而我自己用的還是2.6.22.6比較舊的內核。而網上好像找不到舊版的snull。因此結合《Linux Device Driver ...
  • <! 知識的力量是無限的(當然肯定還有更簡單的方法) !> 當我考慮將省市區三級聯動數據從mysql轉入mongodb時遇到了網上無直接插入mongodb的示例(基本均是mysql插入示例)。於是想到利用json文件直接導入mongodb會比較easy(SQLyog如何導出json?) 在SQLyo ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...