linux設備驅動模型:kobject、kobj_type

来源:https://www.cnblogs.com/couvrir/archive/2023/08/16/17635705.html
-Advertisement-
Play Games

為什麼需要設備驅動模型 內核版本發展 2.4版本之前內核沒有統一的設備驅動模型,但是可以用(例如先前的led字元設備驅動實驗,使用前需要手動調用mknod命令創建設備文件,從而進一步控制硬體)。 2.4~2.6版本內核使用devfs,掛載在/dev目錄。需要在內核驅動中創建設備文件(調用devfs_ ...


為什麼需要設備驅動模型

內核版本發展

2.4版本之前內核沒有統一的設備驅動模型,但是可以用(例如先前的led字元設備驅動實驗,使用前需要手動調用mknod命令創建設備文件,從而進一步控制硬體)。

2.4~2.6版本內核使用devfs,掛載在/dev目錄。需要在內核驅動中創建設備文件(調用devfs_register創建設備文件,無需手動mknod命令,需傳入設備文件名),命名過於死板(編譯後驅動對應的設備文件名固定,無法動態修改)。

2.6版本之後內核統一使用sysfs,掛載在/sys目錄。將設備分類、分層次統一進行管理,配合udev/mdev守護進程(開啟自啟,後臺運行,一直監聽內核驅動發出的消息)動態創建設備文件,命令規則自由制定。

sysfs虛擬文件系統在linux系統中體現出設備驅動模型,類似於proc文件系統,總是被掛載在/sys/掛載點上。目錄對應的inode節點會記錄基本驅動對象(kobject),從而將系統中的設備組成層次結構。用戶可以讀寫目錄下的不同文件來配置基本驅動對象(kobject)的不同屬性。

 

 

設備驅動模型基本元素

kobject:sysfs的一個目錄,常用來表示基本驅動對象,不允許發送消息到用戶空間。

kset:sysfs的一個目錄,常用來管理kobject,允許發送消息到用戶空間。

kobj_type:目錄下屬性文件的操作介面。

kobject既可以通過parent指針找到上層kobject,也可以通過kset指針找到上層kobject。但上層kobject對象無法遍歷到下層,所以較少使用。

 

kobject結構體

sysfs中每一個目錄都對應一個kobject,kobject結構體存放在內核/include/linux/kobject.h。

struct kobject {
        const char              *name;        //kobject的名稱,同時也是sysfs下的目錄名字
        struct list_head        entry;        //鏈表節點,用於將kobject加入到kset的list_head
        struct kobject          *parent;    //該kobject的上層節點,構建kobject間的層次關係(在sysfs體現為目錄結構)
        struct kset             *kset;        //該kobject所屬的kset對象(可以為NULL),用於批量管理kobject對象
        struct kobj_type        *ktype;        //該kobject的sysfs文件系統相關的操作和屬性
        struct kernfs_node      *sd;         //該kobject在sysfs文件系統中對應目錄項
        struct kref             kref;        //該kobject的引用次數
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
        struct delayed_work     release;
#endif
        unsigned int state_initialized:1;            //記錄內核對象的初始化狀態
        unsigned int state_in_sysfs:1;                //表示該kobject所代表的內核對象是否在sysfs建立目錄
        unsigned int state_add_uevent_sent:1;        //記錄是否已經向用戶空間發送ADD uevent事件
        unsigned int state_remove_uevent_sent:1;    //記錄是否已經向用戶空間發送REMOVE uevent事件
        unsigned int uevent_suppress:1;                //如果為1,則忽略所有上報的uevent事件
};

由於kobject添加到內核時,需要根據名字註冊到sysfs虛擬文件系統中,之後就不能再直接修改該名字。如果想改,需要調用kobject_rename介面,該介面會主動處理sysfs的相關事宜。
kset如果沒有指定的parent,則會把kset作為parent(kset是一個特殊的kobject)。

uevent提供了“用空空間通知”的功能實現,當內核中有kobject的增刪改等操作時,會通知用戶空間。

 

kset結構體

kset結構體存放在內核/include/linux/kobject.h。

struct kset {
        struct list_head list;                        //指向該kset下所有的kobject組成的鏈表
        spinlock_t list_lock;                        //避免操作鏈表時產生競態的自旋鎖
        struct kobject kobj;                        //該kset自己的kobject(kset是一個特殊的kobject,也會在sysfs中以目錄的形式體現)
        const struct kset_uevent_ops *uevent_ops;    
} __randomize_layout;

uevent_ops為該kset的uevent操作函數集(函數指針)。當kset的某些kobject對象發生狀態變化需要通知用戶空間時,調用其中對應的函數來完成。
當任一kobject需要上報uevent時,都要調用它所屬的kset的uevent_ops,添加環境變數,或者過濾uevent(kset可以決定哪些uevent可以上報)。
因此一個kobject不屬於任一kset時,是不允許發生uevent的。

 

kobj_type結構體

kobj_type結構體存放在內核/include/linux/kobject.h。

struct kobj_type {
        /* 銷毀kobject對象時調用 */
        void (*release)(struct kobject *kobj);
        
        /* 該類型的kobject的sysfs虛擬文件系統操作介面(讀屬性介面show和寫屬性介面store) */
        const struct sysfs_ops *sysfs_ops;
        
        /* 該類型的kobject的attribute表(sysfs的一個文件)。將會在kobject添加到內核時,一併註冊到sysfs中 */
        struct attribute **default_attrs;
        
        const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
        const void *(*namespace)(struct kobject *kobj);
        void (*get_ownership)(struct kobject *kobj, kuid_t *uid, kgid_t *gid);
};

 

 

kobject:驅動的基石

kobject主要功能

  • 通過parent指針,可以將所有kobject以層次結構的形式組合起來。
  • 使用一個引用計數,來記錄kobject被引用的次數,併在引用計數為0時釋放kobject對象(這是kobject誕生時的唯一功能)。
  • 和sysfs虛擬文件系統配合,將每一個kobject及其特性以文件形式顯示到用戶空間。
  • 在Linux中,kobject幾乎不會單獨存在。它的主要功能就是內嵌在一個大型的數據結構中,為這個數據結構提供一些底層的功能實現。
  • Linux驅動開發者很少會直接使用kobject以及它提供的介面,而是使用構建在kobject之上的設備模型介面。

 

整個kobject機制的理解

kobject的核心功能是:保持一個引用計數,當該引用計數減為0時,自動釋放kobject所占的記憶體空間(這決定了kobject必須是動態分配)。

kobject的常見使用場景:內嵌在大型的數據結構中(如kset、device_driver等),因此這些大型的數據結構也必須是動態分配、動態釋放。ktype的release回調函數負責釋放kobject(甚至是包含kobject的數據結構)的記憶體空間。

 

kobject使用流程

kobject大多數情況下(有一例外)會嵌在其它數據結構中使用,使用流程如下:

  1. 定義一個struct kset類型的指針,併在初始化時為它分配空間,添加到內核中。
  2. 根據實際情況,定義內嵌有kobject的自己所需的數據結構原型。
  3. 定義一個適合自己的ktype,並實現其中回調函數release。
  4. 在需要使用到包含kobject的數據結構時,動態分配該數據結構,並分配kobject空間,添加到內核中。
  5. 每一次引用數據結構時,調用kobject_get介面增加引用計數;引用結束時,調用kobject_put介面,減少引用次數。
  6. 當引用計數為0時,kobject模塊會自動調用ktype所提供的release介面,釋放上層數據結構以及kobject的記憶體空間。

例外:

  開發者只需在sysfs中創建一個目錄,而不需要其它的kset、ktype的操作。這是可以直接調用kobject_create_and_add介面,分配一個kobject結構並把它添加到內核中。

 

kobject_create_and_add()函數

存放在內核/lib/kobject.c 

/**
 * kobject_create_and_add - 動態創建一個struct kobject並將其註冊到sysfs
 *
 * @name: 對象的名稱
 * @parent: 這個kobject的父kobject(如果有的話)。
 *
 * 這個函數動態地創建一個kobject結構並將其註冊到sysfs。當您完成此結構時,調用kobject_put(),當不再使用該結構時,該結構將被動態釋放。
 *
 * 如果無法創建kobject,則返回NULL。
 */
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
{
        struct kobject *kobj;
        int retval;

        kobj = kobject_create();    //創建並初始化一個kobject對象        
        if (!kobj)
                return NULL;

        retval = kobject_add(kobj, parent, "%s", name);    //sysfs創建一個目錄項並與kobject對象關聯
        if (retval) {
                pr_warn("%s: kobject_add error: %d\n", __func__, retval);
                kobject_put(kobj);
                kobj = NULL;
        }
        return kobj;
}

 kobject_create_and_add()函數是kobject_create函數和kobject_add函數的組合。整體功能是創建一個名字為“name”的kobject對象,並將其添加到指定的父kobject對象下。

...

 

 

kobj_type:用戶空間的法寶

sysfs_create_group()函數

存放在內核/fs/sysfs/group.c文件中。

在kobject中,分析到 kobject_create()kobject_init(kobj, &dymic_kobj_ktype)dymic_kobj_ktype.sysfs_ops = &kobj_sysfs_ops

kobj_sysfs_ops中存放著統一的操作介面show和store。調用統一的操作介面時,會在內部進一步調用具體的操作介面。

 

kernfs_init_inode()函數

存放在內核/fs/kernfs/inode.c文件中。

 

設備驅動模型實驗1-kobject點燈

實驗思路:內核模塊+LED驅動+kobject+kobj_attribute

內核模塊:動態載入

LED驅動:控制硬體LED

kobject:在/sys創建目錄項

kobj_attribute:為kobject對象的屬性文件提供獨有的讀寫介面

kobject_led.c文件 

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <asm/io.h>

/* GPIO虛擬地址映射 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO04;
static void __iomem *SW_PAD_GPIO1_IO04;
static void __iomem *GPIO1_GDIR;
static void __iomem *GPIO1_DR;

static int foo;
static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
        /* buf 將會被自動拷貝到用戶空間 */
        return sprintf(buf, "%d\n", foo);
}

static ssize_t foo_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
        /* buf內容來自用戶空間,由內核自動完成了。kstrtoint 是將子串buf以十進位的格式輸出到foo */
        int ret = kstrtoint(buf, 10, &foo);
        if(ret < 0)     return ret;
        return count;
}

static ssize_t led_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
        int var;

        if(strcmp(attr->attr.name, "led") == 0)
                var = 123;

        return sprintf(buf, "%d\n", var);
}

static ssize_t led_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
        if(strcmp(attr->attr.name, "led") == 0){
                if(!memcmp(buf, "on", 2)){
                        iowrite32(0<<4, GPIO1_DR);
                }else if(!memcmp(buf, "off", 3)){
                        iowrite32(1<<4, GPIO1_DR);
                }
        }

        return count;
}

/* __ATTR 定義在 include/linux/sysfs.h。foo 對應屬性文件名。
 * show成員 和 store成員 最終分別會被 kobject->ktype 下的 kobj_sys_ops 下的 kobj_attr_show 和 kobj_attr_store 調用。
 */
static struct kobj_attribute foo_attribute = __ATTR(foo, 0664, foo_show, foo_store);
static struct kobj_attribute led_attribute = __ATTR(led, 0664, led_show, led_store);

static struct attribute *attrs[] = {
        &foo_attribute.attr,
        &led_attribute.attr,
        NULL, /* need to NULL terminate the list of attributes */
};

static struct attribute_group attr_group = {
        .attrs = attrs,
};

static struct kobject *led_kobj;
static int __init led_init(void){
        int retval;

        /* GPIO相關寄存器操作 */
        IMX6U_CCM_CCGR1 = ioremap(0x20c406c, 4);
        SW_MUX_GPIO1_IO04 = ioremap(0x20e006c, 4);
        SW_PAD_GPIO1_IO04 = ioremap(0x20e02f8, 4);
        GPIO1_GDIR = ioremap(0x0209c004, 4);
        GPIO1_DR = ioremap(0x0209c000, 4);

        /* 使能GPIO1時鐘 */
        iowrite32(0xffffffff, IMX6U_CCM_CCGR1);

        /* 設置GPIO1_IO04復用為普通GPIO */
        iowrite32(5, SW_MUX_GPIO1_IO04);

        /* 設置GPIO屬性 */
        iowrite32(0x10b0, SW_PAD_GPIO1_IO04);

        /* 設置GPIO1_IO04為輸出功能 */
        iowrite32(1<<4, GPIO1_GDIR);

        /* LED輸出高電平 */
        iowrite32(1<<4, GPIO1_DR);

        /* 創建一個kobject對象,上一層節點設置為 NULL,此kobject對象在 sysfs 下的根目錄。
         * 此函數執行完會在 /sys 目錄下生成一個名為"led_kobject"的目錄。
         */
        led_kobj = kobject_create_and_add("led_kobject", NULL);
        if(!led_kobj)   return -ENOMEM;

        /* 為kobject設置屬性文件,並且將屬性文件和操作介面綁定起來 */
        retval = sysfs_create_group(led_kobj, &attr_group);
        if(retval)
                kobject_put(led_kobj);

        return 0;
}

static void __exit led_exit(void){
        /* 取消映射 */
        iounmap(IMX6U_CCM_CCGR1);
        iounmap(SW_MUX_GPIO1_IO04);
        iounmap(SW_PAD_GPIO1_IO04);
        iounmap(GPIO1_GDIR);
        iounmap(GPIO1_DR);

        /* 註銷字元設備驅動 */
        kobject_put(led_kobj);
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("couvrir");
MODULE_DESCRIPTION("led module");
MODULE_ALIAS("led module");      

Makefile文件

照舊。

執行過程

虛擬機:執行make和make copy。生成.ko文件。

開發板(在掛載目錄下執行): 

sudo insmod kobject_led.ko

查看/sys/文件夾,存在led_kobject的目錄項。 

查看/sys/led_kobject的屬性文件。

然後就是echo和cat命令的使用。

sudo rmmod kobject_led

 


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

-Advertisement-
Play Games
更多相關文章
  • ## 中台框架後端項目 Admin.Core 的介紹與配置說明 > 中台admin是前後端分離許可權管理系統,Admin.Core為後端項目,基於.NET 7.0開發。 > 支持多租戶、數據許可權、動態 Api、任務調度、OSS 文件上傳、滑塊拼圖驗證、多資料庫,分散式緩存、分散式事務等 - 項目地址 ...
  • ## 背景 ​ 我們寫博客難免需要大量圖片,如果把圖片一張張上傳到博客相冊再引用圖片鏈接,就很麻煩。所以需要一種手段,讓我們可以在本地寫完博客內容後,自動把博客引用的圖片傳到網上,併在網上發佈。 ​ 這時候就需要***dotnet-cnblog*** :博客園文章快捷發佈工具 ## 環境準備 ### ...
  • 整體統一前端框架,使用VUE重新寫,支持Windows模式和傳統菜單導航模式切換。整體框架支持租戶,全面支持雲端部署與應用。全新界面設計,提升用戶的易用性。 ...
  • Woa(Wechat Official Account)是一個基於.net 7開發的微信公眾平臺介面項目,利用Supabase作為數據存儲和消息通信服務,同時提供了ChatGPT和Claude2等目前熱門的生成式AI會話功能接入。 ...
  • # ansible、Ad-Hoc、YAML劇本 ## 1.簡介 ansible是新出現的自動化運維工具,基於Python開發,集合了眾多運維工具(puppet、cfengine、chef、func、fabric)的優點,實現了批量系統配置、批量程式部署、批量運行命令等功能。 ## 2.部署 1.dn ...
  • 哈嘍大家好,我是鹹魚 文章《[SELinux 導致 Keepalived 檢測腳本無法執行](https://mp.weixin.qq.com/s?__biz=MzkzNzI1MzE2Mw==&mid=2247486319&idx=1&sn=4932d10d7ad39d02d2536be1a70b1 ...
  • [toc] # 最小化安裝Linux系統初始化腳本 **註:此腳本適用於centos 7/8、Ubuntu1804,具體需要根據實際情況進行測試調整。** 此腳本包含的功能: 1. 允許 root 用戶使用 ssh 登錄 2. 關閉 selinux 3. 關閉防火牆 4. 設置 ps1 5. 設置默 ...
  • 多摩川協議理解-(1) 多摩川編碼器有絕對值的和增量的兩種,這邊主要是我對多摩川絕對值的理解。 前文, 多摩川編碼器其實是日本有一家公司叫多摩川的公司做出來的,其實他們家有很多種編碼器,但是這裡我只對他的們出的協議作出理解。 硬體介面方面,因為有單圈和多圈只分,多圈一般有電池,所以會多一個帶電池的。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...