linux设备模型bus,device,driver,(kobject、ktype、kset,bus_type、device、device_driver)
1.1Linux設備驅動模型簡介
1、什么是設備驅動模型
(1)類class。總線bus(負責將設備和驅動掛接起來)。設備devices、驅動driver(可以看到在驅動源碼中,不管是什么樣的驅動,都是以struct_xxx_driver來表示的)。Linux設備驅動中的四個框架、分別對應Linux驅動源代碼中的四個結構體。四個結構體分別描述Linux設備驅動中的類、總線、設備、驅動,這四個概念。對應的就是設備驅動模型這個概念了,四個模子。
(2)kobject和對象生命周期。kobject是Linux源代碼中的一個結構體,高度抽象的結構體,就是Linux內核中所有對象高度抽象出來的類,也就是Linux面向對象中,一個總的父類。
Linux中是如何管理對象的生命周期呢。就是利用了kobject總類中的機制,每個對象都有這種機制,因為kobject是linux中最高的父類,也就是基類,這種機制就會讓每個對象能夠具有自我管理生命周期的特性。 對象不用時,自己會將自己free掉,就跟調用了析構函數一樣。這就是Linux內核雖然是用C寫的,但是是面向對象的含義所在。
(3)sysfs,一種虛擬文件系統,作用是將應用層的空間和內核空間中的內容建立起了一個映射關系,就是內核中的一些結構體什么的信息值啊,在sysfs這個虛擬文件系統中以文件的形式展現出來,這樣應用層就可以跟內核進行互動。比如在sysfs中的控制led燈的文件中,我們echo一個值進行就可以讓led燈亮,這就是sysfs虛擬文件系統為我們提供的機制,讓內核和應用層建立起了映射關系。
(4)udev,也是為了實現內核空間和用戶空間(應用層)之間的通信,讓用戶空間可以及時的知道內核空間中發生的事情。比如某個驅動被加載了,或者被卸載了,在用戶空間都可以體現,像可以在用戶空間用lsmod查看。
什么是設備驅動模型呢,上面的四個東西()就是設備驅動模型。
1、2為什么需要設備驅動模型
1、早期內核(2.4版本之前)是沒有統一的設備驅動模型的,但照樣可以用。用法就是我們自己去insmod一個驅動,mknod一個設備文件、在驅動對象卸載時,rmmod去卸載,對應的的釋放內存的那種方法。
2、2.6版本及以后就引入了設備驅動模型了,我們就可以去class_create,自動創建一個設備驅動等。因為設備越來越多了,需要管理了,所以需要有一個好的體系。所以為了好管理,我們在去寫驅動的時候,就會去用設備驅動模型去寫驅動了,就是調用框架中提供的成員函數去創建驅動,同時每一個驅動因為都繼承自最終的父類kobject,所以都可以在自己驅動消亡的時候,自己知道去釋放內存。說白了設備驅動模型就是一種規則,用這種規則去寫驅動,可以很好的管理設備驅動,因為設備驅動太多了。
3、設備驅動模型統一實現和維護一些特性:如,電源管理、熱插拔、對象自我管理自己的生命周期(內存的釋放),用戶空間和內核空間之間的信息交互等。
1、3整個Linux驅動開發中的兩個點
1、驅動源碼本身的編寫、調試。重點在于對硬件要很了解,才能寫這個硬件的驅動。這樣的驅動一般都是廠家寫的。
2、驅動什么時候被安裝(當insmod時、這種用戶層去手動安裝的方式是非常老的了,當我們設備接入時驅動自動安裝,開機時自動加載,這個是新的方式,也就是利用設備驅動模型去寫的驅動。)、驅動中的函數什么時候被調用(在應用層怎么操作一下,就會對應調用驅動中的函數)。這部分和硬件無關,完全和設備驅動模型有關。設備驅動模型是Linux內核提供的,所以我們就是用設備驅動模型。
2、1、設備驅動模型的底層架構(三件套,三個結構體、kobject、kobj_type、kset)
1、kobject(總的結構體,最開始的,相當于父類,其他的結構體都是基于這個結構體構建起來的,所以可以把這個結構體看成一個基類,用C面向過程的角度來看,這個結構體是會被其他結構體包含的,是其他結構體的一個成員)
(1)kobject定義在include/linux/kobject.h中。
(2)是各種對象(總線、設備、驅動、類class等的對象)最基本的單元,提供一些公用型服務,如:對象引用計數、維護對象鏈表、對象上鎖、對用戶空間的表示。
(3)設備驅動模型中各種對象其內部都會包含一個kobject結構體,都繼承自這個父類,kobject就是基類。
(4)總線、class類、設備、驅動、這幾個抽象的類中都會繼承kobject,所以kobject有的屬性和行為,總線、class類、設備、驅動都會有。
(5)struct kobject {
const char*name;//來描述當前這個模型對象的名字
struct list_headentry;//用來將來維護對象鏈表,維護統一模型對象的鏈表,比如可以遍歷驅動的所有對象、設備的所有對象、總線的、class的。
struct kobject*parent;//上下層之間的掛接、不是平行的掛接,比如驅動的對象和總線的對象進行掛接,就需要這個成員變量來進行掛接。
struct kset*kset;//對象上鎖的功能。當用當前這個對象要進行敏感操作時,如果用總線的對象進行敏感操作,為了防止其他人用這個對象
//就需要對這個對象進行上鎖,就可以用這個成員變量來進行,防止其他人用這個總線的對象,當自己操作完事之后在
//進行解鎖。
struct kobj_type*ktype;//用來對對象在用戶空間的表示的,將來在sysfs中能看到設備的驅動等。
struct sysfs_dirent*sd;
struct krefkref;//用來對對象進行引用計數的,當前的對象被別人用了幾次,來統計該模型(總線、class、設備、驅動)的對象被他人用的次數。
//就是讓自己記住,同時被幾個人調用了。幫助維護對象的生命周期的,當當前這個對象的這個引用計數為0時,就表示沒有東西
//在用當前這個對象了,沒有用東西要靠當前這個對象來完成任務了,這時引用計數為0,就可以釋放這個對象的內存了。
unsigned int state_initialized:1;
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;
};
struct kref {//atomic_t是原子操作,就是操作是不可分割的。
atomic_t refcount;//refcount,用來對對象的引用計數的,用了幾個對象。
};
typedef struct {
int counter;
} atomic_t;
(6)kobject相當于面向對象中的總的基類。總線、設備、class、驅動這些抽象的結構體中都會有這個kobject結構體。
2、kobj_type
(1)很多書中簡稱ktype,每一個kobject都需要綁定一個ktype來提供相應的功能,kobject這個總的基類中有一個成員變量為ktype,這個成員變量將來來綁定一個struct kobj_type。
(2)綁定和包含的語義區別:包含指的是結構體肚子中有一個實實在在存在的成員,不需要我們來指定,內部本來就有了。綁定的意思是指,結構體內部中本來沒有這個東西,但是提供了一個指向這個東西的指針,將來我們在用的時候的時候,需要手動的去綁定這個東西,就是將一個東西的指針賦值給這個指針,這樣叫做綁定。
(3)struct kobj_type的定義,凡是手動綁定了這個kobj_type結構體的kobject,都會有kobj_type中的功能,其實就相當于為了kobject提供了成員函數,只不過
這些成員函數最開始并不是在kobject中存在的,這些成員函數是存在一個kobj_type結構體中的,當我們需要了某些行為的時候,比如需要kobj_type中這些成員
函數功能時,就會手動的去讓kobject中的struct kobj_type類型的指針變量去指向這個strct kobj_type有相應成員函數的對象。達到是的kobject中有了相應的
成員函數的目的。將來在設備驅動模型(總線、設備、class、驅動)繼承這個kobject時,也會有這些行為的存在。
struct kobj_type {//這個kobj_type的功能就是提供給我們在sysfs目錄下對文件的那些操作
void (*release)(struct kobject *kobj);//釋放,如果kobject手動綁定了這個kobj_type,在這個函數中會去檢查kobject中記錄引用計數的那個成員
//,如果發現引用計數不為0,說明還有人在用當前對象,那么就會將引用計數減1,如果進到這個release函數
//中,發現引用計數為0了,說明現在是最后一個用這個對象的人也進行調用這個release函數進行釋放對象了,
//則會將當前對象進行釋放,內存釋放,這也就是為什么這個函數的名字不用close,而用release的含義所在。
//close表示調用一次就會關閉所有的,但是release就沒有close語義上那么強硬了,并不是調用一次就關閉所有
//的,而實際上是要看當前對象被引用的次數有沒有變成0.
//看引用計數的方法很簡單,因為函數參數是struct kobject *,所以通過這個參數就可以找到 struct kref,從
//而可以知道當前被引用的次數
const struct sysfs_ops *sysfs_ops;//這個指針,指向的就是我們在sysfs目錄下所操作那個文件對應的執行函數
struct attribute **default_attrs;//這個attribute就是屬性,就是我們在sysfs目錄里面看到的那些文件名,每一個文件對應一個屬性,比如之前led驅動中的bright亮度這個屬性
const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
const void *(*namespace)(struct kobject *kobj);
};
@1://Kobj_type中的這個sysfs_ops結構體指針,這個結構體struct sysfs_ops的封裝是
struct sysfs_ops {
ssize_t(*show)(struct kobject *, struct attribute *,char *);//這個就是操作sysfs時的show方法,對應的就是我們去讀取這個文件,當我們cat這個文件的時候,對應的
//就是這個show方法
ssize_t(*store)(struct kobject *,struct attribute *,const char *, size_t);//這個就是操作sysfs的store方法,對應的就是我們去寫入這個文件,當我們echo向這個文件中
//寫入東西的時候,對應的就是這個store方法
};
@2:struct kobj_type的兩個關鍵點就是:
關鍵點1:sysfs_ops,提供該對象在sysfs中的操作方法(show和store,對應的就是我們cat和echo時內部所執行的函數)
關鍵點2:attribute,屬性,提供在sysfs中以文件形式存在的屬性(比如,led驅動中的bright亮度這個屬性文件,其次就是應用接口)
(4)kobject中的kset
struct kset {
struct list_head list;//包含了一個鏈表,用來將kset鏈接起來
spinlock_t list_lock;//包含了一個自旋鎖
struct kobject kobj;//表示kset里面可以內置一個kobject,就是kset中包含了一個kobject,包含就是里面有一個kobject。也就是說,kset里面包含了一個kobject,但是kobject
//里面有一個kset指針指向了kset(kobject里面綁定了一個kset)
const struct kset_uevent_ops *uevent_ops;
};
@1:kset的主要作用是做頂層kobject的容器類
@2:kset的主要目的是將各個kobject(代表著各個對象)組織出目錄層次架構
@3:可以認為kset就是為了sysfs中弄出目錄(kset直觀看就是個目錄,實現目錄間的上下層結構的),從而讓設備驅動模型中的多個對象能夠有層次有邏輯性的組織在一起
總結:總的來說,上面的kobject,kobj_type,kset,這些設備驅動模型的底層架構,就是為了實現sysfs虛擬文件系統中的那些玩意的,kobject是基礎,kobj_type就是sysfs中對應的文件,
和操作這些文件的基礎(attribute屬性,一個屬性就對應一個文件,sysfs_ops對應的就是對屬性文件的show方法和store方法,kset是構建sysfs虛擬文件系統中目錄層次架構的),kset容器。
3、總線式設備驅動的組織方式(這部分是在Kobject底層架構之上的一個層次,這部分的代碼也是寫內核的人寫的,但是我們寫驅動會用到這里的一部分代碼,越往上越靠近我們寫驅動,越往
下,越靠近寫內核的這些人)
(1)總線:物理上和CPU中都是用來連接東西的
(2)linux內核在設計的時候,采用的就是抽象的總線式設計的。
(3)驅動框架中的總線式設計
@1:比如CPU外面有50個設備,這50個設備就要有50個驅動進行對應,不然這些設備怎么工作呢,這種驅動和設備的對象程度要達到,一旦這個設備接入到了CPU中,這個驅動馬上就跟這個設備對
應上了,一旦這個設備不存在了,這個驅動馬上就會卸載掉。
@2:在Linux內核中,是以總線式的方法來管理設備和驅動之間的關系的。管理的方式:首先操作系統創建一些總線,比如USB總線、PCI總線等,總線由操作系統來管理,設備和驅動又由總線來
管理,比如USB總線又分為兩個分支,一個分支是設備的分支,一個分支是驅動的分支,把所有已經注冊了的USB類的設備放到USB總線下面的設備分支去管理,只要是USB的驅動,就把它放到
USB總線的驅動分支里面去管理,USB總線下面的那些很多個設備的聯系肯定是由一個鏈表將他們連接起來的,USB驅動那邊也有很多個驅動,也肯定是由一個鏈表連接起來的。這樣當我們有一個
USB設備直接插入到了系統中,系統檢測到了這個設備,就會將這個USB設備添加到USB總線下的USB設備分支下面的鏈表中去進行管理,這樣我們的系統中就注冊了一個新的USB設備了,那么這個
設備有沒有驅動呢,因此系統就會在USB總線下的USB驅動分支中的鏈表中去找有沒有這個USB設備對應的USB驅動,如果找到了,兩者配上對了,就可以直接使用了,如果在USB總線的USB驅動分支
中的鏈表里沒有找到這個USB設備的USB驅動,那么就會像上層進行匯報表明這個新插入的USB設備是沒有驅動的。這套找的和管理的方法,是由USB總線來負責的,并不是由Linux內核來負責的。
所以在Linux內核中,總線是用來管理設備和驅動的,查找設備對應的驅動,找的了進行配對,沒找到進行匯報,刪除驅動等,這些設備和驅動的管理都是有總線來管理的,比如上面說的USB總線
,而Linux內核卻不需要參與其中,因此Linux內核就像是總經理,他只需要管理總線就行了,而總線就像是部門主管,他需要管理他下面的兵,也就是設備和驅動,這種管理設備和驅動的方法。
就叫做總線式管理設備和驅動的方法。總線就像是中層管理人員,負責承上啟下,對上由Linux內核負責,對下管理設備和驅動。
(3)bus_type結構體(總線對應的結構體,就是一個總線模板,有I2C總線的bus_type,usb總線的bus_type),關鍵是match函數和uevent函數
struct bus_type {//這個就是總線的模子,類,是一個抽象,抽象出了總線所應該有的功能,將來所要管理的事
const char*name;//總線的名字,當這個總線注冊上的時候,在操作系統的sys目錄下的bus中是可以看到的,里面全是總線,比如在AC97這個總線中,里面就有兩個目錄,一個
//是設備device目錄,一個driver驅動目錄,可以看出來總線下面確實是有兩個分支的,一個設備的分支(所有注冊在AC97總線中的設備),一個驅動的分支(
//所有注冊在AC97這個總線下面的驅動)
struct bus_attribute*bus_attrs;//總線本身自己所擁有的屬性(在總線目錄下),就是在sys/bus/ac97這個總線目錄下看到的文件,屬性就是文件
struct device_attribute*dev_attrs;//就是進入到該名字的總線中的device目錄下時,device擁有的屬性(device目錄下的文件)
struct driver_attribute*drv_attrs;//就是進入到該名字的總線中的driver目錄下時,driver擁有的屬性(driver目錄下的文件)
int (*match)(struct device *dev, struct device_driver *drv); //總線模板中的這個函數,就是用來做該總線下的設備(device這個分支)和驅動(driver這個分支)之間的匹配的
//改總線下的每一個設備和驅動之間的關系就是由這個match方法來管理的
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct dev_pm_ops *pm;//這個dev_pm_ops里面全是函數指針,是一個函數指針集,是跟電源管理相關的,總線中的電源管理函數全在這里,比如讓這個總線上的所有設備全都休眠
//,就可以調用這個函數指針集中的相應的函數來讓這個總線上的所有設備全部進入到休眠狀態
struct bus_type_private *p;
};
4、設備(總線下管理的那些設備)
(1)struct device是硬件設備在內核驅動框架中的一種抽象,每用一個這個結構體類型產生一個變量(對象),就代表我們系統里面的一個硬件設備,這個結構體中的信息是所有的設備所共有
的一些信息。填充時就是填充的這些設備共有的信息。
(2)device_register用于向內核驅動框架中注冊一個設備,我們自己寫驅動的時候,就需要用這個device_register去注冊這個設備,這個函數是由內核驅動框架提供的,給驅動開發者使用的,
比如要寫一個U盤的驅動,就需要注冊一個U盤的設備,比如要寫一個聲卡的驅動,就需要注冊一個聲卡的設備,設備里面有很多的信息,如使用的中斷號等
(3)通常device不會單獨使用(是作為一個父類被繼承的),而是被包含在一個具體設備結構體中,如struct usb_device(這個結構體則是指某一個具體的硬件設備,所以里面要繼承自device
這個結構體,也就是要包含所有設備所共有的那部分信息)。struct device就是父類,usb_device就是子類,子類比父類更加具體。
5、驅動(總線下管理的那些驅動)
(1)struct device_driver是驅動程序在內核驅動框架中的抽象
(2)關鍵元素1:name,驅動程序的名字,很重要,經常被用來作為驅動和設備的匹配依據
(3)關鍵元素2:probe,驅動程序的探測函數(probe函數是用總線式驅動框架來設計驅動時才需要用到的,百分之80多的驅動是要用總線式的方式來完成的,因為將來設備多了便于管理)
,用來檢測一個設備是否可以被該驅動所管理。probe函數是總線式管理的設備和驅動中,非常重要的函數,是驅動的入口。地位相當于我們以前寫的驅動中的insmod時調用的函數
struct device_driver {
const char*name;//驅動的名字
struct bus_type*bus;
struct module*owner;
const char*mod_name;/* used for built-in modules */
bool suppress_bind_attrs;/* disables bind/unbind via sysfs */
#if defined(CONFIG_OF)
const struct of_device_id*of_match_table;
#endif
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
const struct attribute_group **groups;
const struct dev_pm_ops *pm;
struct driver_private *p;
};
總結:總線管理設備和驅動。我們經常寫驅動時,需要用總線式來進行設計,用總線模板填充一個我們的總線,用設備模板填充一個我們的設備,用驅動模板填充一個我們的驅動,用總線來管理。
bus_type總線模板,比如I2C總線會有I2C_bus_type表示是i2c中。struct device 掛在總線上的設備公用功能部分,struct usb_device,device前面加上設備的名字,此時這個結構中包含dev
ice公有的部分,同時包含usb設備自有的東西,該設備中有指向表示是哪個bus_type總線的指針,也有表示自己設備的名字,struct device_driver 設備的驅動,這個里面就包含了設備的驅動代碼部分,同時也有對應的名字,里面也有一個指向bus_type總線的指針,用來表示這個驅動是屬于哪個總線下的,包含了改驅動的哪些操作方法等,也有probe探測函數,設備和驅動所指向的同一個bus_type總線中有一個mach函數,用來將設備和驅動進行匹配在一起,匹配規則是用名字來匹配的。 總線、設備和驅動三者是一組的。
6、類
(1)相關結構體:struct class 和 struct class_device
(2)udev的使用離不開class,udev的功能,就是熱插拔的功能,就是在系統運行的時候,有一個設備突然插入到了這個系統中,想用udev就需要用到class,udev/mdev的熱插拔功能就是用這個class類來實現的
(3)class的真正意義在于作為同屬于一個class的多個設備的容器,也就是說,class是一種人造的概念,目的就是為了對各種設備進行分類管理。當然,class在分類的同時還對每個類貼上了一
些“標簽”,這也是設備驅動模型為我們寫驅動提供的基礎設施。一方面某個設備是從屬于某個總線的,另一方面某個設備也是叢屬于某個class的。比如插USB接口的可以是U盤,也可以是USB
攝像頭,他倆屬于同一個總線,但是不屬于同一個類,所以一個設備是接受雙重管理的。也就是說,一個設備是由某個總線管理的,同時這個設備又屬于某一個類,所以在總線目錄下找到了這個
設備,很有可能在class類中的不同類中也找到了這個設備。
(4)sys目錄下的device才是真正的設備,bus下面的device是說明這個device是由哪個總線管理的,最終進入到這個設備中都會符號鏈接跳轉到sys目錄下的device中
總結
以上是生活随笔為你收集整理的linux设备模型bus,device,driver,(kobject、ktype、kset,bus_type、device、device_driver)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我烤烧饼想暂停做粽子,端午节后重做烧饼,
- 下一篇: 晓芹海参怎么样 介绍晓芹海参的口感和营养