tft每日頭條

 > 科技

 > 全面注冊制總結

全面注冊制總結

科技 更新时间:2024-11-19 16:23:52

全面注冊制總結?内核中所有已分配的字符設備編号都記錄在一個名為 chrdevs 散列表裡該散列表中的每一個元素是一個 char_device_struct 結構,它的定義如下:,下面我們就來聊聊關于全面注冊制總結?接下來我們就一起去了解一下吧!

全面注冊制總結(淺淺講解字符設備編号的注冊分配)1

全面注冊制總結

内核中所有已分配的字符設備編号都記錄在一個名為 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() 主要執行以下步驟:

  1. 分配一個新的 char_device_struct 結構,并用 0 填充。

  2. 如果申請的設備編号範圍的主設備号為 0,那麼表示設備驅動程序請求動态分配一個主設備号。動态分配主設備号的原則是從散列表的最後一個桶向前尋找,那個桶是空的,主設備号就是相應散列桶的序 号。所以動态分配的主設備号總是小于 256,如果每個桶都有字符設備編号了,那動态分配就會失敗。

  3. 根據參數設置 char_device_struct 結構中的初始設備号,範圍大小及設備驅動名稱。

  4. 計算出主設備号所對應的散列桶,為新的 char_device_struct 結構尋找正确的位置。同時,如果設備編号範圍有重複的話,則出錯返回。

  5. 将新的 char_device_struct 結構插入散列表中,并返回 char_device_struct 結構的地址。

更多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每日頭條,我们将持续为您更新最新资讯!

查看全部

相关科技资讯推荐

热门科技资讯推荐

网友关注

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