全面注冊制總結?内核中所有已分配的字符設備編号都記錄在一個名為 chrdevs 散列表裡該散列表中的每一個元素是一個 char_device_struct 結構,它的定義如下:,下面我們就來聊聊關于全面注冊制總結?接下來我們就一起去了解一下吧!
内核中所有已分配的字符設備編号都記錄在一個名為 chrdevs 散列表裡。該散列表中的每一個元素是一個 char_device_struct 結構,它的定義如下:
static struct char_device_struct {
struct char_device_struct *next; // 指向散列沖突鍊表中的下一個元素的指針
unsigned int major; // 主設備号
unsigned int baseminor; // 起始次設備号
int minorct; // 設備編号的範圍大小
char name[64]; // 處理該設備編号範圍内的設備驅動的名稱
struct file_operations *fops; // 沒有使用
struct cdev *cdev; // 指向字符設備驅動程序描述符的指針
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
注 意,内核并不是為每一個字符設備編号定義一個 char_device_struct 結構,而是為一組對應同一個字符設備驅動的設備編号範圍定義一個 char_device_struct 結構。chrdevs 散列表的大小是 255,散列算法是把每組字符設備編号範圍的主設備号以 255 取模插入相應的散列桶中。同一個散列桶中的字符設備編号範圍是按起始次設備号遞增排序的。
注冊内核提供了三個函數 來注冊一組字符設備編号,這三個函數分别是 register_chrdev_region()、alloc_chrdev_region() 和 register_chrdev()。這三個函數都會調用一個共用的 __register_chrdev_region() 函數來注冊一組設備編号範圍(即一個 char_device_struct 結構)。所以下面先來看一下 __register_chrdev_region() 函數的實現代碼。
static struct char_device_struct * __register_chrdev_region(unsigned int major, unsigned int baseminor, int minorct, const char *name)
{
struct char_device_struct *cd, **cp;
int ret = 0;
int i;
cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
if (cd == NULL)
return ERR_PTR(-ENOMEM);
mutex_lock(&chrdevs_lock);
if (major == 0) {
for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--)
if (chrdevs[i] == NULL)
break;
if (i == 0) {
ret = -EBUSY;
goto out;
}
major = i;
ret = major;
}
cd->major = major;
cd->baseminor = baseminor;
cd->minorct = minorct;
strncpy(cd->name,name, 64);
i = major_to_index(major);
for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
if ((*cp)->major > major ||
((*cp)->major == major && ( ((*cp)->baseminor >= baseminor) || ((*cp)->baseminor (*cp)->minorct > baseminor)) ))
break;
/* Check for overlapping minor ranges. */
if (*cp && (*cp)->major == major) {
int old_min = (*cp)->baseminor;
int old_max = (*cp)->baseminor (*cp)->minorct - 1;
int new_min = baseminor;
int new_max = baseminor minorct - 1;
/* New driver overlaps from the left. */
if (new_max >= old_min && new_max <= old_max) {
ret = -EBUSY;
goto out;
}
/* New driver overlaps from the right. */
if (new_min <= old_max && new_min >= old_min) {
ret = -EBUSY;
goto out;
}
}
cd->next = *cp;
*cp = cd;
mutex_unlock(&chrdevs_lock);
return cd;
out:
mutex_unlock(&chrdevs_lock);
kfree(cd);
return ERR_PTR(ret);
}
函數 __register_chrdev_region() 主要執行以下步驟:
更多linux内核視頻教程文檔資料免費領取後台私信【内核】自行獲取.
分析完 __register_chrdev_region() 後,我們來一個個看那三個注冊函數。首先是 register_chrdev_region()。
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() 函數用于分配指定的設備編号範圍。如果申請的設備編号範圍跨越了主設備号,它會把分配範圍内的編号按主設備号分割成較小的子範圍,并在每個子範圍上調用 __register_chrdev_region() 。如果其中有一次分配失敗的話,那會把之前成功分配的都全部退回。
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;
}
alloc_chrdev_region() 函數用于動态申請設備編号範圍,這個函數好像并沒有檢查範圍過大的情況,不過動态分配總是找個空的散列桶,所以問題也不大。通過指針參數返回實際獲得的起始設備編号。
int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
{
struct char_device_struct *cd;
struct cdev *cdev;
char *s;
int err = -ENOMEM;
cd = __register_chrdev_region(major, 0, 256, name);
if (IS_ERR(cd))
return PTR_ERR(cd);
cdev = cdev_alloc();
if (!cdev)
goto out2;
cdev->owner = fops->owner;
cdev->ops = fops;
kobject_set_name(&cdev->kobj, "%s", name);
for (s = strchr(kobject_name(&cdev->kobj),'/'); s; s = strchr(s, '/'))
*s = '!';
err = cdev_add(cdev, MKDEV(cd->major, 0), 256);
if (err)
goto out;
cd->cdev = cdev;
return major ? 0 : cd->major;
out:
kobject_put(&cdev->kobj);
out2:
kfree(__unregister_chrdev_region(cd->major, 0, 256));
return err;
}
最 後一個 register_chrdev() 是一個老式分配設備編号範圍的函數。它分配一個單獨主設備号和 0 ~ 255 的次設備号範圍。如果申請的主設備号為 0 則動态分配一個。該函數還需傳入一個 file_operations 結構的指針,函數内部自動分配了一個新的 cdev 結構。關于這些,在後續講字符設備驅動的注冊時會說明。
注銷和 注冊分配字符設備編号範圍類似,内核提供了兩個注銷字符設備編号範圍的函數,分别是 unregister_chrdev_region() 和 unregister_chrdev() 。它們都調用了 __unregister_chrdev_region() 函數。由于比較簡單,就不加說明了,隻把代碼貼出來。
static struct char_device_struct * __unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct)
{
struct char_device_struct *cd = NULL, **cp;
int i = major_to_index(major);
mutex_lock(&chrdevs_lock);
for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
if ((*cp)->major == major &&
(*cp)->baseminor == baseminor &&
(*cp)->minorct == minorct)
break;
if (*cp) {
cd = *cp;
*cp = cd->next;
}
mutex_unlock(&chrdevs_lock);
return cd;
}
void unregister_chrdev_region(dev_t from, unsigned count)
{
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;
kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
}
}
void unregister_chrdev(unsigned int major, const char *name)
{
struct char_device_struct *cd;
cd = __unregister_chrdev_region(major, 0, 256);
if (cd && cd->cdev)
cdev_del(cd->cdev);
kfree(cd);
}
注意, unregister_chrdev() 會把 register_chrdev() 中自動分配的 cdev 給注銷掉。
導讀-最新發表 - 内核技術中文網 - 構建全國最權威的内核技術交流分享論壇
轉載地址:淺淺講解字符設備編号的注冊分配! - 圈點 - 内核技術中文網 - 構建全國最權威的内核技術交流分享論壇
更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!