tft每日頭條

 > 圖文

 > linux内核的設備樹在哪裡修改

linux内核的設備樹在哪裡修改

圖文 更新时间:2025-01-27 00:35:59

linux内核的設備樹在哪裡修改?本文引用的内核代碼參考來自版本 linux-5.15.4 ,我來為大家科普一下關于linux内核的設備樹在哪裡修改?下面希望有你要的答案,我們一起來看看吧!

linux内核的設備樹在哪裡修改(Linux設備驅動-内核如何管理設備号)1

linux内核的設備樹在哪裡修改

開篇

本文引用的内核代碼參考來自版本 linux-5.15.4 。

在 Linux 系統中,每個注冊到系統的設備都有一個編号,這個編号便是 Linux 系統中的設備号。

設備号作為一種系統資源,需要加以管理。否則,如果設備号與驅動程序對應關系錯誤,就會引起混亂或引起潛在的問題。

通過查看 /proc/devices 文件可以得到系統中注冊的設備,第一列為主設備号,第二列為設備名稱

$ cat /proc/devices Character devices: 1 mem 4 /dev/vc/0 4 tty 4 ttyS 5 /dev/tty 5 /dev/console 5 /dev/ptmx 5 ttyprintk 6 lp 7 vcs 10 misc 13 input 21 sg ... Block devices: 7 loop 8 sd 9 md 11 sr 65 sd 66 sd ...

設備号的構成

一個設備号由主設備号和次設備号構成。

主設備号對應設備驅動程序,同一類設備一般使用相同的主設備号。

次設備号由驅動程序使用,驅動程序用來描述使用該驅動的設備的序号,序号一般從 0 開始。

Linux 設備号用 dev_t 類型的變量進行标識,這是一個 32位 無符号整數,内核源碼定義為:

/* <include/linux/types.h> */ typedef u32 __kernel_dev_t; typedef __kernel_dev_t dev_t;

主設備号用 dev_t 的高 12 位表示,次設備号用 dev_t 低 20 位表示。

内核提供了幾個宏定義,供驅動程序操作設備号時使用:

/* <include/linux/kdev_t.h> */ #define MINORBITS 20 #define MINORMASK ((1U << MINORBITS) - 1) #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))

宏 MAJOR 從設備号 dev 中提取主設備号。宏 MINOR 用來從設備号 dev 中提取次設備号。宏 MKDEV 用來将主設備号 ma 和 次設備号 mi 組合成 dev_t 類型的設備号。

另外,内核也提供了從設備文件 i-節點結構(inode 結構體)中獲取主次設備号的函數,如下:

/* <include/linux/fs.h> */ /* 獲取次設備号 */ static inline unsigned iminor(const struct inode *inode) { return MINOR(inode->i_rdev); } /* 獲取主設備号 */ static inline unsigned imajor(const struct inode *inode) { return MAJOR(inode->i_rdev); }

通過函數源碼可知,獲取主設備号和次設備号最終是通過宏定義完成的。

内核管理設備号

以字符設備為例,向内核中注冊設備号,内核是如何分配和管理設備号的呢?

在編寫字符設備驅動時,可以通過如下兩個系統調用向内核注冊設備号:

  • register_chrdev_region()

注冊一系列連續的字符設備号,主設備号需要函數調用者指定。此函數的原型為:

int register_chrdev_region(dev_t from, unsigned count, const char *name)

參數 from 為設備編号,包含主設備号和次設備号。參數 count 用于指定連續設備号的個數,即當前驅動程序所管理的同類設備的個數。參數 name 為設備或驅動的名字。

執行成功,返回 0。失敗,則返回一個負值的錯誤碼。

  • alloc_chrdev_region()

注冊一系列連續的字符設備号,主設備号是由内核動态分配得到的。此函數的原型為:

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

參數 dev 為函數的傳出參數,用于記錄動态分配的設備号,如果申請多個設備号,則此參數記錄這些連續設備号的起始值。

參數 baseminor 指定首個次設備号。參數 count 用于指定連續設備号的個數。參數 name 為設備或驅動的名字。

執行成功,返回 0。失敗,則返回一個負值的錯誤碼。

接下來,看看這兩個函數的内部實現流程。

register_chrdev_region()

該函數的内核源碼為,關鍵部分已加注釋:

/* <fs.char_dev.c> */ int register_chrdev_region(dev_t from, unsigned count, const char *name) { struct char_device_struct *cd; dev_t to = from count; dev_t n, next; /* 循環,注冊多個連續的設備号 */ for (n = from; n < to; n = next) { /* 計算得到下一個設備号 */ next = MKDEV(MAJOR(n) 1, 0); /* 判斷是否超限 */ if (next > to) next = to; /* 向内核注冊指定的設備号 */ cd = __register_chrdev_region(MAJOR(n), MINOR(n), next - n, name); if (IS_ERR(cd)) goto fail; } return 0; fail: /* 如果失敗,則釋放已申請的設備号資源 */ to = n; for (n = from; n < to; n = next) { next = MKDEV(MAJOR(n) 1, 0); kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n)); } return PTR_ERR(cd); }

由代碼内容可知,這個函數的核心處理流程是通過内部調用 __register_chrdev_region()實現的。

這個函數的主要功能是,将要使用的設備号注冊到内核的設備号管理體系中,避免多個驅動程序使用相同的設備号,而引起的混亂。

如果注冊設備号已經被使用,則會返回錯誤碼告知調用者,即調用失敗。如果成功,則函數返回 0。

struct char_device_struct

在調用過程中,會涉及到一個關鍵的數據結構 struct char_device_struct,其定義如下:

#define CHRDEV_MAJOR_HASH_SIZE 255 static struct char_device_struct { struct char_device_struct *next; /* 鍊表指針 */ unsigned int major; /* 主設備号 */ unsigned int baseminor; /* 次設備号 */ int minorct; /* 此設備号個數 */ char name[64]; /* 設備名稱 */ struct cdev *cdev; /* will die */ } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];

定義結構體的同時,還定義了一個全局性的指針數組 chrdevs,是内核用來分配和管理設備号的。數組中的每一個元素都是指向 struct char_device_struct 類型的指針。

函數 register_chrdev_region() 的主要功能是将驅動程序要使用的設備号記錄到 chrdevs 數組中。

__register_chrdev_region()

核心處理函數 __register_chrdev_region() 内部,首先會分配一個 struct char_device_struct 類型的指針 cd,然後對其進行初始化(已經去除無關代碼):

static struct char_device_struct * __register_chrdev_region(unsigned int major, unsigned int baseminor, int minorct, const char *name) { ... cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL); if (cd == NULL) return ERR_PTR(-ENOMEM); /* 根據主設備号計算索引,搜索 chrdevs 數組,判斷主設備号是否可用 */ i = major_to_index(major); for (curr = chrdevs[i]; curr; prev = curr, curr = curr->next) { if (curr->major < major) continue; if (curr->major > major) break; if (curr->baseminor curr->minorct <= baseminor) continue; if (curr->baseminor >= baseminor minorct) break; goto out; } /* 初始化信息 */ cd->major = major; cd->baseminor = baseminor; cd->minorct = minorct; strlcpy(cd->name, name, sizeof(cd->name)); /* 将分配的 cd 加入到 chrdevs[i] 中 */ if (!prev) { cd->next = curr; chrdevs[i] = cd; } else { cd->next = prev->next; prev->next = cd; } ... }

函數申請完内存資源後,開始掃描 chrdevs 數組,确保當前注冊的設備号可用。如果設備号占用,函數返回錯誤碼,即調用失敗。

如果設備号可用,則用設備号和名字信息初始化。初始化完成後,将 struct char_device_struct 加入到内核管理設備号的鍊表中。

alloc_chrdev_region()

此函數由内核動态分配設備号,該函數的内核源碼如下,關鍵部分已加注釋:

/* <fs.char_dev.c> */ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name) { struct char_device_struct *cd; /* 向内核注冊設備号 */ cd = __register_chrdev_region(0, baseminor, count, name); if (IS_ERR(cd)) return PTR_ERR(cd); /* 得到動态獲取的首個設備号 */ *dev = MKDEV(cd->major, cd->baseminor); return 0; }

這個函數的核心處理也是由函數 __register_chrdev_region() 實現的。

與 register_chrdev_region() 相比,alloc_chrdev_region() 在調用 __register_chrdev_region() 時,第一個參數為 0。此時 __register_chrdev_region() 處理流程代碼如下,

static struct char_device_struct * __register_chrdev_region(unsigned int major, unsigned int baseminor, int minorct, const char *name) { ... cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL); if (cd == NULL) return ERR_PTR(-ENOMEM); /* 查找可用的主設備号 */ f (major == 0) { ret = find_dynamic_major(); if (ret < 0) { pr_err("CHRDEV \"%s\" dynamic allocation region is full\n", name); goto out; } major = ret; } ... }

在分配完成 struct char_device_struct 内存資源之後,通過 find_dynamic_major() 查找可用的主設備号。後續處理與 register_chrdev_region() 函數調用處理相同。

設備号分配成功後,将 struct char_device_struct 類型指針返回給 alloc_chrdev_region() 函數。然後再通過如下代碼将新分派的設備号返回給 alloc_chrdev_region() 調用者:

*dev = MKDEV(cd->major, cd->baseminor);

小結

本文主要介紹了以下幾點内容:

  • 設備号是如何構成的,以及對其操作的宏定義。
  • register_chrdev_region() 和 alloc_chrdev_region() 實現細節。
  • 記錄設備号相關信息的關鍵數據結構 struct char_device_struct。
  • 内核通過 chrdevs 數組來跟蹤系統中設備号的使用情況。
,

更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

查看全部

相关圖文资讯推荐

热门圖文资讯推荐

网友关注

Copyright 2023-2025 - www.tftnews.com All Rights Reserved