1、設備模型
2.6內核增加了一個引人注目的新特性------統一設備模型
設備模型提供了一個獨立的機制專門來表示設備,并描述在系統中的拓撲結構
(1)代碼重復最小化
(2)可以列出系統中所有的設備,觀察到他們的狀態,并且查看他們連接的總線
(3) 可以將設備和其對應的驅動聯系起來
(4) 可以按照設備類型進行分類
2、kobject
設備模型的核心部分就是kobject
struct kobject {
const char *name; /*指向kobject的名字*/
struct list_head entry;
struct kobject *parent/*指向父kobject 實現層次結構*/
struct kset *kset;
struct kobj_type *ktype;
struct sysfs_dirent *sd;/*指向sysfs_dirent結構體 這個結構體就表示kobject對象的層次結構*/
struct kref kref;/*提供引用計數 其核心成員是一原子型變量,用來表示內核對象的引用計數 內核通過該成員追蹤內核對象生命周期*/
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;/*是否已經加入sysfs*/
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
};
3、ktype
kobject對象被關聯到一種特殊的類型 即ktype(kernel object type的縮寫) ktype由kobj_type結構體來表示,定義于頭文件
struct kobj_type {
void (*release)(struct kobject *kobj);
struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
};
1)ktype的存在是為了描述一族kobject所具有的普遍特性,實現同類的kobject都能共享相同的特性
2)release指針指向在kobject引用計數減至零時調用析構函數,該函數負責釋放所有kobject使用的內存和其他相關的清理工作
3)defaults_attrars 結構體數組定義了所有具有相同類型的kobject對象的屬性
4)sysfs文件系統根據對應的kobject屬性來創建文件
4、kset
1)kset是kobject對象的集合體,將相關的kobject集合到一起
2)具有相同ktype的kobject可以被分組到不同的kset
5、管理和操作kobject
kobject一般都被嵌入到設備結構體中
(1)kobject的初始化一個kobject對象
void kobject_init(struct kobject *kobj,struct kobj_type *ktype)
(2)定義并初始化
struct kobject *kobject_create(void)
6、引用計數
1)kobject通過引用計數控制對象的有效生命周期
(1)初始化后kobject的引用計數置為1
(2)當引用計數為0時,則表示設備已經卸載,不能在操作對應的設備
2)操作引用計數的接口
(1)增加一個引用計數
struct kobject *kobject_get(struct kobject *kobj)
(2)減少一個引用計數
void kobject_put(struct kobject *kobj)
3)描述引用計數的結構kref
kobject計數是通過kref實現
struct kref{
atomic_t refcount
}
(1)初始化
void kref_init(struct kref *kref)
(2)增加一個計數
void kref_get(struct kref *kref)
(3)減一個計數
void kref_put(struct kref *kref)
代碼:
(1)創建一個kobject對象并初始化
(2) 定制自己的kobject對象創建與釋放函數
7、sysfs
sysfs文件系統是一個處于內存中的虛擬文件系統,用文件系統的方式提供kobject對象層次結構的視圖
bus:提供一個系統總線視圖
dev:提供已經注冊設備節點的視圖
device:系統中設備結構體視圖
class:給用戶的視圖 通過對device實際設備目錄的符號鏈接
kernel:包含內核配置項和狀態信息
fs:已經注冊文件系統的視圖
class與devices 一個是高層概念 給用戶的視圖 一個底層物理設備 給內核的視圖
8、sysfs添加和刪除kobject
將kobject對象映射到sysfs文件系統中,產生對應的文件
int kobject_add(struct kobject *kobj, struct kobject *parent,
const char *fmt, ...)
從sysfs文件系統中刪除一個kobject對應文件目錄,需要使用函數kobject_decl()
void kobject_del(struct kobject *kobj)
int kobject_set_name(struct kobject *kobj, const char *fmt, ...)//設置kobject對象在sysfs中的名字
int sysfs_create_dir(struct kobject * kobj)//在sysfs文件系統中創建目錄
void sysfs_remove_dir(struct kobject * kobj)//在sysfs文件系統中刪除目錄
struct kobject *kobject_get(struct kobject *kobj)//可以用來獲取kobj-parent
代碼3:將對應的kobject映射到對應sysfs文件系統中,增加my_kobject_add()函數my_kobject_del()函數
代碼4:將當前的kobject對象增加parent節點 增加my_kobject_create_and_add()函數
9、向sysfs添加文件
1)默認的文件集合通過kobject和kset中的ktype字段提供
2)具有相同屬性的kobject導入到sysfs文件系統中的文件也具有相同的屬性
3)默認文件屬性由kobj_type結構來描述
struct kobj_type {
void (*release)(struct kobject *kobj);
struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
};
4)kobj_type結構中包含默認文件集合中所有文件的屬性,
default_attrs 默認文件屬性結構體數組
struct attribute {
const char *name; 屬性名稱,在sysfs文件系統中顯示的名字
struct module *owner;所屬模塊 如果存在
mode_t mode;權限
};
sysfs 文件系統根據默認文件屬性建立對應的文件
5)增加新的文件
(1)根據新文件屬性在sysfs文件系統創建文件
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);
}
6)對sysfs文件系統中的文件進行讀寫
struct sysfs_ops {
在讀sysfs文件時該方法會調用
ssize_t (*show)(struct kobject *, struct attribute *,char *);
在寫sysfs文件時該方法被調用
ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
};
struct kset *kset_create_and_add(const char *name,struct kset_uevent_ops *uevent_ops,struct kobject *parent_kobj)//創建一個kset集合
10、創建新屬性
sysfs_create_link():增加新的鏈接
sysfs_remove_file():刪除新的文件
sysfs_remove_link() 刪除鏈接文件
代碼5:在sysfs文件系統中增加文件
代碼6:在sysfs文件系統中增加多個文件,并設置新文件的屬性
代碼7: 在sysfs文件系統中針對每個文件設置相應的的操作方法show()與store()
代碼8:底層實現led驅動并關聯到kobject對象中
11、uevent
1)kset是屬于一組kobject的集合
struct kset { struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
const struct kset_uevent_ops *uevent_ops;
};
kset_uevent_ops : 當kset中的kobject對象發生狀態變化需要通知用戶空間
struct kset_uevent_ops {
int (* const filter)(struct kset *kset, struct kobject *kobj);
const char *(* const name)(struct kset *kset, struct kobject *kobj);
int (* const uevent)(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env);
};
2)kset相關的API
void kset_init(struct kset *k)
初始化kset對象
int kset_register(struct kset *k)
初始化并向系統注冊一個kset對象
static void kobject_init_internal(struct kobject *kobj)
注冊kset對象
notes:
.kset對象本身也是一個kobject內核對象,在sysfs文件系統中生成一個新的目錄
.注冊kset對象,內核編譯啟用了CONFIG_HOTPLUG,則需要就這一事件通知用戶空間(內核配置文件中可以查詢)
.事件通知由kobject_uevent完成
.不屬于kset的kobject對象不能完成事件通知
.kobject之間通過parent成員實現層次關系,當kobject的parent為NULL時,就會把kobj->kset->kobj作為kobj的parent
struct kset *kset_create_and_add(const char *name, const struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
創建一個kset對象并添加到sysfs文件系統中
3)hotplug(熱插拔)
當一個設備動態加入系統時,設備驅動程序可以檢查到設備,并通過通知的方式告知用戶空間
通知用戶空間的方式一般有兩種:udev 與 /sbin/hotplug,現在使用更多的是udev
udev 的實現基于內核中的網絡機制,通過標準的socket接口來監聽來自內核的網絡廣播包,并對接收的包進行分析處理
4)hotplug相關的API
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
發送一個 event給用戶空間,以網絡數據包的形式發送給用戶空間應用程序
@action : 發送event類型
enum kobject_action {
KOBJ_ADD,
KOBJ_REMOVE,
KOBJ_CHANGE,
KOBJ_MOVE,
KOBJ_ONLINE,
KOBJ_OFFLINE,
KOBJ_MAX
};
kobject_uevent_env()
sprintf(scratch, "%s@%s", action_string, devpath); @273L
傳遞給用戶空間event數據的內容
"事件類型@設備路徑"
example:
"add@/kset-test/kobject-test/"
uevent一般與應用程序結合起來使用,一般可以接收并處理uevent的應用程序有udev or mdev
udev工具通過netlink獲取內核發出的uevent消息,并且處理,加載相應的驅動或者在/dev/目錄下生成對應的設備結點
udev服務啟動后,會掃描/sys目錄下所有具有uevent屬性文件,在進行相應的處理
uevent的觸發被封裝到設備模型的操作中,當添加設備的時則會發送一個uevent(user event)事件,
udev 工具會通過netlink獲取uevent消息 ,然后進行加載驅動或者在/dev/目錄下生成對應的設備結點
int device_add()
{
......
kobject_uevent(&dev->kobj, KOBJ_ADD);
......
}
5)netlink機制
(1)netlink機制的特點
.netlink是一種特殊的socket
.netlink可以實現內核與應用程序進行雙向傳輸通訊,并且使用socket()API進行交互
.netlink是一種異步通訊方式,在內核與用戶態進行傳遞的消息保存在socket緩沖區數據隊列中,發送消息只是把消息保存在接收者的socket的接收隊列中,而不需要等待接收者收到消息
(2)使用socket()創建netlink套接字
int socket(int domain, int type, int protocol);
@domain:協議族 AF_NETLINK or PF_NETLINK
@type :socket類型 SOCK_DGRAM or SOCK_RAW
@protocol : 協議類型 NETLINK_KOBJECT_UEVENT
(3)netlink地址結構
struct sockaddr_nl {
__kernel_sa_family_t nl_family; /* AF_NETLINK */
unsigned short nl_pad; /* zero */
__u32 nl_pid; /* port ID */
__u32 nl_groups; /* multicast groups mask */
};
@nl_family : AF_NETLINK 協議族
@nl_pad : 填0
@nl_pid : 進程pid ,可以通過getpid()獲取
(3)使用setsockopt()設置套接字選項
setsockopt(sd, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize));
@SO_RCVBUFFORCE:設置或者獲取緩沖區的大小
(4)使用bind()綁定地址結構
retval= bind(sd, (void*)&snl, sizeof(struct sockaddr_nl))
(5)讀取netlink接收緩沖區的內容
read(sd, buf, sizeof(buf));